diff --git a/appinfo/routes.php b/appinfo/routes.php index e9f7b5363..729616b20 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -21,6 +21,8 @@ // photos ['name' => 'photos#getPhotosFromDb', 'url' => '/photos', 'verb' => 'GET'], ['name' => 'photos#getNonLocalizedPhotosFromDb', 'url' => '/photos/nonlocalized', 'verb' => 'GET'], + ['name' => 'photos#getNonLocalizedPhotoIds', 'url' => '/photos/nonlocalizedids', 'verb' => 'GET'], + ['name' => 'photos#getNonLocalizedPhotosByIds', 'url' => '/photos/nonlocalizedbyids', 'verb' => 'GET'], ['name' => 'photos#placePhotos', 'url' => '/photos', 'verb' => 'POST'], // contacts diff --git a/js/nonLocalizedPhotosController.js b/js/nonLocalizedPhotosController.js index 29f3eb5ce..1e80339ee 100644 --- a/js/nonLocalizedPhotosController.js +++ b/js/nonLocalizedPhotosController.js @@ -14,6 +14,7 @@ function NonLocalizedPhotosController (optionsController, timeFilterController, this.nonLocalizedPhotoMarkersLastVisible = -1; this.timeFilterBegin = 0; this.timeFilterEnd = Date.now(); + this.nonLocalizedPhotoIds = []; } NonLocalizedPhotosController.prototype = { @@ -30,6 +31,7 @@ NonLocalizedPhotosController.prototype = { iconSize: [this.PHOTO_MARKER_VIEW_SIZE, this.PHOTO_MARKER_VIEW_SIZE] } }); + this.callForImageIds(); this.nonLocalizedPhotoLayer.on('click', this.getNonLocalizedPhotoMarkerOnClickFunction()); this.nonLocalizedPhotoLayer.on('clusterclick', function (a) { if (a.layer.getChildCount() > 20) { @@ -48,7 +50,7 @@ NonLocalizedPhotosController.prototype = { that.optionsController.saveOptionValues({nonLocalizedPhotosLayer: that.map.hasLayer(that.nonLocalizedPhotoLayer)}); that.updateTimeFilterRange(); that.timeFilterController.setSliderToMaxInterval(); - }); + }) // click on menu button $('body').on('click', '.nonLocalizedPhotosMenuButton', function(e) { var wasOpen = $(this).parent().parent().parent().find('>.app-navigation-entry-menu').hasClass('open'); @@ -269,30 +271,56 @@ NonLocalizedPhotosController.prototype = { } }, - callForImages: function() { - this.nonLocalizedPhotosRequestInProgress = true; - $('#navigation-nonLocalizedPhotos').addClass('icon-loading-small'); + callForImageIds: function() { $.ajax({ - url: OC.generateUrl('apps/maps/photos/nonlocalized'), + url: OC.generateUrl('apps/maps/photos/nonlocalizedids'), type: 'GET', async: true, context: this }).done(function (response) { - if (response.length == 0) { + if (response.length === 0) { //showNoPhotosMessage(); } else { - this.addNonLocalizedPhotosToMap(response); + this.nonLocalizedPhotoIds=response; } - this.nonLocalizedPhotosDataLoaded = true; }).always(function (response) { - this.nonLocalizedPhotosRequestInProgress = false; - $('#navigation-nonLocalizedPhotos').removeClass('icon-loading-small'); + //do something; }).fail(function() { OC.Notification.showTemporary(t('maps', 'Failed to load non-geolocalized photos')); }); }, + callForImages: function() { + this.nonLocalizedPhotosRequestInProgress = true; + $('#navigation-nonLocalizedPhotos').addClass('icon-loading-small'); + var that = this; + for (var i=0; i.app-navigation-entry-menu').addClass('open'); } }); + // expand navigation + $('body').on('click', '#navigation-photos', function(e) { + if (e.target.tagName === 'LI' && $(e.target).attr('id') === 'navigation-photos') { + that.toggleNavigation(); + that.optionsController.saveOptionValues({photosNavigationShow: $('#navigation-favorites').hasClass('open')}); + } + }); }, updateMyFirstLastDates: function() { @@ -86,6 +93,10 @@ PhotosController.prototype = { } }, + toggleNavigation: function() { + $('#navigation-photos').toggleClass('open'); + }, + getPhotoMarkerOnClickFunction: function() { var _app = this; return function(evt) { diff --git a/lib/Controller/PhotosController.php b/lib/Controller/PhotosController.php index 5e72faea2..40b064ca8 100644 --- a/lib/Controller/PhotosController.php +++ b/lib/Controller/PhotosController.php @@ -53,6 +53,23 @@ public function getNonLocalizedPhotosFromDb() { return new DataResponse($result); } + /** + * @NoAdminRequired + * @NoCSRFRequired + */ + public function getNonLocalizedPhotoIds() { + $result = $this->geophotoService->getNonLocalizedIdsFromDB($this->userId); + return new DataResponse($result); + } + + /** + * @param $ids + * @NoAdminRequired + * @NoCSRFRequired + */ + public function getNonLocalizedPhotosByIds($ids) { + return new DataResponse($this->geophotoService->getNonLocalizedByIds($this->userId, $ids)); + } /** * @NoAdminRequired diff --git a/lib/DB/GeophotoMapper.php b/lib/DB/GeophotoMapper.php index 4a4e0d6c9..2be8a16b4 100644 --- a/lib/DB/GeophotoMapper.php +++ b/lib/DB/GeophotoMapper.php @@ -27,6 +27,18 @@ public function find($id) { return $this->findEntity($sql, [$id]); } + public function findByUserAndId($userId, $id) { + try { + $sql = 'SELECT * FROM `*PREFIX*maps_photos` ' . + 'WHERE `user_id` = ? ' . + 'AND `id` = ? '; + return $this->findEntity($sql, [$userId, $id]); + } + catch (\Throwable $e) { + return null; + } + } + public function findByFileId($userId, $fileId) { try { $sql = 'SELECT * FROM `*PREFIX*maps_photos` ' . diff --git a/lib/Service/GeophotoService.php b/lib/Service/GeophotoService.php index e19d90be1..61b782e49 100644 --- a/lib/Service/GeophotoService.php +++ b/lib/Service/GeophotoService.php @@ -19,12 +19,14 @@ use OCP\Files\Folder; use OCP\IPreview; use OCP\ILogger; +use OCP\ICache; use OCA\Maps\Service\PhotofilesService; use OCA\Maps\DB\Geophoto; use OCA\Maps\DB\GeophotoMapper; use OCA\Maps\Service\TracksService; +use OCA\Maps\Service\DevicesService; class GeophotoService { @@ -34,18 +36,21 @@ class GeophotoService { private $logger; private $preview; private $tracksService; - private $timeordedPointSets; + private $timeorderedPointSets; + private $devicesService; + private $cache; - public function __construct (ILogger $logger, IRootFolder $root, IL10N $l10n, GeophotoMapper $photoMapper, IPreview $preview, TracksService $tracksService, $userId) { + public function __construct (ILogger $logger, IRootFolder $root, IL10N $l10n, GeophotoMapper $photoMapper, IPreview $preview, TracksService $tracksService, DevicesService $devicesService, ICache $cache, $userId) { $this->root = $root; $this->l10n = $l10n; $this->photoMapper = $photoMapper; $this->logger = $logger; $this->preview = $preview; $this->tracksService = $tracksService; - $this->timeordedPointSets = null; + $this->timeorderedPointSets = null; $this->userId = $userId; - + $this->devicesService = $devicesService; + $this->cache = $cache; } /** @@ -91,7 +96,7 @@ public function getAllFromDB ($userId) { * @return array with geodatas of all nonLocalizedPhotos */ public function getNonLocalizedFromDB ($userId) { - $foo = $this->loadTimeordedPointSets($userId); + $foo = $this->loadTimeOrderedPointSets($userId); $photoEntities = $this->photoMapper->findAllNonLocalized($userId); $userFolder = $this->getFolderForUser($userId); $filesById = []; @@ -128,6 +133,80 @@ public function getNonLocalizedFromDB ($userId) { return $filesById; } + /** + * @param string $userId + * @return array with all id's of nonLocalizedPhotos + */ + public function getNonLocalizedIdsFromDB ($userId) { + $foo = $this->loadTimeOrderedPointSets($userId); + $photoEntities = $this->photoMapper->findAllNonLocalized($userId); + $fileIds = []; + $photoEntitiesById = []; + foreach ($photoEntities as $photoEntity) { + $fileIds[] = $photoEntity->getId(); + $photoEntitiesById[$photoEntity->getId()] = [ + "dateTaken"=>$photoEntity->getDateTaken(), + "fileId"=>$photoEntity->getFileId(), + ]; + } + $this->cache->set('mapsPhotoInformationById', json_encode($photoEntitiesById), $ttl=300); + $this->cache->set('mapsPhotoInformationByIdTime', time(), $ttl=300); + return $fileIds; + } + /** + * + */ + public function getNonLocalizedByIds($userId, $ids) { + $foo = $this->loadTimeOrderedPointSets($userId); + $photoInformationById = json_decode($this->cache->get('mapsPhotoInformationById'),TRUE); + if (!is_array($photoInformationById) or time()-$this->cache->get('mapsPhotoInformationByIdTime')>300) { + $foo = $this->getNonLocalizedIdsFromDB($userId); + } + $filesById = []; + foreach ($ids as $id) { + if (array_key_exists($id, $photoInformationById)) { + $photoInformation = $photoInformationById[$id]; + } else { + $photoEntity = $this->photoMapper->findByUserAndId($userId, $id); + $photoInformation = [ + "dateTaken"=>$photoEntity->getDateTaken(), + "fileId"=>$photoEntity->getFileId(), + ]; + } + if (!is_null($photoInformation)) { + $userFolder = $this->getFolderForUser($userId); + + $cache = $userFolder->getStorage()->getCache(); + $previewEnableMimetypes = $this->getPreviewEnabledMimetypes(); + + $cacheEntry = $cache->get($photoInformation["fileId"]); + if ($cacheEntry) { + // this path is relative to owner's storage + //$path = $cacheEntry->getPath(); + // but we want it relative to current user's storage + $file = $userFolder->getById($photoInformation["fileId"])[0]; + if (!is_null($file)) { + $path = preg_replace('/^\/'.$userId.'\//', '', $file->getPath()); + + $date = $photoInformation["dateTaken"] ?? \time(); + $locations = $this->getLocationGuesses($date); + foreach ($locations as $location) { + $file_object = new \stdClass(); + $file_object->fileId = $photoInformation["fileId"]; + $file_object->path = $this->normalizePath($path); + $file_object->hasPreview = in_array($cacheEntry->getMimeType(), $previewEnableMimetypes); + $file_object->lat = $location[0]; + $file_object->lng = $location[1]; + $file_object->dateTaken = $date; + $filesById[] = $file_object; + }; + } + } + } + } + + return $filesById; + } /** * returns a array of locations for a given date * @param $dateTaken @@ -135,7 +214,7 @@ public function getNonLocalizedFromDB ($userId) { */ private function getLocationGuesses($dateTaken) { $locations = []; - foreach ($this->timeordedPointSets as $timeordedPointSet) { + foreach ($this->timeorderedPointSets as $timeordedPointSet) { $location = $this->getLocationFromSequenceOfPoints($dateTaken,$timeordedPointSet); if (!is_null($location)) { $locations[] = $location; @@ -152,7 +231,13 @@ private function getLocationGuesses($dateTaken) { * Timeorderd Point sets is an Array of Arrays with time => location as key=>value pair, which are orderd by the key. * This function loads this Arrays from all Track files of the user. */ - private function loadTimeordedPointSets($userId) { + private function loadTimeOrderedPointSets($userId) { + $this->timeorderedPointSets = json_decode($this->cache->get('mapsTimeOrderedPointSets'),TRUE); + if (is_array($this->timeorderedPointSets) and time()-$this->cache->get('mapsTimeOrderedPointSetsTime')<300) { + $this->logger->debug("timeorderedPointSets loaded from cache"); + return null; + } + $this->timeorderedPointSets = []; $userFolder = $this->getFolderForUser($userId); foreach ($this->tracksService->getTracksFromDB($userId) as $gpxfile) { $res = $userFolder->getById($gpxfile['file_id']); @@ -160,11 +245,23 @@ private function loadTimeordedPointSets($userId) { $file = $res[0]; if ($file->getType() === \OCP\Files\FileInfo::TYPE_FILE) { foreach ($this->getTracksFromGPX($file->getContent()) as $track) { - $this->timeordedPointSets[] = $this->getTimeorderdPointsFromTrack($track); + $this->timeorderedPointSets[] = $this->getTimeorderdPointsFromTrack($track); } } } } + foreach ($this->devicesService->getDevicesFromDB($userId) as $device) { + $device_points = $this->devicesService->getDevicePointsFromDB($userId, $device); + $points = []; + foreach ($device_points as $pt) { + $points[$pt->timestamp] = [(string) $pt->lat, (string) $pt->lng]; + } + $foo = ksort($points); + $this->timeorderedPointSets[] = $points; + } + $this->logger->debug("timeorderedPointSets created"); + $this->cache->set('mapsTimeOrderedPointSets', json_encode($this->timeorderedPointSets), $ttl=300); + $this->cache->set('mapsTimeOrderedPointSetsTime', time(), $ttl=300); return null; } @@ -179,6 +276,7 @@ private function getTracksFromGPX($content) { foreach ($gpx->trk as $trk) { $tracks[] = $trk; } + return $tracks; } @@ -207,6 +305,13 @@ private function getTimeorderdPointsFromTrack($track) { * @param $points array sorted by keys timestamp => [lat, lng] */ private function getLocationFromSequenceOfPoints($dateTaken, $points) { + $foo = end($points); + $end = key($points); + $foo = reset($points); + $start = key($points); + if ($start > $dateTaken OR $end < $dateTaken) { + return null; + } $smaller = null; $bigger = null; foreach ($points as $time => $locations) { diff --git a/templates/navigation/index.php b/templates/navigation/index.php index 230032ae8..d33f38bfb 100644 --- a/templates/navigation/index.php +++ b/templates/navigation/index.php @@ -68,7 +68,7 @@ -