Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions appinfo/application.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,26 @@
use OC\AppFramework\Utility\SimpleContainer;
use \OCP\AppFramework\App;
use OCA\Maps\Controller\PageController;
use OCA\Maps\Hook\FileHooks;
use OCA\Maps\Service\PhotofilesService;


class Application extends App {
public function __construct (array $urlParams=array()) {
parent::__construct('maps', $urlParams);

$container = $this->getContainer();

$this->getContainer()->registerService('FileHooks', function($c) {
return new FileHooks(
$c->query('ServerContainer')->getRootFolder(),
\OC::$server->query(PhotofilesService::class),
$c->query('ServerContainer')->getLogger(),
$c->query('AppName')
);
});

$this->getContainer()->query('FileHooks')->register();
}

}
35 changes: 35 additions & 0 deletions appinfo/database.xml
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,39 @@
</field>
</declaration>
</table>
<table>
<name>*dbprefix*maps_photos</name>
<declaration>
<field>
<name>id</name>
<type>integer</type>
<default>0</default>
<notnull>true</notnull>
<autoincrement>1</autoincrement>
<length>41</length>
</field>
<field>
<name>user_id</name>
<type>text</type>
<notnull>true</notnull>
<length>64</length>
</field>
<field>
<name>file_id</name>
<type>integer</type>
<notnull>true</notnull>
<length>10</length>
</field>
<field>
<name>lat</name>
<type>float</type>
<notnull>true</notnull>
</field>
<field>
<name>lng</name>
<type>float</type>
<notnull>true</notnull>
</field>
</declaration>
</table>
</database>
6 changes: 6 additions & 0 deletions appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,10 @@
<route>maps.page.index</route>
</navigation>
</navigations>
<types>
<filesystem/>
</types>
<commands>
<command>OCA\Maps\Command\RescanPhotos</command>
</commands>
</info>
5 changes: 4 additions & 1 deletion appinfo/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
return [
'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'page#do_echo', 'url' => '/echo', 'verb' => 'POST'],
['name' => 'page#do_echo', 'url' => '/echo', 'verb' => 'POST'],

//photos
['name' => 'photos#getPhotosFromDb', 'url' => '/photos', 'verb' => 'GET'],
]
];
45 changes: 45 additions & 0 deletions css/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,48 @@ tr.selected td {
.leaflet-popup-content-wrapper {
border-radius: 3px !important;
}

.leaflet-marker-photo {
border: 2px solid #fff;
box-shadow: 3px 3px 10px #888;
}

.leaflet-marker-photo.photo-marker{
top: -10px;
}

.leaflet-marker-photo.photo-marker:after {
content:"";
position: relative;
bottom: 16px;
border-width: 10px 10px 0;
border-style: solid;
border-color: #fff transparent;
display: block;
width: 0;
margin-left: auto;
margin-right: auto;
}

.leaflet-marker-photo .thumbnail {
width: 100%;
height: 100%;
background-size: cover;
background-position: center center;
background-repeat: no-repeat;
background-color: white;
}

.leaflet-marker-photo .label {
position: absolute;
top: -7px;
right: -11px;
color: #fff;
background-color: #333;
border-radius: 9px;
height: 18px;
min-width: 18px;
line-height: 12px;
text-align: center;
padding: 3px;
}
155 changes: 155 additions & 0 deletions js/photosController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
function PhotosController () {
this.PHOTO_MARKER_VIEW_SIZE = 40;
this.photosDataLoaded = false;
this.photosRequestInProgress = false;
}

PhotosController.prototype = {

appendToMap : function(map) {
this.map = map;
this.photoLayer = L.markerClusterGroup({
iconCreateFunction : this.getClusterIconCreateFunction(),
showCoverageOnHover : false,
maxClusterRadius: this.PHOTO_MARKER_VIEW_SIZE + 10,
icon: {
iconSize: [this.PHOTO_MARKER_VIEW_SIZE, this.PHOTO_MARKER_VIEW_SIZE]
}
});
this.photoLayer.on('click', this.getPhotoMarkerOnClickFunction());
this.photoLayer.addTo(this.map);
},

showLayer: function() {
if (!this.photosDataLoaded && !this.photosRequestInProgress) {
this.callForImages();
}
if (!this.map.hasLayer(this.photoLayer)) {
this.map.addLayer(this.photoLayer);
}
},

hideLayer: function() {
if (this.map.hasLayer(this.photoLayer)) {
this.map.removeLayer(this.photoLayer);
}
},

getPhotoMarkerOnClickFunction() {
var _app = this;
return function(evt) {
var marker = evt.layer;
var content;
if (marker.data.hasPreview) {
var previewUrl = _app.generatePreviewUrl(marker.data.path);
var img = "<img src=" + previewUrl + "/>";
//Workaround for https://github.com/Leaflet/Leaflet/issues/5484
$(img).on('load', function() {
marker.getPopup().update();
});
content = img;
} else {
content = marker.data.path;
}
marker.bindPopup(content, {
className: 'leaflet-popup-photo',
maxWidth: "auto"
}).openPopup();
}
},

getClusterIconCreateFunction() {
var _app = this;
return function(cluster) {
var marker = cluster.getAllChildMarkers()[0].data;
var iconUrl;
if (marker.hasPreview) {
iconUrl = _app.generatePreviewUrl(marker.path);
} else {
iconUrl = _app.getImageIconUrl();
}
var label = cluster.getChildCount();
return new L.DivIcon(L.extend({
className: 'leaflet-marker-photo cluster-marker',
html: '<div class="thumbnail" style="background-image: url(' + iconUrl + ');"></div>​<span class="label">' + label + '</span>'
}, this.icon));
}
},

createPhotoView: function(markerData) {
var iconUrl;
if (markerData.hasPreview) {
iconUrl = this.generatePreviewUrl(markerData.path);
} else {
iconUrl = this.getImageIconUrl();
}
this.generatePreviewUrl(markerData.path);
return L.divIcon(L.extend({
html: '<div class="thumbnail" style="background-image: url(' + iconUrl + ');"></div>​',
className: 'leaflet-marker-photo photo-marker'
}, markerData, {
iconSize: [this.PHOTO_MARKER_VIEW_SIZE, this.PHOTO_MARKER_VIEW_SIZE],
iconAnchor: [this.PHOTO_MARKER_VIEW_SIZE / 2, this.PHOTO_MARKER_VIEW_SIZE]
}));
},

addPhotosToMap : function(photos) {
var markers = this.preparePhotoMarkers(photos);
this.photoLayer.addLayers(markers);
},

preparePhotoMarkers : function(photos) {
var markers = [];
for (var i = 0; i < photos.length; i++) {
var markerData = {
lat: photos[i].lat,
lng: photos[i].lng,
path: photos[i].path,
albumId: photos[i].folderId,
hasPreview : photos[i].hasPreview
};
var marker = L.marker(markerData, {
icon: this.createPhotoView(markerData)
});
marker.data = markerData;
markers.push(marker);
}
return markers;
},

callForImages: function() {
this.photosRequestInProgress = true;
$.ajax({
'url' : OC.generateUrl('apps/maps/photos'),
'type': 'GET',
'context' : this,
'success': function(response) {
if (response.length == 0) {
//showNoPhotosMessage();
} else {
this.addPhotosToMap(response);
}
this.photosDataLoaded = true;
},
'complete': function(response) {
this.photosRequestInProgress = false;
}
});
},

/* Preview size 32x32 is used in files view, so it sould be generated */
generateThumbnailUrl: function (filename) {
return OC.generateUrl('core') + '/preview.png?file=' + encodeURI(filename) + '&x=32&y=32';
},

/* Preview size 375x211 is used in files details view */
generatePreviewUrl: function (filename) {
return OC.generateUrl('core') + '/preview.png?file=' + encodeURI(filename) + '&x=375&y=211&a=1';
},

getImageIconUrl: function() {
return OC.generateUrl('/apps/theming/img/core/filetypes') + '/image.svg?v=2';
}

};

4 changes: 4 additions & 0 deletions js/script.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
(function($, OC) {
$(function() {
mapController.initMap();
photosController.appendToMap(mapController.map);
photosController.showLayer();

// Popup
$(document).on('click', '#opening-hours-header', function() {
Expand Down Expand Up @@ -65,6 +67,8 @@
}
};

var photosController = new PhotosController();

var searchController = {
isGeocodeabe: function(str) {
var pattern = /^\s*\d+\.?\d*\,\s*\d+\.?\d*\s*$/;
Expand Down
77 changes: 77 additions & 0 deletions lib/Command/RescanPhotos.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

/**
* Nextcloud - maps
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Piotr Bator <prbator@gmail.com>
* @copyright Piotr Bator 2017
*/

namespace OCA\Maps\Command;

use OCP\Encryption\IManager;
use OCP\Files\NotFoundException;
use OCP\IUser;
use OCP\IUserManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

use OCA\Maps\Service\PhotofilesService;

class RescanPhotos extends Command {

protected $userManager;

protected $output;

protected $encryptionManager;

private $photofilesService;

public function __construct(IUserManager $userManager,
IManager $encryptionManager,
PhotofilesService $photofilesService) {
parent::__construct();
$this->userManager = $userManager;
$this->encryptionManager = $encryptionManager;
$this->photofilesService = $photofilesService;
}
protected function configure() {
$this->setName('maps:scan-photos')
->setDescription('Rescan photos GPS exif data')
->addArgument(
'user_id',
InputArgument::OPTIONAL,
'Rescan photos GPS exif data for the given user'
);
}

protected function execute(InputInterface $input, OutputInterface $output) {
if ($this->encryptionManager->isEnabled()) {
$output->writeln('Encryption is enabled. Aborted.');
return 1;
}
$this->output = $output;
$userId = $input->getArgument('user_id');
if ($userId === null) {
$this->userManager->callForSeenUsers(function (IUser $user) {
$this->rescanUserPhotos($user->getUID());
});
} else {
$user = $this->userManager->get($userId);
if ($user !== null) {
$this->rescanUserPhotos($userId);
}
}
return 0;
}

private function rescanUserPhotos($userId) {
$this->photofilesService->rescan($userId);
}
}
Loading