mirror of
https://github.com/openvk/openvk
synced 2024-11-15 03:31:18 +03:00
Гео-метки
This commit is contained in:
parent
a2384cc231
commit
da863c25f3
14 changed files with 297 additions and 4 deletions
|
@ -245,6 +245,23 @@ class Post extends Postable
|
||||||
$this->unwire();
|
$this->unwire();
|
||||||
$this->save();
|
$this->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGeo(): ?object
|
||||||
|
{
|
||||||
|
if (!$this->getRecord()->geo) return NULL;
|
||||||
|
|
||||||
|
return (object) json_decode($this->getRecord()->geo, true, JSON_UNESCAPED_UNICODE);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLat(): ?float
|
||||||
|
{
|
||||||
|
return (float) $this->getRecord()->geo_lat ?? NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLon(): ?float
|
||||||
|
{
|
||||||
|
return (float) $this->getRecord()->geo_lon ?? NULL;
|
||||||
|
}
|
||||||
|
|
||||||
use Traits\TRichText;
|
use Traits\TRichText;
|
||||||
}
|
}
|
||||||
|
|
10
Web/Models/sql/get-nearest-posts.tsql
Normal file
10
Web/Models/sql/get-nearest-posts.tsql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
SELECT *,
|
||||||
|
SQRT(
|
||||||
|
POW(69.1 * (? - geo_lat), 2) +
|
||||||
|
POW(69.1 * (? - geo_lon) * COS(RADIANS(geo_lat)), 2)
|
||||||
|
) AS distance
|
||||||
|
FROM Posts
|
||||||
|
WHERE id <> ?
|
||||||
|
HAVING distance < 1 AND distance IS NOT NULL
|
||||||
|
ORDER BY distance
|
||||||
|
LIMIT 25;
|
|
@ -292,8 +292,17 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$this->flashFail("err", " ");
|
$this->flashFail("err", " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$geo = NULL;
|
||||||
|
|
||||||
|
if (!is_null($this->postParam("geo")) && $this->postParam("geo") != "none") {
|
||||||
|
$geo = json_decode($this->postParam("geo"), true, JSON_UNESCAPED_UNICODE);
|
||||||
|
if (!$geo["lat"] || !$geo["lng"] || !$geo["name"]) {
|
||||||
|
$this->flashFail("err", tr("error"), "Ошибка при прикреплении геометки");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(empty($this->postParam("text")) && !$photo && !$video && !$poll && !$note)
|
if(empty($this->postParam("text")) && !$photo && !$video && !$poll && !$note && !$geo)
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_empty_or_too_big"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -305,6 +314,11 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
$post->setAnonymous($anon);
|
$post->setAnonymous($anon);
|
||||||
$post->setFlags($flags);
|
$post->setFlags($flags);
|
||||||
$post->setNsfw($this->postParam("nsfw") === "on");
|
$post->setNsfw($this->postParam("nsfw") === "on");
|
||||||
|
if ($geo) {
|
||||||
|
$post->setGeo(json_encode($geo));
|
||||||
|
$post->setGeo_Lat($geo["lat"]);
|
||||||
|
$post->setGeo_Lon($geo["lng"]);
|
||||||
|
}
|
||||||
$post->save();
|
$post->save();
|
||||||
} catch (\LengthException $ex) {
|
} catch (\LengthException $ex) {
|
||||||
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
|
$this->flashFail("err", tr("failed_to_publish_post"), tr("post_is_too_big"));
|
||||||
|
@ -480,4 +494,49 @@ final class WallPresenter extends OpenVKPresenter
|
||||||
# TODO localize message based on language and ?act=(un)pin
|
# TODO localize message based on language and ?act=(un)pin
|
||||||
$this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
|
$this->flashFail("succ", tr("information_-1"), tr("changes_saved_comment"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderNearest(int $wall, int $post_id): void
|
||||||
|
{
|
||||||
|
if ($_SERVER["REQUEST_METHOD"] !== "POST") $this->notFound();
|
||||||
|
$this->assertUserLoggedIn();
|
||||||
|
|
||||||
|
$post = $this->posts->getPostById($wall, $post_id);
|
||||||
|
if(!$post)
|
||||||
|
$this->notFound();
|
||||||
|
|
||||||
|
$lat = $post->getLat();
|
||||||
|
$lon = $post->getLon();
|
||||||
|
|
||||||
|
if (!$lat || !$lon)
|
||||||
|
$this->returnJson(["success" => false, "error" => "У поста не указана гео-метка"]);
|
||||||
|
|
||||||
|
$query = file_get_contents(__DIR__ . "/../Models/sql/get-nearest-posts.tsql");
|
||||||
|
$_posts = DatabaseConnection::i()->getContext()->query($query, $lat, $lon, $post->getId())->fetchAll();
|
||||||
|
$posts = [];
|
||||||
|
foreach ($_posts as $post) {
|
||||||
|
$distance = $post["distance"];
|
||||||
|
$post = (new Posts)->get($post["id"]);
|
||||||
|
if (!$post || $post->isDeleted()) continue;
|
||||||
|
|
||||||
|
$owner = $post->getOwner();
|
||||||
|
|
||||||
|
$preview = mb_substr($post->getText(), 0, 50) . (strlen($post->getText()) > 50 ? "..." : "");
|
||||||
|
$posts[] = [
|
||||||
|
"preview" => strlen($preview) > 0 ? $preview : "(нет текста)",
|
||||||
|
"url" => "/wall" . $post->getPrettyId(),
|
||||||
|
"time" => $post->getPublicationTime()->html(),
|
||||||
|
"owner" => [
|
||||||
|
"url" => $owner->getURL(),
|
||||||
|
"avatar_url" => $owner->getAvatarURL(),
|
||||||
|
"name" => $owner->getCanonicalName(),
|
||||||
|
"verified" => $owner->isVerified(),
|
||||||
|
"writes" => ($owner instanceof User) ? ($owner->isFemale() ? tr("post_writes_f") : tr("post_writes_m")) : tr("post_writes_m")
|
||||||
|
],
|
||||||
|
"geo" => $post->getGeo(),
|
||||||
|
"distance" => $distance
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->returnJson(["success" => true, "posts" => $posts, "need_count" => count($posts) === 25]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog">
|
<div n:class="postFeedWrapper, $thisUser->hasMicroblogEnabled() ? postFeedWrapperMicroblog">
|
||||||
{include "../components/textArea.xml", route => "/wall" . $thisUser->getId() . "/makePost", graffiti => true, polls => true, notes => true}
|
{include "../components/textArea.xml", route => "/wall" . $thisUser->getId() . "/makePost", graffiti => true, polls => true, notes => true, geo => true}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{foreach $posts as $post}
|
{foreach $posts as $post}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
<div class="content_divider">
|
<div class="content_divider">
|
||||||
<div>
|
<div>
|
||||||
<div n:if="$canPost" class="content_subtitle">
|
<div n:if="$canPost" class="content_subtitle">
|
||||||
{include "../components/textArea.xml", route => "/wall$owner/makePost"}
|
{include "../components/textArea.xml", route => "/wall$owner/makePost", geo => true}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
|
@ -73,6 +73,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div n:if="$post->getGeo()" style="padding: 4px;">
|
||||||
|
<div style="border-bottom: #ECECEC solid 1px;" />
|
||||||
|
<div style="cursor: pointer; padding: 4px;" onclick="javascript:openGeo({$post->getGeo()}, {$post->getOwner()->getId()}, {$post->getVirtualId()})"><b>Геометка</b>: {$post->getGeo()->name}</div>
|
||||||
|
</div>
|
||||||
<div n:if="$post->isAd()" style="color:grey;">
|
<div n:if="$post->isAd()" style="color:grey;">
|
||||||
<br/>
|
<br/>
|
||||||
! Этот пост был размещён за взятку.
|
! Этот пост был размещён за взятку.
|
||||||
|
|
|
@ -67,6 +67,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div n:if="$post->getGeo()" style="padding: 4px;">
|
||||||
|
<div style="border-bottom: #ECECEC solid 1px;" />
|
||||||
|
<div style="cursor: pointer; padding: 4px;" onclick="javascript:openGeo({$post->getGeo()}, {$post->getOwner()->getId()}, {$post->getVirtualId()})"><b>Геометка</b>: {$post->getGeo()->name}</div>
|
||||||
|
</div>
|
||||||
<div n:if="$post->isAd()" style="color:grey;">
|
<div n:if="$post->isAd()" style="color:grey;">
|
||||||
<br/>
|
<br/>
|
||||||
! Этот пост был размещён за взятку.
|
! Этот пост был размещён за взятку.
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="post-has-note">
|
<div class="post-has-note">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="post-has-geo">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div n:if="$postOpts ?? true" class="post-opts">
|
<div n:if="$postOpts ?? true" class="post-opts">
|
||||||
{var $anonEnabled = OPENVK_ROOT_CONF['openvk']['preferences']['wall']['anonymousPosting']['enable']}
|
{var $anonEnabled = OPENVK_ROOT_CONF['openvk']['preferences']['wall']['anonymousPosting']['enable']}
|
||||||
|
@ -58,6 +61,7 @@
|
||||||
<input n:if="!OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading']" type="file" class="postFileSel" id="postFileVid" name="_vid_attachment" accept="video/*" style="display:none;" />
|
<input n:if="!OPENVK_ROOT_CONF['openvk']['preferences']['videos']['disableUploading']" type="file" class="postFileSel" id="postFileVid" name="_vid_attachment" accept="video/*" style="display:none;" />
|
||||||
<input type="hidden" name="poll" value="none" />
|
<input type="hidden" name="poll" value="none" />
|
||||||
<input type="hidden" id="note" name="note" value="none" />
|
<input type="hidden" id="note" name="note" value="none" />
|
||||||
|
<input type="hidden" id="geo" name="geo" value="none" />
|
||||||
<input type="hidden" name="type" value="1" />
|
<input type="hidden" name="type" value="1" />
|
||||||
<input type="hidden" name="hash" value="{$csrfToken}" />
|
<input type="hidden" name="hash" value="{$csrfToken}" />
|
||||||
<br/>
|
<br/>
|
||||||
|
@ -91,6 +95,10 @@
|
||||||
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/office-chart-bar-stacked.png" />
|
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/actions/office-chart-bar-stacked.png" />
|
||||||
{_poll}
|
{_poll}
|
||||||
</a>
|
</a>
|
||||||
|
<a n:if="$geo ?? false" href="javascript:initGeo({$textAreaId})">
|
||||||
|
<img src="/assets/packages/static/openvk/img/oxygen-icons/16x16/apps/amarok.png" />
|
||||||
|
Гео-метка
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -113,3 +121,10 @@
|
||||||
{script "js/vnd_literallycanvas.js"}
|
{script "js/vnd_literallycanvas.js"}
|
||||||
{css "js/node_modules/literallycanvas/lib/css/literallycanvas.css"}
|
{css "js/node_modules/literallycanvas/lib/css/literallycanvas.css"}
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{if $geo}
|
||||||
|
{script "js/node_modules/leaflet/dist/leaflet.js"}
|
||||||
|
{css "js/node_modules/leaflet/dist/leaflet.css"}
|
||||||
|
{script "js/node_modules/leaflet-control-geocoder/dist/Control.Geocoder.js"}
|
||||||
|
{css "js/node_modules/leaflet-control-geocoder/dist/Control.Geocoder.css"}
|
||||||
|
{/if}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div n:if="$canPost" class="content_subtitle">
|
<div n:if="$canPost" class="content_subtitle">
|
||||||
{include "../components/textArea.xml", route => "/wall$owner/makePost", graffiti => true, polls => true, notes => true}
|
{include "../components/textArea.xml", route => "/wall$owner/makePost", graffiti => true, polls => true, notes => true, geo => true}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
|
|
@ -137,6 +137,8 @@ routes:
|
||||||
handler: "Wall->delete"
|
handler: "Wall->delete"
|
||||||
- url: "/wall{num}_{num}/pin"
|
- url: "/wall{num}_{num}/pin"
|
||||||
handler: "Wall->pin"
|
handler: "Wall->pin"
|
||||||
|
- url: "/wall{num}_{num}/nearest"
|
||||||
|
handler: "Wall->nearest"
|
||||||
- url: "/blob_{text}/{?path}.{text}"
|
- url: "/blob_{text}/{?path}.{text}"
|
||||||
handler: "Blob->file"
|
handler: "Blob->file"
|
||||||
placeholders:
|
placeholders:
|
||||||
|
|
|
@ -262,4 +262,163 @@ async function showArticle(note_id) {
|
||||||
u("#articleText").html(`<h1 class="articleView_nameHeading">${note.title}</h1>` + note.html);
|
u("#articleText").html(`<h1 class="articleView_nameHeading">${note.title}</h1>` + note.html);
|
||||||
u("body").removeClass("dimmed");
|
u("body").removeClass("dimmed");
|
||||||
u("body").addClass("article");
|
u("body").addClass("article");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function initGeo(tid) {
|
||||||
|
MessageBox("Прикрепить геометку", "<div id=\"osm-map\"></div>", ["Прикрепить", "Отмена"], [(function () {
|
||||||
|
let marker = {
|
||||||
|
lat: currentMarker._latlng.lat,
|
||||||
|
lng: currentMarker._latlng.lng,
|
||||||
|
name: currentMarker._popup._content
|
||||||
|
};
|
||||||
|
$(`#post-buttons${tid} #geo`).val(JSON.stringify(marker));
|
||||||
|
$(`#post-buttons${tid} .post-has-geo`).text(`Геометка: ${marker.name}`);
|
||||||
|
$(`#post-buttons${tid} .post-has-geo`).show();
|
||||||
|
}), Function.noop]);
|
||||||
|
|
||||||
|
const element = document.getElementById('osm-map');
|
||||||
|
element.style = 'height: 600px;';
|
||||||
|
|
||||||
|
let markerLayers = L.layerGroup();
|
||||||
|
|
||||||
|
let map = L.map(element, {
|
||||||
|
center: [55.322978, 38.673362],
|
||||||
|
zoom: 10,
|
||||||
|
attributionControl: false,
|
||||||
|
width: 800
|
||||||
|
});
|
||||||
|
let currentMarker = null;
|
||||||
|
markerLayers.addTo(map);
|
||||||
|
|
||||||
|
map.on('click', (e) => {
|
||||||
|
let lat = e.latlng.lat;
|
||||||
|
let lng = e.latlng.lng;
|
||||||
|
|
||||||
|
if (currentMarker) map.removeLayer(currentMarker);
|
||||||
|
|
||||||
|
$.get({
|
||||||
|
url: `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=jsonv2`,
|
||||||
|
success: (response) => {
|
||||||
|
markerLayers.clearLayers();
|
||||||
|
|
||||||
|
currentMarker = L.marker([lat, lng]).addTo(map);
|
||||||
|
currentMarker.bindPopup(response?.name ?? response?.display_name).openPopup();
|
||||||
|
markerLayers.addLayer(currentMarker);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const geocoderControl = L.Control.geocoder({
|
||||||
|
defaultMarkGeocode: false,
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
geocoderControl.on('markgeocode', function (e) {
|
||||||
|
console.log(e);
|
||||||
|
let lat = e.geocode.properties.lat;
|
||||||
|
let lng = e.geocode.properties.lon;
|
||||||
|
let name = (e.geocode.properties?.name ?? e.geocode.properties.display_name);
|
||||||
|
|
||||||
|
if (currentMarker) map.removeLayer(currentMarker);
|
||||||
|
|
||||||
|
currentMarker = L.marker([lat, lng]).addTo(map);
|
||||||
|
currentMarker.bindPopup(name).openPopup();
|
||||||
|
|
||||||
|
console.log("Широта: " + lat + ", Долгота: " + lng);
|
||||||
|
console.log("Название места: " + name);
|
||||||
|
|
||||||
|
let marker = {
|
||||||
|
lat: lat,
|
||||||
|
lng: lng,
|
||||||
|
name: name
|
||||||
|
};
|
||||||
|
map.setView([lat, lng], 15);
|
||||||
|
});
|
||||||
|
|
||||||
|
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
$(".ovk-diag-cont").width('50%');
|
||||||
|
setTimeout(function(){ map.invalidateSize()}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openGeo(data, owner_id, virtual_id) {
|
||||||
|
MessageBox("Геометка", "<div id=\"osm-map\"></div>", ["Ближайшие посты", "OK"], [(function () {
|
||||||
|
getNearPosts(owner_id, virtual_id);
|
||||||
|
}), Function.noop]);
|
||||||
|
|
||||||
|
let element = document.getElementById('osm-map');
|
||||||
|
element.style = 'height: 600px;';
|
||||||
|
|
||||||
|
let map = L.map(element, {attributionControl: false});
|
||||||
|
let target = L.latLng(data.lat, data.lng);
|
||||||
|
map.setView(target, 15);
|
||||||
|
let marker = L.marker(target).addTo(map);
|
||||||
|
marker.bindPopup(data.name).openPopup();
|
||||||
|
|
||||||
|
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
$(".ovk-diag-cont").width('50%');
|
||||||
|
setTimeout(function(){ map.invalidateSize()}, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getNearPosts(owner_id, virtual_id) {
|
||||||
|
$.ajax({
|
||||||
|
type: "POST",
|
||||||
|
url: `/wall${owner_id}_${virtual_id}/nearest`,
|
||||||
|
success: (response) => {
|
||||||
|
if (response.success) {
|
||||||
|
openNearPosts(response);
|
||||||
|
} else {
|
||||||
|
MessageBox("Ошибка", "Произошла ошибка в ходе запроса:" + (response?.error ?? "Неизвестная ошибка"), ["OK"], [Function.noop]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openNearPosts(posts) {
|
||||||
|
console.log(posts);
|
||||||
|
let MsgBody = "";
|
||||||
|
|
||||||
|
posts.posts.forEach((post) => {
|
||||||
|
MsgBody += `<a style="color: inherit; display: block; margin-bottom: 8px;" href="${post.url}" target="_blank">
|
||||||
|
<table border="0" style="font-size: 11px;" class="post">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td width="54" valign="top">
|
||||||
|
<a href="${post.owner.url}">
|
||||||
|
<img src="${post.owner.avatar_url}" width="50">
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
<td width="100%" valign="top">
|
||||||
|
<div class="post-author">
|
||||||
|
<a href="${post.owner.url}"><b>${post.owner.name}</b></a>
|
||||||
|
${post.owner.verified ? `<img class="name-checkmark" src="/assets/packages/static/openvk/img/checkmark.png">` : ""}
|
||||||
|
${post.owner.writes}
|
||||||
|
<br>
|
||||||
|
<a href="${post.url}" class="date">
|
||||||
|
${post.time}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="post-content" id="2_28">
|
||||||
|
<div class="text" id="text2_28">
|
||||||
|
${post.preview}
|
||||||
|
</div>
|
||||||
|
<div style="padding: 4px;">
|
||||||
|
<div style="border-bottom: #ECECEC solid 1px;"></div>
|
||||||
|
<div style="cursor: pointer; padding: 4px;"><b>Геометка</b>: ${post.geo.name}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</a>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (posts.need_count) MsgBody += "<br/><br/><center style='color: grey;'>Показаны первые 25 постов</center>"
|
||||||
|
|
||||||
|
MessageBox("Ближайшие посты", MsgBody, ["OK"], [Function.noop]);
|
||||||
}
|
}
|
|
@ -6,6 +6,8 @@
|
||||||
"jquery": "^3.0.0",
|
"jquery": "^3.0.0",
|
||||||
"knockout": "^3.5.1",
|
"knockout": "^3.5.1",
|
||||||
"ky": "^0.19.0",
|
"ky": "^0.19.0",
|
||||||
|
"leaflet": "^1.9.4",
|
||||||
|
"leaflet-control-geocoder": "^2.4.0",
|
||||||
"literallycanvas": "^0.5.2",
|
"literallycanvas": "^0.5.2",
|
||||||
"monaco-editor": "^0.20.0",
|
"monaco-editor": "^0.20.0",
|
||||||
"msgpack-lite": "^0.1.26",
|
"msgpack-lite": "^0.1.26",
|
||||||
|
|
|
@ -173,6 +173,18 @@ ky@^0.19.0:
|
||||||
resolved "https://registry.yarnpkg.com/ky/-/ky-0.19.0.tgz#d6ad117e89efe2d85a1c2e91462d48ca1cda1f7a"
|
resolved "https://registry.yarnpkg.com/ky/-/ky-0.19.0.tgz#d6ad117e89efe2d85a1c2e91462d48ca1cda1f7a"
|
||||||
integrity sha512-RkDgbg5ahMv1MjHfJI2WJA2+Qbxq0iNSLWhreYiCHeHry9Q12sedCnP5KYGPt7sydDvsyH+8UcG6Kanq5mpsyw==
|
integrity sha512-RkDgbg5ahMv1MjHfJI2WJA2+Qbxq0iNSLWhreYiCHeHry9Q12sedCnP5KYGPt7sydDvsyH+8UcG6Kanq5mpsyw==
|
||||||
|
|
||||||
|
leaflet-control-geocoder@^2.4.0:
|
||||||
|
version "2.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/leaflet-control-geocoder/-/leaflet-control-geocoder-2.4.0.tgz#f6c00ae00b53d2ac5908e874a9aefd414f615f22"
|
||||||
|
integrity sha512-b2QlxuFd40uIDbnoUI3U9fzfnB4yKUYlmsXjquJ2d2YjoJqnyVYcIJeErAVv3kPvX3nI0gzvBq1XHMgSVFrGkQ==
|
||||||
|
optionalDependencies:
|
||||||
|
open-location-code "^1.0.0"
|
||||||
|
|
||||||
|
leaflet@^1.9.4:
|
||||||
|
version "1.9.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.4.tgz#23fae724e282fa25745aff82ca4d394748db7d8d"
|
||||||
|
integrity sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==
|
||||||
|
|
||||||
literallycanvas@^0.5.2:
|
literallycanvas@^0.5.2:
|
||||||
version "0.5.2"
|
version "0.5.2"
|
||||||
resolved "https://registry.yarnpkg.com/literallycanvas/-/literallycanvas-0.5.2.tgz#7d4800a8d9c4b38a593e91695d52466689586abd"
|
resolved "https://registry.yarnpkg.com/literallycanvas/-/literallycanvas-0.5.2.tgz#7d4800a8d9c4b38a593e91695d52466689586abd"
|
||||||
|
@ -225,6 +237,11 @@ object-assign@^4.1.0, object-assign@^4.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
|
||||||
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
|
||||||
|
|
||||||
|
open-location-code@^1.0.0:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/open-location-code/-/open-location-code-1.0.3.tgz#5ea1a34ee5221c6cafa04392e1bd906fd7488f7e"
|
||||||
|
integrity sha512-DBm14BSn40Ee241n80zIFXIT6+y8Tb0I+jTdosLJ8Sidvr2qONvymwqymVbHV2nS+1gkDZ5eTNpnOIVV0Kn2fw==
|
||||||
|
|
||||||
plotly.js-dist@^1.52.3:
|
plotly.js-dist@^1.52.3:
|
||||||
version "1.52.3"
|
version "1.52.3"
|
||||||
resolved "https://registry.yarnpkg.com/plotly.js-dist/-/plotly.js-dist-1.52.3.tgz#4c16c6da6adab6cdba169087b5005bdddbf10834"
|
resolved "https://registry.yarnpkg.com/plotly.js-dist/-/plotly.js-dist-1.52.3.tgz#4c16c6da6adab6cdba169087b5005bdddbf10834"
|
||||||
|
|
4
install/sqls/00038-posts-geo.sql
Normal file
4
install/sqls/00038-posts-geo.sql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
ALTER TABLE `Posts`
|
||||||
|
ADD `geo` LONGTEXT NULL DEFAULT NULL AFTER `deleted`,
|
||||||
|
ADD `geo_lat` DECIMAL(10, 8) NULL DEFAULT NULL AFTER `geo`,
|
||||||
|
ADD `geo_lon` DECIMAL(10, 8) NULL DEFAULT NULL AFTER `geo_lat`;
|
Loading…
Reference in a new issue