diff --git a/public/static/css/map_editor.css b/public/static/css/map_editor.css index fe0b474..b173251 100644 --- a/public/static/css/map_editor.css +++ b/public/static/css/map_editor.css @@ -1,3 +1,17 @@ +#metadata { + position: absolute; + top: 50px; + left: 10px; + padding: 10px; + background-color: #eeeeee; + border: solid 1px #555555; + border-radius: 3px; + box-sizing: border-box; + opacity: 0.95; + z-index: 2; + visibility: hidden; +} + #map { width: 100%; height: calc(100% - 40px); @@ -28,12 +42,15 @@ #control { position: absolute; + top: 50px; + right: 10px; width: 125px; z-index: 3; } #placeControl { position: absolute; + right: 10px; z-index: 3; width: 100px; visibility: hidden; @@ -44,6 +61,9 @@ } @media screen and (max-width: 999px) and (min-height: 600px) { + #metadata { + width: calc(100% - 155px); + } #map.selected { height: calc(50% - 20px); } @@ -53,17 +73,18 @@ right: 0; height: calc(50% - 20px); } - #control { - right: 10px; - top: 50px; - } #placeControl { - right: 10px; top: calc(50% + 30px); } } @media screen and (min-width: 1000px), (max-height: 599px) { + #metadata { + width: calc(50% - 20px); + } + #metadata.selected { + top: 95px; + } #map.selected { width: 50%; } @@ -73,12 +94,13 @@ right: 0; width: 50%; } - #control, #placeControl { - right: 10px; + #placeControl { top: 50px; } + #modified.selected { + right: calc(50% + 10px); + } #control.selected { right: calc(50% + 10px); - top: 50px; } } diff --git a/public/static/js/map_editor.js b/public/static/js/map_editor.js index a5f0726..494ee00 100644 --- a/public/static/js/map_editor.js +++ b/public/static/js/map_editor.js @@ -2,6 +2,10 @@ (function () { var MapEditor = { + metadata: { + name: null, + description: null + }, map: null, panorama: null, selectedMarker: null, @@ -9,6 +13,19 @@ edited: {}, deleted: {}, + editMetadata: function () { + var form = document.getElementById('metadataForm'); + + MapEditor.metadata.name = form.elements.name.value; + MapEditor.metadata.description = form.elements.description.value; + + document.getElementById('mapName').innerHTML = form.elements.name.value ? form.elements.name.value : '[unnamed map]'; + + document.getElementById('metadata').style.visibility = 'hidden'; + + document.getElementById('saveButton').disabled = false; + }, + getPlace: function (placeId, marker) { var xhr = new XMLHttpRequest(); xhr.responseType = 'json'; @@ -92,9 +109,11 @@ select: function (marker) { if (MapEditor.selectedMarker === marker) { + MapEditor.closePlace(); return; } + document.getElementById('metadata').classList.add('selected'); document.getElementById('map').classList.add('selected'); document.getElementById('control').classList.add('selected'); document.getElementById('noPano').style.visibility = 'hidden'; @@ -141,14 +160,14 @@ } }, - resetSelected: function () { + resetSelected: function (del) { if (!MapEditor.selectedMarker) { return; } var placeId = MapEditor.selectedMarker.placeId - if (places[placeId].id) { + if (places[placeId].id && !del) { MapEditor.selectedMarker.setIcon(places[placeId].noPano ? IconCollection.iconRed : IconCollection.iconGreen); MapEditor.selectedMarker.setZIndexOffset(1000); } else { @@ -191,29 +210,25 @@ } MapEditor.selectedMarker.setLatLng({ lat: places[placeId].lat, lng: places[placeId].lng }); + + document.getElementById('saveButton').disabled = false; }, - closePlace: function () { + closePlace: function (del) { + document.getElementById('metadata').classList.remove('selected') document.getElementById('map').classList.remove('selected'); document.getElementById('control').classList.remove('selected'); document.getElementById('noPano').style.visibility = 'hidden'; document.getElementById('panorama').style.visibility = 'hidden'; document.getElementById('placeControl').style.visibility = 'hidden'; - MapEditor.resetSelected(); + MapEditor.resetSelected(del); MapEditor.selectedMarker = null; MapEditor.map.invalidateSize(true); }, deletePlace: function () { - document.getElementById('map').classList.remove('selected'); - document.getElementById('control').classList.remove('selected'); - document.getElementById('noPano').style.visibility = 'hidden'; - document.getElementById('panorama').style.visibility = 'hidden'; - document.getElementById('placeControl').style.visibility = 'hidden'; - document.getElementById('deleteButton').style.display = 'none'; - var placeId = MapEditor.selectedMarker.placeId; if (places[placeId].id && !MapEditor.added[placeId]) { @@ -222,23 +237,29 @@ document.getElementById('deleted').innerHTML = String(Object.keys(MapEditor.deleted).length); } - delete places[placeId]; + MapEditor.closePlace(true); + delete MapEditor.added[placeId]; delete MapEditor.edited[placeId]; document.getElementById('added').innerHTML = String(Object.keys(MapEditor.added).length); document.getElementById('edited').innerHTML = String(Object.keys(MapEditor.edited).length); - MapEditor.map.removeLayer(MapEditor.selectedMarker); - MapEditor.selectedMarker = null; - - MapEditor.map.invalidateSize(true); + document.getElementById('saveButton').disabled = false; }, saveMap: function () { document.getElementById('loading').style.visibility = 'visible'; var data = new FormData(); + + if (MapEditor.metadata.name !== null) { + data.append('name', MapEditor.metadata.name); + } + if (MapEditor.metadata.description !== null) { + data.append('description', MapEditor.metadata.description); + } + for (var placeId in MapEditor.added) { if (!MapEditor.added.hasOwnProperty(placeId)) { continue; @@ -277,6 +298,8 @@ document.getElementById('added').innerHTML = '0'; document.getElementById('edited').innerHTML = '0'; document.getElementById('deleted').innerHTML = '0'; + + document.getElementById('saveButton').disabled = true; }; xhr.open('POST', '/admin/saveMap/' + mapId + '/json', true); @@ -381,6 +404,29 @@ motionTracking: false }); + document.getElementById('mapName').onclick = function (e) { + e.preventDefault(); + + var metadata = document.getElementById('metadata'); + + if (metadata.style.visibility === 'visible') { + metadata.style.visibility = 'hidden'; + } else { + metadata.style.visibility = 'visible'; + document.getElementById('metadataForm').elements.name.select(); + } + }; + + document.getElementById('metadataForm').onsubmit = function (e) { + e.preventDefault(); + + MapEditor.editMetadata(); + }; + + document.getElementById('closeMetadataButton').onclick = function () { + document.getElementById('metadata').style.visibility = 'hidden'; + }; + document.getElementById('saveButton').onclick = function () { MapEditor.saveMap(); }; @@ -389,7 +435,7 @@ MapEditor.applyPlace(); }; - document.getElementById('cancelButton').onclick = function () { + document.getElementById('closeButton').onclick = function () { MapEditor.closePlace(); }; diff --git a/src/Controller/MapAdminController.php b/src/Controller/MapAdminController.php index fcf2782..f4809c8 100644 --- a/src/Controller/MapAdminController.php +++ b/src/Controller/MapAdminController.php @@ -8,6 +8,7 @@ use MapGuesser\Interfaces\Authorization\ISecured; use MapGuesser\Interfaces\Database\IResultSet; use MapGuesser\Interfaces\Request\IRequest; use MapGuesser\Interfaces\Response\IContent; +use MapGuesser\Repository\MapRepository; use MapGuesser\Repository\PlaceRepository; use MapGuesser\Response\HtmlContent; use MapGuesser\Response\JsonContent; @@ -18,11 +19,14 @@ class MapAdminController implements ISecured { private IRequest $request; + private MapRepository $mapRepository; + private PlaceRepository $placeRepository; public function __construct(IRequest $request) { $this->request = $request; + $this->mapRepository = new MapRepository(); $this->placeRepository = new PlaceRepository(); } @@ -44,11 +48,11 @@ class MapAdminController implements ISecured { $mapId = (int) $this->request->query('mapId'); - $bounds = $this->getMapBounds($mapId); - + $map = $this->mapRepository->getById($mapId); + $bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']); $places = $this->getPlaces($mapId); - $data = ['mapId' => $mapId, 'bounds' => $bounds->toArray(), 'places' => &$places]; + $data = ['mapId' => $mapId, 'mapName' => $map['name'], 'mapDescription' => str_replace('
', '\n', $map['description']), 'bounds' => $bounds->toArray(), 'places' => &$places]; return new HtmlContent('admin/map_editor', $data); } @@ -109,25 +113,19 @@ class MapAdminController implements ISecured 'bound_east_lng' => $mapBounds->getEastLng() ]; + if (isset($_POST['name'])) { + $map['name'] = $_POST['name'] ? $_POST['name'] : '[unnamed map]'; + } + if (isset($_POST['description'])) { + $map['description'] = str_replace(['\n', '\r\n'], '
', $_POST['description']); + } + $this->saveMapData($mapId, $map); $data = ['added' => $addedIds]; return new JsonContent($data); } - private function getMapBounds(int $mapId): Bounds - { - $select = new Select(\Container::$dbConnection, 'maps'); - $select->columns(['bound_south_lat', 'bound_west_lng', 'bound_north_lat', 'bound_east_lng']); - $select->whereId($mapId); - - $map = $select->execute()->fetch(IResultSet::FETCH_ASSOC); - - $bounds = Bounds::createDirectly($map['bound_south_lat'], $map['bound_west_lng'], $map['bound_north_lat'], $map['bound_east_lng']); - - return $bounds; - } - private function calculateMapBounds(int $mapId): Bounds { $select = new Select(\Container::$dbConnection, 'places'); diff --git a/views/admin/map_editor.php b/views/admin/map_editor.php index 1917063..cea6b25 100644 --- a/views/admin/map_editor.php +++ b/views/admin/map_editor.php @@ -3,18 +3,53 @@

- + MapGuesser

-

Added: 0 | Edited: 0 | Deleted: 0

+

+ + + + + + 0 + + + 0 + + + + 0 +

+
+
+ + +
+ + +
+
+
- - Back to maps +
@@ -22,7 +57,7 @@
- +