diff --git a/.babelrc.js b/.babelrc.js
new file mode 100644
index 000000000..e54da4c9a
--- /dev/null
+++ b/.babelrc.js
@@ -0,0 +1,11 @@
+module.exports = {
+ plugins: [
+ '@babel/plugin-syntax-dynamic-import',
+ '@babel/plugin-proposal-object-rest-spread'
+ ],
+ presets: [
+ [
+ '@babel/preset-env'
+ ]
+ ]
+}
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 000000000..220348c2b
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,27 @@
+module.exports = {
+ root: true,
+ env: {
+ node: true
+ },
+ extends: [
+ "plugin:vue/recommended",
+ "eslint:recommended",
+ "prettier/vue",
+ "plugin:prettier/recommended"
+ ],
+ rules: {
+ "vue/component-name-in-template-casing": ["error", "PascalCase"],
+ "no-console": process.env.NODE_ENV === "production" ? "error" : "off",
+ "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off"
+ },
+ globals: {
+ OC: false,
+ OCA: false,
+ t: false,
+ n: false,
+ $: false // TODO: remove once jQuery has been removed
+ },
+ parserOptions: {
+ parser: "babel-eslint"
+ }
+};
diff --git a/.stylelintrc.json b/.stylelintrc.json
new file mode 100644
index 000000000..40db42c66
--- /dev/null
+++ b/.stylelintrc.json
@@ -0,0 +1,3 @@
+{
+ "extends": "stylelint-config-standard"
+}
diff --git a/appinfo/application.php b/appinfo/application.php
index 8ffc8e0c1..8b081be00 100644
--- a/appinfo/application.php
+++ b/appinfo/application.php
@@ -12,14 +12,13 @@
namespace OCA\Maps\AppInfo;
-use OC\AppFramework\Utility\SimpleContainer;
-use OCA\Maps\Service\AddressService;
+use OCA\Maps\Controller\PublicFavoritesApiController;
use \OCP\AppFramework\App;
-use OCA\Maps\Controller\PageController;
use OCA\Maps\Controller\UtilsController;
use OCA\Maps\Controller\FavoritesController;
use OCA\Maps\Controller\FavoritesApiController;
use OCA\Maps\Controller\DevicesController;
+use OCA\Maps\Controller\PublicPageController;
use OCA\Maps\Controller\DevicesApiController;
use OCA\Maps\Controller\RoutingController;
use OCA\Maps\Controller\TracksController;
@@ -37,7 +36,7 @@ public function __construct (array $urlParams=array()) {
$container = $this->getContainer();
- $this->getContainer()->registerService('FileHooks', function($c) {
+ $this->getContainer()->registerService('FileHooks', function ($c) {
return new FileHooks(
$c->query('ServerContainer')->getRootFolder(),
\OC::$server->query(PhotofilesService::class),
@@ -52,136 +51,164 @@ public function __construct (array $urlParams=array()) {
$container->registerService(
'FavoritesController', function ($c) {
- return new FavoritesController(
- $c->query('AppName'),
- $c->query('Request'),
- $c->query('UserId'),
- $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
- $c->query('ServerContainer')->getConfig(),
- $c->getServer()->getShareManager(),
- $c->getServer()->getAppManager(),
- $c->getServer()->getUserManager(),
- $c->getServer()->getGroupManager(),
- $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ return new FavoritesController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
+ $c->query('ServerContainer')->getConfig(),
+ $c->getServer()->getShareManager(),
+ $c->getServer()->getAppManager(),
+ $c->getServer()->getUserManager(),
+ $c->getServer()->getGroupManager(),
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getLogger(),
+ new FavoritesService(
$c->query('ServerContainer')->getLogger(),
- new FavoritesService(
- $c->query('ServerContainer')->getLogger(),
- $c->query('ServerContainer')->getL10N($c->query('AppName'))
- ),
- $c->query('ServerContainer')->getDateTimeZone()
- );
- }
- );
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getSecureRandom()
+ ),
+ $c->query('ServerContainer')->getDateTimeZone()
+ );
+ });
$container->registerService(
'FavoritesApiController', function ($c) {
- return new FavoritesApiController(
- $c->query('AppName'),
- $c->query('Request'),
- $c->query('UserId'),
- $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
- $c->query('ServerContainer')->getConfig(),
- $c->getServer()->getShareManager(),
- $c->getServer()->getAppManager(),
- $c->getServer()->getUserManager(),
- $c->getServer()->getGroupManager(),
+ return new FavoritesApiController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
+ $c->query('ServerContainer')->getConfig(),
+ $c->getServer()->getShareManager(),
+ $c->getServer()->getAppManager(),
+ $c->getServer()->getUserManager(),
+ $c->getServer()->getGroupManager(),
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getLogger(),
+ new FavoritesService(
+ $c->query('ServerContainer')->getLogger(),
$c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getSecureRandom()
+ )
+ );
+ });
+
+ $container->registerService(
+ 'PublicFavoritesAPIController', function ($c) {
+ return new PublicFavoritesApiController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('Session'),
+ $c->query('ServerContainer')->getConfig(),
+ new FavoritesService(
$c->query('ServerContainer')->getLogger(),
- new FavoritesService(
- $c->query('ServerContainer')->getLogger(),
- $c->query('ServerContainer')->getL10N($c->query('AppName'))
- )
- );
- }
- );
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getSecureRandom()
+ )
+ );
+ });
$container->registerService(
- 'DevicesController', function ($c) {
- return new DevicesController(
- $c->query('AppName'),
- $c->query('Request'),
- $c->query('UserId'),
- $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
- $c->query('ServerContainer')->getConfig(),
- $c->getServer()->getShareManager(),
- $c->getServer()->getAppManager(),
- $c->getServer()->getUserManager(),
- $c->getServer()->getGroupManager(),
+ 'PublicPageController', function ($c) {
+ return new PublicPageController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('Session'),
+ $c->query('ServerContainer')->getConfig(),
+ $c->query('Logger'),
+ new FavoritesService(
+ $c->query('ServerContainer')->getLogger(),
$c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getSecureRandom()
+ )
+ );
+ });
+
+ $container->registerService(
+ 'DevicesController', function ($c) {
+ return new DevicesController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
+ $c->query('ServerContainer')->getConfig(),
+ $c->getServer()->getShareManager(),
+ $c->getServer()->getAppManager(),
+ $c->getServer()->getUserManager(),
+ $c->getServer()->getGroupManager(),
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getLogger(),
+ new DevicesService(
$c->query('ServerContainer')->getLogger(),
- new DevicesService(
- $c->query('ServerContainer')->getLogger(),
- $c->query('ServerContainer')->getL10N($c->query('AppName'))
- ),
- $c->query('ServerContainer')->getDateTimeZone()
- );
- }
- );
+ $c->query('ServerContainer')->getL10N($c->query('AppName'))
+ ),
+ $c->query('ServerContainer')->getDateTimeZone()
+ );
+ });
$container->registerService(
'DevicesApiController', function ($c) {
- return new DevicesApiController(
- $c->query('AppName'),
- $c->query('Request'),
- $c->query('UserId'),
- $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
- $c->query('ServerContainer')->getConfig(),
- $c->getServer()->getShareManager(),
- $c->getServer()->getAppManager(),
- $c->getServer()->getUserManager(),
- $c->getServer()->getGroupManager(),
- $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ return new DevicesApiController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
+ $c->query('ServerContainer')->getConfig(),
+ $c->getServer()->getShareManager(),
+ $c->getServer()->getAppManager(),
+ $c->getServer()->getUserManager(),
+ $c->getServer()->getGroupManager(),
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getLogger(),
+ new DevicesService(
$c->query('ServerContainer')->getLogger(),
- new DevicesService(
- $c->query('ServerContainer')->getLogger(),
- $c->query('ServerContainer')->getL10N($c->query('AppName'))
- )
- );
- }
- );
+ $c->query('ServerContainer')->getL10N($c->query('AppName'))
+ )
+ );
+ });
$container->registerService(
'RoutingController', function ($c) {
- return new RoutingController(
- $c->query('AppName'),
- $c->query('Request'),
- $c->query('UserId'),
- $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
- $c->query('ServerContainer')->getConfig(),
- $c->getServer()->getShareManager(),
- $c->getServer()->getAppManager(),
- $c->getServer()->getUserManager(),
- $c->getServer()->getGroupManager(),
- $c->query('ServerContainer')->getL10N($c->query('AppName')),
- $c->query('ServerContainer')->getLogger(),
- $c->query('ServerContainer')->getDateTimeZone()
- );
- }
- );
+ return new RoutingController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
+ $c->query('ServerContainer')->getConfig(),
+ $c->getServer()->getShareManager(),
+ $c->getServer()->getAppManager(),
+ $c->getServer()->getUserManager(),
+ $c->getServer()->getGroupManager(),
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getLogger(),
+ $c->query('ServerContainer')->getDateTimeZone()
+ );
+ });
$container->registerService(
'TracksController', function ($c) {
- return new TracksController(
- $c->query('AppName'),
- $c->query('Request'),
- $c->query('UserId'),
- $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
- $c->query('ServerContainer')->getConfig(),
- $c->getServer()->getShareManager(),
- $c->getServer()->getAppManager(),
- $c->getServer()->getUserManager(),
- $c->getServer()->getGroupManager(),
- $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ return new TracksController(
+ $c->query('AppName'),
+ $c->query('Request'),
+ $c->query('UserId'),
+ $c->query('ServerContainer')->getUserFolder($c->query('UserId')),
+ $c->query('ServerContainer')->getConfig(),
+ $c->getServer()->getShareManager(),
+ $c->getServer()->getAppManager(),
+ $c->getServer()->getUserManager(),
+ $c->getServer()->getGroupManager(),
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getLogger(),
+ new TracksService(
$c->query('ServerContainer')->getLogger(),
- new TracksService(
- $c->query('ServerContainer')->getLogger(),
- $c->query('ServerContainer')->getL10N($c->query('AppName')),
- $c->query('ServerContainer')->getRootFolder(),
- $c->getServer()->getShareManager()
- )
- );
- }
- );
+ $c->query('ServerContainer')->getL10N($c->query('AppName')),
+ $c->query('ServerContainer')->getRootFolder(),
+ $c->getServer()->getShareManager()
+ )
+ );
+ });
$container->registerService(
'UtilsController', function ($c) {
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 2186abd14..1e28dff28 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -12,6 +12,7 @@
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'page#do_echo', 'url' => '/echo', 'verb' => 'POST'],
['name' => 'page#openGeoLink', 'url' => '/openGeoLink/{url}', 'verb' => 'GET'],
+ ['name' => 'public_page#sharedFavoritesCategory', 'url' => '/s/favorites/{token}', 'verb' => 'GET'],
// utils
@@ -47,6 +48,18 @@
['name' => 'favorites_api#editFavorite', 'url' => '/api/{apiversion}/favorites/{id}', 'verb' => 'PUT'],
['name' => 'favorites_api#deleteFavorite', 'url' => '/api/{apiversion}/favorites/{id}', 'verb' => 'DELETE'],
+ // public favorites API
+ [
+ 'name' => 'favorites_api#preflighted_cors',
+ 'url' => '/api/1.0/public/favorites{path}',
+ 'verb' => 'OPTIONS',
+ 'requirements' => ['path' => '.+']
+ ],
+ ['name' => 'public_favorites_api#getFavorites', 'url' => '/api/1.0/public/{token}/favorites', 'verb' => 'GET'],
+ ['name' => 'public_favorites_api#addFavorite', 'url' => '/api/1.0/public/{token}/favorites', 'verb' => 'POST'],
+ ['name' => 'public_favorites_api#editFavorite', 'url' => '/api/1.0/public/{token}/favorites/{id}', 'verb' => 'PUT'],
+ ['name' => 'public_favorites_api#deleteFavorite', 'url' => '/api/1.0/public/{token}/favorites/{id}', 'verb' => 'DELETE'],
+
// favorites
['name' => 'favorites#getFavorites', 'url' => '/favorites', 'verb' => 'GET'],
['name' => 'favorites#addFavorite', 'url' => '/favorites', 'verb' => 'POST'],
@@ -54,6 +67,9 @@
['name' => 'favorites#deleteFavorite', 'url' => '/favorites/{id}', 'verb' => 'DELETE'],
['name' => 'favorites#deleteFavorites', 'url' => '/favorites', 'verb' => 'DELETE'],
['name' => 'favorites#renameCategories', 'url' => '/favorites-category', 'verb' => 'PUT'],
+ ['name' => 'favorites#getSharedCategories', 'url' => '/favorites-category/shared-categories', 'verb' => 'GET'],
+ ['name' => 'favorites#shareCategory', 'url' => '/favorites-category/{category}/share', 'verb' => 'POST'],
+ ['name' => 'favorites#unShareCategory', 'url' => '/favorites-category/{category}/un-share', 'verb' => 'POST'],
['name' => 'favorites#exportFavorites', 'url' => '/export/favorites', 'verb' => 'POST'],
['name' => 'favorites#importFavorites', 'url' => '/import/favorites', 'verb' => 'POST'],
@@ -73,7 +89,7 @@
['name' => 'devices_api#getDevices', 'url' => '/api/{apiversion}/devices', 'verb' => 'GET'],
['name' => 'devices_api#getDevicePoints', 'url' => '/api/{apiversion}/devices/{id}', 'verb' => 'GET'],
['name' => 'devices_api#addDevicePoint', 'url' => '/api/{apiversion}/devices', 'verb' => 'POST'],
- ['name' => 'devices_api#editDevice', 'url' => '/api/{apiversion}/devices/{id}', 'verb' => 'PUT'],
+ ['name' => 'devices_api#editDevice', 'url' => '/api/{apiversion}/devices/{id}', 'verbGET' => 'PUT'],
['name' => 'devices_api#deleteDevice', 'url' => '/api/{apiversion}/devices/{id}', 'verb' => 'DELETE'],
// devices
diff --git a/css/style.scss b/css/style.scss
index bf4576cd6..529dea664 100644
--- a/css/style.scss
+++ b/css/style.scss
@@ -808,3 +808,33 @@ tr.selected td {
.ui-autocomplete {
z-index: 9999;
}
+
+.category-line .category-sharing-dialogue {
+ width: 100%;
+ background: rgba(#000, .1);
+ padding: 0.5em;
+ display: none;
+
+ .category-sharing-checkbox-container {
+ display: flex;
+ align-items: center;
+
+ .category-sharing-checkbox {
+ margin-right: 1em;
+ cursor: pointer;
+ }
+ }
+
+ .category-sharing-link {
+ width: 100%;
+ visibility: hidden;
+
+ &.visible {
+ visibility: visible;
+ }
+ }
+}
+
+.category-line.sharingDialogueVisible .category-sharing-dialogue {
+ display: block;
+}
diff --git a/js/favoritesController.js b/js/favoritesController.js
index 41e3ec9f4..55f19f272 100644
--- a/js/favoritesController.js
+++ b/js/favoritesController.js
@@ -24,6 +24,7 @@ function FavoritesController(optionsController, timeFilterController) {
this.lastUsedCategory = null;
this.movingFavoriteId = null;
+ this.sharingFavoriteId = null;
// used by optionsController to know if favorite loading
// was done before or after option restoration
@@ -110,6 +111,59 @@ FavoritesController.prototype = {
}
that.enterAddFavoriteMode(cat);
});
+ // Open or close sharing dialogue for favorites category
+ $('body').on('click', '.categoryShareButton', function(e) {
+ var category = $(this).parent().parent().parent().attr('category');
+ var openCategory = that.sharingFavoriteId;
+
+ if (openCategory !== null) {
+ that.leaveSharingFavoriteMode();
+ }
+
+ if (openCategory !== category) {
+ that.enterSharingFavoriteMode(category);
+ }
+ });
+
+ $('body').on('change', '.category-sharing-checkbox', function(e) {
+ var category = $(this).parent().parent().parent().attr('category');
+
+ var shareDialogue = $(this).parent().parent();
+
+ if (!this.checked) {
+ $.ajax({
+ type: 'POST',
+ url: OC.generateUrl('/apps/maps/favorites-category/' + category + '/un-share'),
+ data: {},
+ async: true
+ }).done(function (response) {
+ const linkEl = shareDialogue.children('.category-sharing-link');
+
+ linkEl.val('');
+ linkEl.removeClass('visible');
+ }).always(function () {
+
+ }).fail(function() {
+ OC.Notification.showTemporary(t('maps', 'Failed to share favorites category'));
+ });
+ } else {
+ $.ajax({
+ type: 'POST',
+ url: OC.generateUrl('/apps/maps/favorites-category/' + category + '/share'),
+ data: {},
+ async: true
+ }).done(function (response) {
+ const linkEl = shareDialogue.children('.category-sharing-link');
+
+ linkEl.val(OC.generateUrl('/apps/maps/s/favorites/' + response.token));
+ linkEl.addClass('visible');
+ }).always(function () {
+
+ }).fail(function() {
+ OC.Notification.showTemporary(t('maps', 'Failed to share favorites category'));
+ });
+ }
+ });
// cancel favorite edition
$('body').on('click', '.canceleditfavorite', function(e) {
that.map.closePopup();
@@ -409,34 +463,56 @@ FavoritesController.prototype = {
getFavorites: function() {
var that = this;
$('#navigation-favorites').addClass('icon-loading-small');
- var req = {};
- var url = OC.generateUrl('/apps/maps/favorites');
- $.ajax({
+
+ var favorites = [];
+ var sharedCategories = [];
+
+ $.when(
+ $.ajax({
+ url: OC.generateUrl('/apps/maps/favorites'),
+ data: {},
type: 'GET',
- url: url,
- data: req,
- async: true
- }).done(function (response) {
- var fav, marker, cat, color;
- for (var i=0; i < response.length; i++) {
- fav = response[i];
- that.addFavoriteMap(fav);
+ async: true,
+ success: function(response) {
+ favorites = response;
+ },
+ fail: function(response) {
+ OC.Notification.showTemporary(t('maps', 'Failed to load favorites'));
+ }
+ }), $.ajax({
+ url: OC.generateUrl('/apps/maps/favorites-category/shared-categories'),
+ data: {},
+ type: 'GET',
+ async: true,
+ success: function(response) {
+ for (var i = 0; i < response.length; i++) {
+ sharedCategories[response[i].category] = response[i].token;
+ }
+ },
+ fail: function(response) {
+ OC.Notification.showTemporary(t('maps', 'Failed to load favorite share token'));
}
+ })
+ ).then(function() {
+ for (var i=0; i < favorites.length; i++) {
+ var token = sharedCategories[favorites[i].category] || null;
+
+ that.addFavoriteMap(favorites[i], true, false, token);
+ }
+
that.updateCategoryCounters();
that.favoritesLoaded = true;
that.updateTimeFilterRange();
that.timeFilterController.setSliderToMaxInterval();
- }).always(function (response) {
+
$('#navigation-favorites').removeClass('icon-loading-small');
- }).fail(function() {
- OC.Notification.showTemporary(t('maps', 'Failed to load favorites'));
});
},
// add category in side menu
// add layer
// set color and icon
- addCategory: function(rawName, enable=false) {
+ addCategory: function(rawName, enable=false, shareToken = null) {
var name = rawName.replace(' ', '-');
// color
@@ -472,61 +548,71 @@ FavoritesController.prototype = {
// side menu entry
var imgurl = OC.generateUrl('/svg/core/actions/star?color='+color);
var li = '
' +
- ' '+rawName+'' +
- ' ' +
- ' ' +
- ' ' +
- '
'+t('maps', 'Category deleted')+'
' +
- '
' +
- '
' +
- ' ' +
- '';
+ ' '+rawName+'' +
+ ' ' +
+ '
' +
+ ' - 1
' +
+ ' ' +
+ ' ' +
+ '
' +
+ '
' +
+ ' ' +
+ ' ' +
+ '
'+t('maps', 'Category deleted')+'
' +
+ '
' +
+ '
' +
+ ' ' +
+ ' ' +
+ ' ' +
+ ' ' +
+ '
' +
+ '';
var beforeThis = null;
var rawLower = rawName.toLowerCase();
@@ -670,6 +756,16 @@ FavoritesController.prototype = {
this.addFavoriteCategory = null;
},
+ enterSharingFavoriteMode: function(categoryName) {
+ $('#' + categoryName.replace(' ', '-') + '-category').addClass('sharingDialogueVisible');
+ this.sharingFavoriteId = categoryName;
+ },
+
+ leaveSharingFavoriteMode: function() {
+ $('#' + this.sharingFavoriteId.replace(' ', '-') + '-category').removeClass('sharingDialogueVisible');
+ this.sharingFavoriteId = null;
+ },
+
addFavoriteClickMap: function(e) {
var categoryName = this.favoritesController.addFavoriteCategory;
if (categoryName === this.favoritesController.defaultCategory && this.favoritesController.lastUsedCategory !== null) {
@@ -721,11 +817,11 @@ FavoritesController.prototype = {
},
// add a marker to the corresponding layer
- addFavoriteMap: function(fav, enableCategory=false, fromUserAction=false) {
+ addFavoriteMap: function(fav, enableCategory=false, fromUserAction=false, shareToken = null) {
// manage category first
cat = fav.category;
if (!this.categoryLayers.hasOwnProperty(cat)) {
- this.addCategory(cat, enableCategory);
+ this.addCategory(cat, enableCategory, shareToken);
if (enableCategory) {
this.saveEnabledCategories();
}
diff --git a/js/utils.js b/js/utils.js
index 8f214a945..ebe2bf9cc 100644
--- a/js/utils.js
+++ b/js/utils.js
@@ -1,323 +1,340 @@
function basename(str) {
- var base = new String(str).substring(str.lastIndexOf('/') + 1);
- return base;
+ var base = new String(str).substring(str.lastIndexOf("/") + 1);
+ return base;
}
function dirname(path) {
- return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, '');;
+ return path.replace(/\\/g, "/").replace(/\/[^\/]*$/, "");
}
function Timer(callback, mydelay) {
- var timerId, start, remaining = mydelay;
+ var timerId,
+ start,
+ remaining = mydelay;
- this.pause = function() {
- window.clearTimeout(timerId);
- remaining -= new Date() - start;
- };
+ this.pause = function() {
+ window.clearTimeout(timerId);
+ remaining -= new Date() - start;
+ };
- this.resume = function() {
- start = new Date();
- window.clearTimeout(timerId);
- timerId = window.setTimeout(callback, remaining);
- };
+ this.resume = function() {
+ start = new Date();
+ window.clearTimeout(timerId);
+ timerId = window.setTimeout(callback, remaining);
+ };
- this.resume();
+ this.resume();
}
function getLetterColor(letter1, letter2) {
- var letter1Index = letter1.toLowerCase().charCodeAt(0);
- var letter2Index = letter2.toLowerCase().charCodeAt(0);
- var letterCoef = (letter1Index * letter2Index) % 100 / 100;
- var h = letterCoef * 360;
- var s = 75 + letterCoef * 10;
- var l = 50 + letterCoef * 10;
- return {h: Math.round(h), s: Math.round(s), l: Math.round(l)};
+ var letter1Index = letter1.toLowerCase().charCodeAt(0);
+ var letter2Index = letter2.toLowerCase().charCodeAt(0);
+ var letterCoef = ((letter1Index * letter2Index) % 100) / 100;
+ var h = letterCoef * 360;
+ var s = 75 + letterCoef * 10;
+ var l = 50 + letterCoef * 10;
+ return { h: Math.round(h), s: Math.round(s), l: Math.round(l) };
}
function hslToRgb(h, s, l) {
- var r, g, b;
+ var r, g, b;
- if(s == 0){
- r = g = b = l; // achromatic
- }else{
- var hue2rgb = function hue2rgb(p, q, t){
- if(t < 0) t += 1;
- if(t > 1) t -= 1;
- if(t < 1/6) return p + (q - p) * 6 * t;
- if(t < 1/2) return q;
- if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
- return p;
- }
+ if (s == 0) {
+ r = g = b = l; // achromatic
+ } else {
+ var hue2rgb = function hue2rgb(p, q, t) {
+ if (t < 0) t += 1;
+ if (t > 1) t -= 1;
+ if (t < 1 / 6) return p + (q - p) * 6 * t;
+ if (t < 1 / 2) return q;
+ if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
+ return p;
+ };
- var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- var p = 2 * l - q;
- r = hue2rgb(p, q, h + 1/3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1/3);
- }
- var rgb = {r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255)};
- var hexStringR = rgb.r.toString(16);
- if (hexStringR.length % 2) {
- hexStringR = '0' + hexStringR;
- }
- var hexStringG = rgb.g.toString(16);
- if (hexStringG.length % 2) {
- hexStringG = '0' + hexStringG;
- }
- var hexStringB = rgb.b.toString(16);
- if (hexStringB.length % 2) {
- hexStringB = '0' + hexStringB;
- }
- return hexStringR+hexStringG+hexStringB;
+ var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
+ var p = 2 * l - q;
+ r = hue2rgb(p, q, h + 1 / 3);
+ g = hue2rgb(p, q, h);
+ b = hue2rgb(p, q, h - 1 / 3);
+ }
+ var rgb = {
+ r: Math.round(r * 255),
+ g: Math.round(g * 255),
+ b: Math.round(b * 255)
+ };
+ var hexStringR = rgb.r.toString(16);
+ if (hexStringR.length % 2) {
+ hexStringR = "0" + hexStringR;
+ }
+ var hexStringG = rgb.g.toString(16);
+ if (hexStringG.length % 2) {
+ hexStringG = "0" + hexStringG;
+ }
+ var hexStringB = rgb.b.toString(16);
+ if (hexStringB.length % 2) {
+ hexStringB = "0" + hexStringB;
+ }
+ return hexStringR + hexStringG + hexStringB;
}
function hexToRgb(hex) {
- var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
- return result ? {
+ var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
+ return result
+ ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
- } : null;
+ }
+ : null;
}
function pad(num, size) {
- var s = num + '';
- while (s.length < size) s = '0' + s;
- return s;
+ var s = num + "";
+ while (s.length < size) s = "0" + s;
+ return s;
}
Date.prototype.toIsoString = function() {
- var tzo = -this.getTimezoneOffset(),
- dif = tzo >= 0 ? '+' : '-',
- pad = function(num) {
- var norm = Math.floor(Math.abs(num));
- return (norm < 10 ? '0' : '') + norm;
- };
- return this.getFullYear() +
- '-' + pad(this.getMonth() + 1) +
- '-' + pad(this.getDate()) +
- ' ' + pad(this.getHours()) +
- ':' + pad(this.getMinutes()) +
- ':' + pad(this.getSeconds()) +
- ' GMT'+dif + pad(tzo / 60) +
- ':' + pad(tzo % 60);
-}
+ var tzo = -this.getTimezoneOffset(),
+ dif = tzo >= 0 ? "+" : "-",
+ pad = function(num) {
+ var norm = Math.floor(Math.abs(num));
+ return (norm < 10 ? "0" : "") + norm;
+ };
+ return (
+ this.getFullYear() +
+ "-" +
+ pad(this.getMonth() + 1) +
+ "-" +
+ pad(this.getDate()) +
+ " " +
+ pad(this.getHours()) +
+ ":" +
+ pad(this.getMinutes()) +
+ ":" +
+ pad(this.getSeconds()) +
+ " GMT" +
+ dif +
+ pad(tzo / 60) +
+ ":" +
+ pad(tzo % 60)
+ );
+};
function brify(str, linesize) {
- var res = '';
- var words = str.split(' ');
- var cpt = 0;
- var toAdd = '';
- for (var i=0; i';
- toAdd = words[i] + ' ';
- cpt = words[i].length + 1;
- }
+ var res = "";
+ var words = str.split(" ");
+ var cpt = 0;
+ var toAdd = "";
+ for (var i = 0; i < words.length; i++) {
+ if (cpt + words[i].length < linesize) {
+ toAdd += words[i] + " ";
+ cpt += words[i].length + 1;
+ } else {
+ res += toAdd + "
";
+ toAdd = words[i] + " ";
+ cpt = words[i].length + 1;
}
- res += toAdd;
- return res;
+ }
+ res += toAdd;
+ return res;
}
function metersToDistance(m) {
- var unit = 'metric';
- var n = parseFloat(m);
- if (unit === 'metric') {
- if (n > 1000) {
- return (n / 1000).toFixed(2) + ' km';
- }
- else{
- return n.toFixed(2) + ' m';
- }
+ var unit = "metric";
+ var n = parseFloat(m);
+ if (unit === "metric") {
+ if (n > 1000) {
+ return (n / 1000).toFixed(2) + " km";
+ } else {
+ return n.toFixed(2) + " m";
}
- else if (unit === 'english') {
- var mi = n * METERSTOMILES;
- if (mi < 1) {
- return (n * METERSTOFOOT).toFixed(2) + ' ft';
- }
- else {
- return mi.toFixed(2) + ' mi';
- }
- }
- else if (unit === 'nautical') {
- var nmi = n * METERSTONAUTICALMILES;
- return nmi.toFixed(2) + ' nmi';
+ } else if (unit === "english") {
+ var mi = n * METERSTOMILES;
+ if (mi < 1) {
+ return (n * METERSTOFOOT).toFixed(2) + " ft";
+ } else {
+ return mi.toFixed(2) + " mi";
}
+ } else if (unit === "nautical") {
+ var nmi = n * METERSTONAUTICALMILES;
+ return nmi.toFixed(2) + " nmi";
+ }
}
function metersToElevation(m) {
- var unit = 'metric';
- var n = parseFloat(m);
- if (unit === 'metric' || unit === 'nautical') {
- return n.toFixed(2) + ' m';
- }
- else {
- return (n * METERSTOFOOT).toFixed(2) + ' ft';
- }
+ var unit = "metric";
+ var n = parseFloat(m);
+ if (unit === "metric" || unit === "nautical") {
+ return n.toFixed(2) + " m";
+ } else {
+ return (n * METERSTOFOOT).toFixed(2) + " ft";
+ }
}
function kmphToSpeed(kmph) {
- var unit = 'metric';
- var nkmph = parseFloat(kmph);
- if (unit === 'metric') {
- return nkmph.toFixed(2) + ' km/h';
- }
- else if (unit === 'english') {
- return (nkmph * 1000 * METERSTOMILES).toFixed(2) + ' mi/h';
- }
- else if (unit === 'nautical') {
- return (nkmph * 1000 * METERSTONAUTICALMILES).toFixed(2) + ' kt';
- }
+ var unit = "metric";
+ var nkmph = parseFloat(kmph);
+ if (unit === "metric") {
+ return nkmph.toFixed(2) + " km/h";
+ } else if (unit === "english") {
+ return (nkmph * 1000 * METERSTOMILES).toFixed(2) + " mi/h";
+ } else if (unit === "nautical") {
+ return (nkmph * 1000 * METERSTONAUTICALMILES).toFixed(2) + " kt";
+ }
}
function minPerKmToPace(minPerKm) {
- var unit = 'metric';
- var nMinPerKm = parseFloat(minPerKm);
- if (unit === 'metric') {
- return nMinPerKm.toFixed(2) + ' min/km';
- }
- else if (unit === 'english') {
- return (nMinPerKm / 1000 / METERSTOMILES).toFixed(2) + ' min/mi';
- }
- else if (unit === 'nautical') {
- return (nMinPerKm / 1000 / METERSTONAUTICALMILES).toFixed(2) + ' min/nmi';
- }
+ var unit = "metric";
+ var nMinPerKm = parseFloat(minPerKm);
+ if (unit === "metric") {
+ return nMinPerKm.toFixed(2) + " min/km";
+ } else if (unit === "english") {
+ return (nMinPerKm / 1000 / METERSTOMILES).toFixed(2) + " min/mi";
+ } else if (unit === "nautical") {
+ return (nMinPerKm / 1000 / METERSTONAUTICALMILES).toFixed(2) + " min/nmi";
+ }
}
-function formatTimeSeconds(time_s){
- var minutes = Math.floor(time_s / 60);
- var hours = Math.floor(minutes / 60);
+function formatTimeSeconds(time_s) {
+ var minutes = Math.floor(time_s / 60);
+ var hours = Math.floor(minutes / 60);
- var ph = pad(hours, 2);
- var pm = pad(minutes % 60, 2);
- var ps = pad(time_s % 60, 2);
- return `${ph}:${pm}:${ps}`;
+ var ph = pad(hours, 2);
+ var pm = pad(minutes % 60, 2);
+ var ps = pad(time_s % 60, 2);
+ return `${ph}:${pm}:${ps}`;
}
function isComputer(name) {
- return ( name.match(/windows/i)
- || name.match(/gnu\/linux/i)
- || name.match(/mac\s?os/i)
- || name.match(/chromium\s?os/i)
- || name.match(/ubuntu/i)
- );
+ return (
+ name.match(/windows/i) ||
+ name.match(/gnu\/linux/i) ||
+ name.match(/mac\s?os/i) ||
+ name.match(/chromium\s?os/i) ||
+ name.match(/ubuntu/i)
+ );
}
function isPhone(name) {
- return (name.match(/blackberry/i)
- || name.match(/symbian/i)
- || name.match(/phonetrack/i)
- || name.match(/firefox\s?os/i)
- || name.match(/android/i)
- || name.match(/ios/i)
- || name.match(/windows\s?mobile/i)
- );
+ return (
+ name.match(/blackberry/i) ||
+ name.match(/symbian/i) ||
+ name.match(/phonetrack/i) ||
+ name.match(/firefox\s?os/i) ||
+ name.match(/android/i) ||
+ name.match(/ios/i) ||
+ name.match(/windows\s?mobile/i)
+ );
}
function getDeviceInfoFromUserAgent2(ua) {
- var res = {
- os: null,
- client: null,
- };
- var parser = new UAParser(ua);
- var uap = parser.getResult();
- if (uap.os && uap.os.name) {
- res.os = uap.os.name.replace('Linux', 'GNU/Linux').replace('windows', 'Windows');
- }
- if (uap.browser && uap.browser.name) {
- res.client = uap.browser.name.replace('chrome', 'Chrome');
- }
- return res;
+ var res = {
+ os: null,
+ client: null
+ };
+ var parser = new UAParser(ua);
+ var uap = parser.getResult();
+ if (uap.os && uap.os.name) {
+ res.os = uap.os.name
+ .replace("Linux", "GNU/Linux")
+ .replace("windows", "Windows");
+ }
+ if (uap.browser && uap.browser.name) {
+ res.client = uap.browser.name.replace("chrome", "Chrome");
+ }
+ return res;
}
function getDeviceInfoFromUserAgent(ua) {
- var res = {
- os: null,
- client: null,
- clientVersion: null
- };
- var m;
- // OS
- if (ua.match(/x11/i) || ua.match(/linux/i)) {
- res.os = 'GNU/Linux';
- }
- else if (ua.match(/android/i)) {
- res.os = 'Android';
- }
- else if (ua.match(/windows/i)) {
- res.os = 'Windows';
- }
- else if (ua.match(/iphone/i)) {
- res.os = 'IOS';
- }
- else if (ua.match(/macintosh/i) || ua.match(/darwin/i)) {
- res.os = 'MacOS';
- }
- // BROWSER
- if (ua.match(/firefox\//i) && !ua.match(/seamonkey\//i)) {
- res.client = 'Firefox';
- m = ua.match(/firefox\/([0-9.]*)/i);
- if (m.length > 1) {
- res.clientVersion = m[1];
- }
+ var res = {
+ os: null,
+ client: null,
+ clientVersion: null
+ };
+ var m;
+ // OS
+ if (ua.match(/x11/i) || ua.match(/linux/i)) {
+ res.os = "GNU/Linux";
+ } else if (ua.match(/android/i)) {
+ res.os = "Android";
+ } else if (ua.match(/windows/i)) {
+ res.os = "Windows";
+ } else if (ua.match(/iphone/i)) {
+ res.os = "IOS";
+ } else if (ua.match(/macintosh/i) || ua.match(/darwin/i)) {
+ res.os = "MacOS";
+ }
+ // BROWSER
+ if (ua.match(/firefox\//i) && !ua.match(/seamonkey\//i)) {
+ res.client = "Firefox";
+ m = ua.match(/firefox\/([0-9.]*)/i);
+ if (m.length > 1) {
+ res.clientVersion = m[1];
}
- else if (ua.match(/safari\//i) && !ua.match(/chrome\//i) && !ua.match(/chromium\//i)) {
- res.client = 'Safari';
- m = ua.match(/safari\/([0-9.]*)/i);
- if (m.length > 1) {
- res.clientVersion = m[1];
- }
+ } else if (
+ ua.match(/safari\//i) &&
+ !ua.match(/chrome\//i) &&
+ !ua.match(/chromium\//i)
+ ) {
+ res.client = "Safari";
+ m = ua.match(/safari\/([0-9.]*)/i);
+ if (m.length > 1) {
+ res.clientVersion = m[1];
}
- else if (ua.match(/chrome\//i) && !ua.match(/chromium\//i)) {
- res.client = 'Chrome';
- m = ua.match(/chrome\/([0-9.]*)/i);
- if (m.length > 1) {
- res.clientVersion = m[1];
- }
+ } else if (ua.match(/chrome\//i) && !ua.match(/chromium\//i)) {
+ res.client = "Chrome";
+ m = ua.match(/chrome\/([0-9.]*)/i);
+ if (m.length > 1) {
+ res.clientVersion = m[1];
}
- else if (ua.match(/chromium\//i)) {
- res.client = 'Chromium';
- m = ua.match(/chromium\/([0-9.]*)/i);
- if (m.length > 1) {
- res.clientVersion = m[1];
- }
+ } else if (ua.match(/chromium\//i)) {
+ res.client = "Chromium";
+ m = ua.match(/chromium\/([0-9.]*)/i);
+ if (m.length > 1) {
+ res.clientVersion = m[1];
}
- else if (ua.match(/opr\//i)) {
- res.client = 'Opera';
- m = ua.match(/opr\/([0-9.]*)/i);
- if (m.length > 1) {
- res.clientVersion = m[1];
- }
+ } else if (ua.match(/opr\//i)) {
+ res.client = "Opera";
+ m = ua.match(/opr\/([0-9.]*)/i);
+ if (m.length > 1) {
+ res.clientVersion = m[1];
}
- return res;
+ }
+ return res;
}
function getUrlParameter(sParam) {
- var sPageURL = window.location.search.substring(1);
- var sURLVariables = sPageURL.split('&');
- for (var i = 0; i < sURLVariables.length; i++) {
- var sParameterName = sURLVariables[i].split('=');
- if (sParameterName[0] === sParam) {
- return decodeURIComponent(sParameterName[1]);
- }
+ var sPageURL = window.location.search.substring(1);
+ var sURLVariables = sPageURL.split("&");
+ for (var i = 0; i < sURLVariables.length; i++) {
+ var sParameterName = sURLVariables[i].split("=");
+ if (sParameterName[0] === sParam) {
+ return decodeURIComponent(sParameterName[1]);
}
+ }
}
function formatAddress(address) {
- var strAddress =
- (address.attraction || '')+' '+
- (address.house_number || '')+' '+
- (address.road || '')+' '+
- (address.pedestrian || '')+' '+
- (address.suburb || '')+' '+
- (address.city_district || '')+' '+
- (address.postcode || '')+' '+
- (address.village || address.town || address.city || '')+' '+
- (address.state || '')+' '+
- (address.country || '');
- return strAddress.replace(/\s+/g, ' ').trim();
+ var strAddress =
+ (address.attraction || "") +
+ " " +
+ (address.house_number || "") +
+ " " +
+ (address.road || "") +
+ " " +
+ (address.pedestrian || "") +
+ " " +
+ (address.suburb || "") +
+ " " +
+ (address.city_district || "") +
+ " " +
+ (address.postcode || "") +
+ " " +
+ (address.village || address.town || address.city || "") +
+ " " +
+ (address.state || "") +
+ " " +
+ (address.country || "");
+ return strAddress.replace(/\s+/g, " ").trim();
}
diff --git a/lib/Controller/FavoritesController.php b/lib/Controller/FavoritesController.php
index 287f67a43..4e33bd2de 100644
--- a/lib/Controller/FavoritesController.php
+++ b/lib/Controller/FavoritesController.php
@@ -33,6 +33,7 @@
use OCP\IDateTimeZone;
use OCA\Maps\Service\FavoritesService;
+use Punic\Data;
//use function OCA\Maps\Service\endswith;
@@ -89,6 +90,15 @@ public function getFavorites() {
return new DataResponse($favorites);
}
+ /**
+ * @NoAdminRequired
+ */
+ public function getSharedCategories() {
+ $categories = $this->favoritesService->getSharedCategories($this->userId);
+
+ return new DataResponse($categories);
+ }
+
/**
* @NoAdminRequired
*/
@@ -125,6 +135,28 @@ public function editFavorite($id, $name, $lat, $lng, $category, $comment, $exten
}
}
+ public function shareCategory($category) {
+ // TODO: use better way to check if user owns category
+ if ($this->favoritesService->countFavorites($this->userId, [$category], null, null) === 0) {
+ return new DataResponse("Category does not exist", 400);
+ }
+
+ $response = $this->favoritesService->getCategoryShareLink($this->userId, $category);
+
+ return new DataResponse($response);
+ }
+
+ public function unShareCategory($category) {
+ // TODO: use better way to check if user owns category
+ if ($this->favoritesService->countFavorites($this->userId, [$category], null, null) === 0) {
+ return new DataResponse("Category does not exist", 400);
+ }
+
+ $response = $this->favoritesService->deleteCategoryShareLink($this->userId, $category);
+
+ return new DataResponse($response);
+ }
+
/**
* @NoAdminRequired
*/
diff --git a/lib/Controller/PageController.php b/lib/Controller/PageController.php
index 8e5c02657..888946c01 100644
--- a/lib/Controller/PageController.php
+++ b/lib/Controller/PageController.php
@@ -14,6 +14,7 @@
use OCP\IConfig;
use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse;
+use OCP\AppFramework\Http\Template\PublicTemplateResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Controller;
use OCP\IInitialStateService;
@@ -47,6 +48,44 @@ public function index() {
$params = array('user' => $this->userId);
$this->initialStateService->provideInitialState($this->appName, 'photos', $this->config->getAppValue('photos', 'enabled', 'no') === 'yes');
$response = new TemplateResponse('maps', 'index', $params);
+
+ $this->addCsp($response);
+
+ return $response;
+ }
+
+ /**
+ * @NoAdminRequired
+ * @NoCSRFRequired
+ */
+ public function openGeoLink($url) {
+ $params = array('user' => $this->userId);
+ $params["geourl"] = $url;
+ $response = new TemplateResponse('maps', 'index', $params);
+ if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) {
+ $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
+ // map tiles
+ $csp->addAllowedImageDomain('https://*.tile.openstreetmap.org');
+ $csp->addAllowedImageDomain('https://server.arcgisonline.com');
+ $csp->addAllowedImageDomain('https://*.cartocdn.com');
+ $csp->addAllowedImageDomain('https://*.opentopomap.org');
+ $csp->addAllowedImageDomain('https://*.cartocdn.com');
+ $csp->addAllowedImageDomain('https://*.ssl.fastly.net');
+ $csp->addAllowedImageDomain('https://*.openstreetmap.se');
+ // routing engine
+ $csp->addAllowedConnectDomain('https://*.project-osrm.org');
+ // TODO allow connections to router engine
+ //$csp->addAllowedConnectDomain('http://192.168.0.66:8989');
+ // poi images
+ $csp->addAllowedImageDomain('https://nominatim.openstreetmap.org');
+ // search and geocoder
+ $csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org');
+ $response->setContentSecurityPolicy($csp);
+ }
+ return $response;
+ }
+
+ private function addCsp($response) {
if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) {
$csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
// map tiles
@@ -91,38 +130,5 @@ public function index() {
$csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org');
$response->setContentSecurityPolicy($csp);
}
- return $response;
- }
-
- /**
- * @NoAdminRequired
- * @NoCSRFRequired
- */
- public function openGeoLink($url) {
- $params = array('user' => $this->userId);
- $params["geourl"] = $url;
- $response = new TemplateResponse('maps', 'index', $params);
- if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) {
- $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
- // map tiles
- $csp->addAllowedImageDomain('https://*.tile.openstreetmap.org');
- $csp->addAllowedImageDomain('https://server.arcgisonline.com');
- $csp->addAllowedImageDomain('https://*.cartocdn.com');
- $csp->addAllowedImageDomain('https://*.opentopomap.org');
- $csp->addAllowedImageDomain('https://*.cartocdn.com');
- $csp->addAllowedImageDomain('https://*.ssl.fastly.net');
- $csp->addAllowedImageDomain('https://*.openstreetmap.se');
- // routing engine
- $csp->addAllowedConnectDomain('https://*.project-osrm.org');
- // TODO allow connections to router engine
- //$csp->addAllowedConnectDomain('http://192.168.0.66:8989');
- // poi images
- $csp->addAllowedImageDomain('https://nominatim.openstreetmap.org');
- // search and geocoder
- $csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org');
- $response->setContentSecurityPolicy($csp);
- }
- return $response;
}
-
}
diff --git a/lib/Controller/PublicFavoritesApiController.php b/lib/Controller/PublicFavoritesApiController.php
new file mode 100644
index 000000000..3eebf7c6d
--- /dev/null
+++ b/lib/Controller/PublicFavoritesApiController.php
@@ -0,0 +1,164 @@
+config = $config;
+ $this->favoritesService = $favoritesService;
+ $this->userId = $userId;
+
+ $this->qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
+ }
+
+ public function getPasswordHash(): string {
+ return ""; // TODO:
+ }
+
+ protected function isPasswordProtected(): bool {
+ return false; // TODO
+ }
+
+ public function isValidToken(): bool {
+ return $this->favoritesService->getFavoritesShare($this->getToken()) !== null;
+ }
+
+ /**
+ * @param $token
+ * @return DataResponse
+ *
+ * @PublicPage
+ * @Cors
+ */
+ public function getFavorites($token) {
+ if ($token === '') {
+ return new DataResponse('Invalid token', Http::STATUS_BAD_REQUEST);
+ }
+
+ $favorites = $this->favoritesService->getFavoritesByShareToken($token);
+
+ if ($favorites === false) {
+ return new DataResponse('Not found', Http::STATUS_NOT_FOUND);
+ }
+
+ return new DataResponse($favorites);
+ }
+
+ public function addFavorite($lat, $lng, $name, $comment, $extensions) {
+
+ $share = $this->favoritesService->getFavoritesShare($this->getToken());
+ $category = $share['category'];
+
+
+ if (is_numeric($lat) && is_numeric($lng)) {
+ $favoriteId = $this->favoritesService->addFavoriteToDB($this->userId, $name, $lat, $lng, $category, $comment, $extensions);
+ $favorite = $this->favoritesService->getFavoriteFromDB($favoriteId);
+ return new DataResponse($favorite);
+ }
+ else {
+ return new DataResponse('invalid values', 400);
+ }
+ }
+
+ public function editFavorite($id, $lat, $lng, $name, $comment, $extensions) {
+ $share = $this->favoritesService->getFavoritesShare($this->getToken());
+
+ //TODO: can $share['owner'] and/or $share['category'] be exploited to be null?
+
+ $favorite = $this->favoritesService->getFavoriteFromDB($id, $share['owner'], $share['category']);
+
+ if ($favorite !== null) {
+ if (($lat === null || is_numeric($lat)) &&
+ ($lng === null || is_numeric($lng))
+ ) {
+ $this->favoritesService->editFavoriteInDB($id, $name, $lat, $lng, $favorite['category'], $comment, $extensions);
+ $editedFavorite = $this->favoritesService->getFavoriteFromDB($id);
+
+ return new DataResponse($editedFavorite);
+ }
+ else {
+ return new DataResponse('invalid values', 400);
+ }
+ }
+ else {
+ return new DataResponse('no such favorite', 400);
+ }
+ }
+
+ public function deleteFavorite($id) {
+ $share = $this->favoritesService->getFavoritesShare($this->getToken());
+
+ //TODO: can $share['owner'] and/or $share['category'] be exploited to be null?
+
+ $favorite = $this->favoritesService->getFavoriteFromDB($id, $share['owner'], $share['category']);
+
+ if ($favorite !== null) {
+ $this->favoritesService->deleteFavoriteFromDB($id);
+ return new DataResponse('deleted');
+ }
+ else {
+ return new DataResponse('no such favorite', 400);
+ }
+ }
+
+ /*private function addCsp($response) {
+ if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) {
+ $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
+ // map tiles
+ $csp->addAllowedImageDomain('https://*.tile.openstreetmap.org');
+ $csp->addAllowedImageDomain('https://server.arcgisonline.com');
+ $csp->addAllowedImageDomain('https://*.cartocdn.com');
+ $csp->addAllowedImageDomain('https://*.opentopomap.org');
+ $csp->addAllowedImageDomain('https://*.cartocdn.com');
+ $csp->addAllowedImageDomain('https://*.ssl.fastly.net');
+ $csp->addAllowedImageDomain('https://*.openstreetmap.se');
+
+ // default routing engine
+ $csp->addAllowedConnectDomain('https://*.project-osrm.org');
+ $csp->addAllowedConnectDomain('https://api.mapbox.com');
+ $csp->addAllowedConnectDomain('https://graphhopper.com');
+ // allow connections to custom routing engines
+ $urlKeys = [
+ 'osrmBikeURL',
+ 'osrmCarURL',
+ 'osrmFootURL',
+ 'graphhopperURL'
+ ];
+ foreach ($urlKeys as $key) {
+ $url = $this->config->getAppValue('maps', $key);
+ if ($url !== '') {
+ $scheme = parse_url($url, PHP_URL_SCHEME);
+ $host = parse_url($url, PHP_URL_HOST);
+ $port = parse_url($url, PHP_URL_PORT);
+ $cleanUrl = $scheme . '://' . $host;
+ if ($port && $port !== '') {
+ $cleanUrl .= ':' . $port;
+ }
+ $csp->addAllowedConnectDomain($cleanUrl);
+ }
+ }
+ //$csp->addAllowedConnectDomain('http://192.168.0.66:5000');
+
+ // poi images
+ $csp->addAllowedImageDomain('https://nominatim.openstreetmap.org');
+ // search and geocoder
+ $csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org');
+ $response->setContentSecurityPolicy($csp);
+ }
+ }*/
+}
\ No newline at end of file
diff --git a/lib/Controller/PublicPageController.php b/lib/Controller/PublicPageController.php
new file mode 100644
index 000000000..1c7687684
--- /dev/null
+++ b/lib/Controller/PublicPageController.php
@@ -0,0 +1,148 @@
+
+ * @copyright Vinzenz Rosenkranz 2017
+ */
+
+namespace OCA\Maps\Controller;
+
+use OC\User\Manager;
+use OCA\Maps\Service\FavoritesService;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IConfig;
+use OCP\ILogger;
+use OCP\IRequest;
+use OCP\ISession;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\Template\PublicTemplateResponse;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\PublicShareController;
+
+class PublicPageController extends PublicShareController
+{
+ private $config;
+ private $logger;
+ private $favoritesService;
+
+ public function __construct($appName, IRequest $request, ISession $session, IConfig $config, ILogger $logger, FavoritesService $favoritesService)
+ {
+ parent::__construct($appName, $request, $session);
+ $this->config = $config;
+ $this->logger = $logger;
+ $this->favoritesService = $favoritesService;
+ }
+
+ /**
+ * @param $token
+ *
+ * @return DataResponse|PublicTemplateResponse
+ *
+ * @PublicPage
+ * @NoCSRFRequired
+ */
+ public function sharedFavoritesCategory($token) {
+ if ($token === '') {
+ return new DataResponse([], Http::STATUS_BAD_REQUEST);
+ }
+
+ $share = $this->favoritesService->getFavoritesShare($token);
+
+
+ $response = new PublicTemplateResponse('maps', 'public/favorites_index', []);
+
+ if ($share !== false) {
+ $ownerName = \OC::$server->getUserManager()->get($share['owner'])->getDisplayName();
+
+ $response->setHeaderTitle($share['category']);
+ $response->setHeaderDetails('shared by ' . $ownerName);
+ }
+
+ $this->addCsp($response);
+
+ return $response;
+ }
+
+ /**
+ * Get a hash of the password for this share
+ *
+ * To ensure access is blocked when the password to a share is changed we store
+ * a hash of the password for this token.
+ *
+ * @since 14.0.0
+ */
+ protected function getPasswordHash(): string {
+ return ""; // TODO:
+ }
+
+ /**
+ * Is the provided token a valid token
+ *
+ * This function is already called from the middleware directly after setting the token.
+ *
+ * @since 14.0.0
+ */
+ public function isValidToken(): bool {
+ return $this->favoritesService->getFavoritesShare($this->getToken()) !== null;
+ }
+
+ /**
+ * Is a share with this token password protected
+ *
+ * @since 14.0.0
+ */
+ protected function isPasswordProtected(): bool {
+ return false; // TODO
+ }
+
+ private function addCsp($response)
+ {
+ if (class_exists('OCP\AppFramework\Http\ContentSecurityPolicy')) {
+ $csp = new \OCP\AppFramework\Http\ContentSecurityPolicy();
+ // map tiles
+ $csp->addAllowedImageDomain('https://*.tile.openstreetmap.org');
+ $csp->addAllowedImageDomain('https://server.arcgisonline.com');
+ $csp->addAllowedImageDomain('https://*.cartocdn.com');
+ $csp->addAllowedImageDomain('https://*.opentopomap.org');
+ $csp->addAllowedImageDomain('https://*.cartocdn.com');
+ $csp->addAllowedImageDomain('https://*.ssl.fastly.net');
+ $csp->addAllowedImageDomain('https://*.openstreetmap.se');
+
+ // default routing engine
+ $csp->addAllowedConnectDomain('https://*.project-osrm.org');
+ $csp->addAllowedConnectDomain('https://api.mapbox.com');
+ $csp->addAllowedConnectDomain('https://graphhopper.com');
+ // allow connections to custom routing engines
+ $urlKeys = [
+ 'osrmBikeURL',
+ 'osrmCarURL',
+ 'osrmFootURL',
+ 'graphhopperURL'
+ ];
+ foreach ($urlKeys as $key) {
+ $url = $this->config->getAppValue('maps', $key);
+ if ($url !== '') {
+ $scheme = parse_url($url, PHP_URL_SCHEME);
+ $host = parse_url($url, PHP_URL_HOST);
+ $port = parse_url($url, PHP_URL_PORT);
+ $cleanUrl = $scheme . '://' . $host;
+ if ($port && $port !== '') {
+ $cleanUrl .= ':' . $port;
+ }
+ $csp->addAllowedConnectDomain($cleanUrl);
+ }
+ }
+ //$csp->addAllowedConnectDomain('http://192.168.0.66:5000');
+
+ // poi images
+ $csp->addAllowedImageDomain('https://nominatim.openstreetmap.org');
+ // search and geocoder
+ $csp->addAllowedConnectDomain('https://nominatim.openstreetmap.org');
+ $response->setContentSecurityPolicy($csp);
+ }
+ }
+}
diff --git a/lib/Migration/Version000106Date20191118221134.php b/lib/Migration/Version000106Date20191118221134.php
new file mode 100644
index 000000000..7b4dbd2cb
--- /dev/null
+++ b/lib/Migration/Version000106Date20191118221134.php
@@ -0,0 +1,68 @@
+hasTable('maps_favorite_shares')) {
+ $table = $schema->createTable('maps_favorite_shares');
+ $table->addColumn('id', 'bigint', [
+ 'autoincrement' => true,
+ 'notnull' => true,
+ 'length' => 41,
+ ]);
+ $table->addColumn('owner', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('category', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+ $table->addColumn('token', 'string', [
+ 'notnull' => true,
+ 'length' => 64,
+ ]);
+
+ $table->setPrimaryKey(['id']);
+ }
+
+ return $schema;
+ }
+
+ /**
+ * @param IOutput $output
+ * @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
+ * @param array $options
+ */
+ public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options) {
+ }
+}
diff --git a/lib/Service/FavoritesService.php b/lib/Service/FavoritesService.php
index 81dba7137..47429a5a4 100644
--- a/lib/Service/FavoritesService.php
+++ b/lib/Service/FavoritesService.php
@@ -15,17 +15,20 @@
use OCP\IL10N;
use OCP\ILogger;
use OCP\DB\QueryBuilder\IQueryBuilder;
+use OC\Security\SecureRandom;
use OC\Archive\ZIP;
//use function \OCA\Maps\Service\endswith;
-class FavoritesService {
+class FavoritesService
+{
private $l10n;
private $logger;
private $qb;
private $dbconnection;
+ private $secureRandom;
private $currentFavorite;
private $currentFavoritesList;
@@ -35,23 +38,137 @@ class FavoritesService {
private $kmlInsidePlacemark;
private $kmlCurrentCategory;
- public function __construct (ILogger $logger, IL10N $l10n) {
+ public function __construct(ILogger $logger, IL10N $l10n, SecureRandom $secureRandom)
+ {
$this->l10n = $l10n;
$this->logger = $logger;
+ $this->secureRandom = $secureRandom;
$this->qb = \OC::$server->getDatabaseConnection()->getQueryBuilder();
$this->dbconnection = \OC::$server->getDatabaseConnection();
}
- private function db_quote_escape_string($str){
+ private function db_quote_escape_string($str)
+ {
return $this->dbconnection->quote($str);
}
+ public function getFavoritesShare($token) {
+ $qb = $this->qb;
+
+ $qb->select("owner", "category")
+ ->from("maps_favorite_shares")
+ ->where(
+ $qb->expr()->eq('token', $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR))
+ );
+
+ $req = $qb->execute();
+
+ $row = $req->fetch();
+
+ if ($row == false) {
+ return null;
+ }
+
+ $response = [
+ 'owner' => $row['owner'],
+ 'category' => $row['category']
+ ];
+
+ $qb->resetQueryParts();
+
+ return $response;
+ }
+
+ /**
+ * @param $token
+ * @return bool | array
+ */
+ public function getFavoritesByShareToken($token) {
+ $favorites = [];
+ $qb = $this->qb;
+
+ $qb->select('id', 'owner', 'category', 'token')
+ ->from('maps_favorite_shares')
+ ->where(
+ $qb->expr()->eq('token', $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR))
+ );
+
+ $req = $qb->execute();
+
+ if ($req->rowCount() === 0) {
+ return false;
+ }
+
+ $row = $req->fetch();
+
+ $id = $row['id'];
+ $type = $row['type'];
+ $userId = $row['owner'];
+ $category = $row['category'];
+
+ $req->closeCursor();
+ $qb->resetQueryParts();
+
+ $qb->select('id', 'name', 'date_created', 'date_modified', 'lat', 'lng', 'category', 'comment', 'extensions')
+ ->from('maps_favorites', 'f')
+ ->where(
+ $qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))
+ )->andWhere(
+ $qb->expr()->eq('category', $qb->createNamedParameter($category, IQueryBuilder::PARAM_STR))
+ );
+
+ $req = $qb->execute();
+
+ while ($row = $req->fetch()) {
+ array_push($favorites, [
+ 'id' => intval($row['id']),
+ 'name' => $row['name'],
+ 'date_modified' => intval($row['date_modified']),
+ 'date_created' => intval($row['date_created']),
+ 'lat' => floatval($row['lat']),
+ 'lng' => floatval($row['lng']),
+ 'category' => $row['category'],
+ 'comment' => $row['comment'],
+ 'extensions' => $row['extensions']
+ ]);
+ }
+ $req->closeCursor();
+ $qb->resetQueryParts();
+
+ return $favorites;
+ }
+
+ public function getSharedCategories($owner) {
+ $sharedCategories = [];
+
+ $qb = $this->qb;
+ $qb->select('category', 'token')
+ ->from('maps_favorite_shares')
+ ->where(
+ $qb->expr()->eq('owner', $qb->createNamedParameter($owner, IQueryBuilder::PARAM_STR))
+ );
+ $req = $qb->execute();
+
+ while ($row = $req->fetch()) {
+ array_push($sharedCategories, [
+ 'category' => $row['category'],
+ 'token' => $row['token']
+ ]);
+ }
+
+ $req->closeCursor();
+ $qb->resetQueryParts();
+
+ return $sharedCategories;
+ }
+
/**
* @param string $userId
* @param int $pruneBefore
* @return array with favorites
*/
- public function getFavoritesFromDB($userId, $pruneBefore=0) {
+ public function getFavoritesFromDB($userId, $pruneBefore = 0)
+ {
$favorites = [];
$qb = $this->qb;
$qb->select('id', 'name', 'date_created', 'date_modified', 'lat', 'lng', 'category', 'comment', 'extensions')
@@ -93,7 +210,8 @@ public function getFavoritesFromDB($userId, $pruneBefore=0) {
return $favorites;
}
- public function getFavoriteFromDB($id, $userId=null) {
+ public function getFavoriteFromDB($id, $userId = null, $category = null)
+ {
$favorite = null;
$qb = $this->qb;
$qb->select('id', 'name', 'date_modified', 'date_created', 'lat', 'lng', 'category', 'comment', 'extensions')
@@ -106,6 +224,11 @@ public function getFavoriteFromDB($id, $userId=null) {
$qb->expr()->eq('user_id', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR))
);
}
+ if ($category !== null) {
+ $qb->andWhere(
+ $qb->expr()->eq('category', $qb->createNamedParameter($category, IQueryBuilder::PARAM_STR))
+ );
+ }
$req = $qb->execute();
while ($row = $req->fetch()) {
@@ -136,7 +259,8 @@ public function getFavoriteFromDB($id, $userId=null) {
return $favorite;
}
- public function addFavoriteToDB($userId, $name, $lat, $lng, $category, $comment, $extensions) {
+ public function addFavoriteToDB($userId, $name, $lat, $lng, $category, $comment, $extensions)
+ {
$nowTimeStamp = (new \DateTime())->getTimestamp();
$qb = $this->qb;
$qb->insert('maps_favorites')
@@ -157,7 +281,8 @@ public function addFavoriteToDB($userId, $name, $lat, $lng, $category, $comment,
return $favoriteId;
}
- public function addMultipleFavoritesToDB($userId, $favoriteList) {
+ public function addMultipleFavoritesToDB($userId, $favoriteList)
+ {
$nowTimeStamp = (new \DateTime())->getTimestamp();
$values = [];
@@ -167,34 +292,34 @@ public function addMultipleFavoritesToDB($userId, $favoriteList) {
!array_key_exists('lng', $fav) or !is_numeric($fav['lng'])
) {
continue;
- }
- else {
+ } else {
$lat = floatval($fav['lat']);
$lng = floatval($fav['lng']);
}
- $value = '('.
- $this->db_quote_escape_string($userId).', '.
- ((!array_key_exists('name', $fav) or !$fav['name']) ? 'NULL' : $this->db_quote_escape_string($fav['name'])).', '.
- ((!array_key_exists('date_created', $fav) or !is_numeric($fav['date_created'])) ? $this->db_quote_escape_string($nowTimeStamp) : $this->db_quote_escape_string($fav['date_created'])).', '.
- $this->db_quote_escape_string($nowTimeStamp).', '.
- $this->db_quote_escape_string($lat).', '.
- $this->db_quote_escape_string($lng).', '.
- ((!array_key_exists('category', $fav) or !$fav['category']) ? 'NULL' : $this->db_quote_escape_string($fav['category'])).', '.
- ((!array_key_exists('comment', $fav) or !$fav['comment']) ? 'NULL' : $this->db_quote_escape_string($fav['comment'])).', '.
- ((!array_key_exists('extensions', $fav) or !$fav['extensions']) ? 'NULL' : $this->db_quote_escape_string($fav['extensions'])).')';
+ $value = '(' .
+ $this->db_quote_escape_string($userId) . ', ' .
+ ((!array_key_exists('name', $fav) or !$fav['name']) ? 'NULL' : $this->db_quote_escape_string($fav['name'])) . ', ' .
+ ((!array_key_exists('date_created', $fav) or !is_numeric($fav['date_created'])) ? $this->db_quote_escape_string($nowTimeStamp) : $this->db_quote_escape_string($fav['date_created'])) . ', ' .
+ $this->db_quote_escape_string($nowTimeStamp) . ', ' .
+ $this->db_quote_escape_string($lat) . ', ' .
+ $this->db_quote_escape_string($lng) . ', ' .
+ ((!array_key_exists('category', $fav) or !$fav['category']) ? 'NULL' : $this->db_quote_escape_string($fav['category'])) . ', ' .
+ ((!array_key_exists('comment', $fav) or !$fav['comment']) ? 'NULL' : $this->db_quote_escape_string($fav['comment'])) . ', ' .
+ ((!array_key_exists('extensions', $fav) or !$fav['extensions']) ? 'NULL' : $this->db_quote_escape_string($fav['extensions'])) . ')';
array_push($values, $value);
}
$valuesStr = implode(', ', $values);
$sql = '
INSERT INTO *PREFIX*maps_favorites
(user_id, name, date_created, date_modified, lat, lng, category, comment, extensions)
- VALUES '.$valuesStr.' ;';
+ VALUES ' . $valuesStr . ' ;';
$req = $this->dbconnection->prepare($sql);
$req->execute();
$req->closeCursor();
}
- public function renameCategoryInDB($userId, $cat, $newName) {
+ public function renameCategoryInDB($userId, $cat, $newName)
+ {
$qb = $this->qb;
$qb->update('maps_favorites');
$qb->set('category', $qb->createNamedParameter($newName, IQueryBuilder::PARAM_STR));
@@ -208,7 +333,8 @@ public function renameCategoryInDB($userId, $cat, $newName) {
$qb = $qb->resetQueryParts();
}
- public function editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $extensions) {
+ public function editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $extensions)
+ {
$nowTimeStamp = (new \DateTime())->getTimestamp();
$qb = $this->qb;
$qb->update('maps_favorites');
@@ -238,7 +364,8 @@ public function editFavoriteInDB($id, $name, $lat, $lng, $category, $comment, $e
$qb = $qb->resetQueryParts();
}
- public function deleteFavoriteFromDB($id) {
+ public function deleteFavoriteFromDB($id)
+ {
$qb = $this->qb;
$qb->delete('maps_favorites')
->where(
@@ -248,7 +375,65 @@ public function deleteFavoriteFromDB($id) {
$qb = $qb->resetQueryParts();
}
- public function deleteFavoritesFromDB($ids, $userId) {
+ public function getCategoryShareLink($owner, $category)
+ {
+ $qb = $this->qb;
+
+ $qb->select('token')->from('maps_favorite_shares')
+ ->where(
+ $qb->expr()->eq('category', $qb->createNamedParameter($category, IQueryBuilder::PARAM_STR))
+ )->andWhere(
+ $qb->expr()->eq('owner', $qb->createNamedParameter($owner, IQueryBuilder::PARAM_INT))
+ );
+ $req = $qb->execute();
+
+ $row = $req->fetch();
+
+ if (!$row) {
+ $token = $this->secureRandom->generate(
+ \OC\Share\Constants::TOKEN_LENGTH,
+ \OCP\Security\ISecureRandom::CHAR_HUMAN_READABLE
+ );
+
+ $qb = $qb->resetQueryParts();
+
+ $qb->insert('maps_favorite_shares')->values([
+ 'category' => $qb->createNamedParameter($category, IQueryBuilder::PARAM_STR),
+ 'owner' => $qb->createNamedParameter($owner, IQueryBuilder::PARAM_STR),
+ 'token' => $qb->createNamedParameter($token, IQueryBuilder::PARAM_STR),
+ ]);
+ $qb->execute();
+ } else {
+ $token = $row['token'];
+ }
+
+ $req->closeCursor();
+ $qb->resetQueryParts();
+
+ return [
+ 'token' => $token
+ ];
+ }
+
+ public function deleteCategoryShareLink($owner, $category) {
+ $qb = $this->qb;
+
+ $qb->delete('maps_favorite_shares')
+ ->where(
+ $qb->expr()->eq('owner', $qb->createNamedParameter($owner, IQueryBuilder::PARAM_STR))
+ )->andWhere(
+ $qb->expr()->eq('category', $qb->createNamedParameter($category, IQueryBuilder::PARAM_STR))
+ );
+ $qb->execute();
+ $qb->resetQueryParts();
+
+ return [
+ 'deleted' => true
+ ];
+ }
+
+ public function deleteFavoritesFromDB($ids, $userId)
+ {
$qb = $this->qb;
$qb->delete('maps_favorites')
->where(
@@ -260,15 +445,15 @@ public function deleteFavoritesFromDB($ids, $userId) {
$or->add($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
}
$qb->andWhere($or);
- }
- else {
+ } else {
return;
}
$req = $qb->execute();
$qb = $qb->resetQueryParts();
}
- public function countFavorites($userId, $categoryList, $begin, $end) {
+ public function countFavorites($userId, $categoryList, $begin, $end)
+ {
if ($categoryList === null or
(is_array($categoryList) and count($categoryList) === 0)
) {
@@ -313,16 +498,17 @@ public function countFavorites($userId, $categoryList, $begin, $end) {
return $nbFavorites;
}
- public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $end, $appVersion) {
+ public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $end, $appVersion)
+ {
$qb = $this->qb;
$nbFavorites = $this->countFavorites($userId, $categoryList, $begin, $end);
$gpxHeader = '
-
+
favourites
';
- fwrite($fileHandler, $gpxHeader."\n");
+ fwrite($fileHandler, $gpxHeader . "\n");
$chunkSize = 10000;
$favIndex = 0;
@@ -357,8 +543,8 @@ public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $e
$qb->andWhere($or);
}
$qb->orderBy('date_created', 'ASC')
- ->setMaxResults($chunkSize)
- ->setFirstResult($favIndex);
+ ->setMaxResults($chunkSize)
+ ->setFirstResult($favIndex);
$req = $qb->execute();
while ($row = $req->fetch()) {
@@ -377,14 +563,13 @@ public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $e
$extensions = str_replace('&', '&', $row['extensions']);
$gpxExtension = '';
- $gpxText .= ' ' . "\n";
+ $gpxText .= ' ' . "\n";
$gpxText .= ' ' . $name . '' . "\n";
$gpxText .= ' ' . "\n";
if ($category !== null && strlen($category) > 0) {
$gpxText .= ' ' . $category . '' . "\n";
- }
- else {
- $gpxText .= ' '.$this->l10n->t('Personal').'' . "\n";
+ } else {
+ $gpxText .= ' ' . $this->l10n->t('Personal') . '' . "\n";
}
if ($comment !== null && strlen($comment) > 0) {
$gpxText .= ' ' . $comment . '' . "\n";
@@ -393,7 +578,7 @@ public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $e
$gpxExtension .= ' ' . $extensions . '' . "\n";
}
if ($gpxExtension !== '') {
- $gpxText .= ' '. "\n" . $gpxExtension;
+ $gpxText .= ' ' . "\n" . $gpxExtension;
$gpxText .= ' ' . "\n";
}
$gpxText .= ' ' . "\n";
@@ -408,22 +593,22 @@ public function exportFavorites($userId, $fileHandler, $categoryList, $begin, $e
fwrite($fileHandler, $gpxEnd);
}
- public function importFavorites($userId, $file) {
+ public function importFavorites($userId, $file)
+ {
$lowerFileName = strtolower($file->getName());
if ($this->endswith($lowerFileName, '.gpx')) {
return $this->importFavoritesFromGpx($userId, $file);
- }
- elseif ($this->endswith($lowerFileName, '.kml')) {
+ } elseif ($this->endswith($lowerFileName, '.kml')) {
$fp = $file->fopen('r');
$name = $file->getName();
return $this->importFavoritesFromKml($userId, $fp, $name);
- }
- elseif ($this->endswith($lowerFileName, '.kmz')) {
+ } elseif ($this->endswith($lowerFileName, '.kmz')) {
return $this->importFavoritesFromKmz($userId, $file);
}
}
- public function importFavoritesFromKmz($userId, $file) {
+ public function importFavoritesFromKmz($userId, $file)
+ {
$path = $file->getStorage()->getLocalFile($file->getInternalPath());
$name = $file->getName();
$zf = new ZIP($path);
@@ -432,17 +617,17 @@ public function importFavoritesFromKmz($userId, $file) {
$fstream = $zf->getStream($zippedFilePath, 'r');
$result = $this->importFavoritesFromKml($userId, $fstream, $name);
- }
- else {
+ } else {
$result = [
- 'nbImported'=>0,
- 'linesFound'=>false
+ 'nbImported' => 0,
+ 'linesFound' => false
];
}
return $result;
}
- public function importFavoritesFromKml($userId, $fp, $name) {
+ public function importFavoritesFromKml($userId, $fp, $name)
+ {
$this->nbImported = 0;
$this->linesFound = false;
$this->currentFavoritesList = [];
@@ -459,9 +644,9 @@ public function importFavoritesFromKml($userId, $fp, $name) {
while ($data = fread($fp, 4096000)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
$this->logger->error(
- 'Exception in '.$name.' parsing at line '.
- xml_get_current_line_number($xml_parser).' : '.
- xml_error_string(xml_get_error_code($xml_parser)),
+ 'Exception in ' . $name . ' parsing at line ' .
+ xml_get_current_line_number($xml_parser) . ' : ' .
+ xml_error_string(xml_get_error_code($xml_parser)),
array('app' => 'maps')
);
return 0;
@@ -471,12 +656,13 @@ public function importFavoritesFromKml($userId, $fp, $name) {
xml_parser_free($xml_parser);
return [
- 'nbImported'=>$this->nbImported,
- 'linesFound'=>$this->linesFound
+ 'nbImported' => $this->nbImported,
+ 'linesFound' => $this->linesFound
];
}
- private function kmlStartElement($parser, $name, $attrs) {
+ private function kmlStartElement($parser, $name, $attrs)
+ {
$this->currentXmlTag = $name;
if ($name === 'PLACEMARK') {
$this->currentFavorite = [];
@@ -487,15 +673,15 @@ private function kmlStartElement($parser, $name, $attrs) {
}
}
- private function kmlEndElement($parser, $name) {
+ private function kmlEndElement($parser, $name)
+ {
if ($name === 'KML') {
// create last bunch
if (count($this->currentFavoritesList) > 0) {
$this->addMultipleFavoritesToDB($this->importUserId, $this->currentFavoritesList);
}
unset($this->currentFavoritesList);
- }
- else if ($name === 'PLACEMARK') {
+ } else if ($name === 'PLACEMARK') {
$this->kmlInsidePlacemark = false;
// store favorite
$this->nbImported++;
@@ -526,32 +712,30 @@ private function kmlEndElement($parser, $name) {
}
}
- private function kmlDataElement($parser, $data) {
+ private function kmlDataElement($parser, $data)
+ {
$d = trim($data);
if (!empty($d)) {
if (!$this->kmlInsidePlacemark) {
if ($this->currentXmlTag === 'NAME') {
$this->kmlCurrentCategory = $this->kmlCurrentCategory . $d;
}
- }
- else {
+ } else {
if ($this->currentXmlTag === 'NAME') {
- $this->currentFavorite['name'] = (array_key_exists('name', $this->currentFavorite)) ? $this->currentFavorite['name'].$d : $d;
- }
- else if ($this->currentXmlTag === 'WHEN') {
- $this->currentFavorite['date_created'] = (array_key_exists('date_created', $this->currentFavorite)) ? $this->currentFavorite['date_created'].$d : $d;
- }
- else if ($this->currentXmlTag === 'COORDINATES') {
- $this->currentFavorite['coordinates'] = (array_key_exists('coordinates', $this->currentFavorite)) ? $this->currentFavorite['coordinates'].$d : $d;
- }
- else if ($this->currentXmlTag === 'DESCRIPTION') {
- $this->currentFavorite['comment'] = (array_key_exists('comment', $this->currentFavorite)) ? $this->currentFavorite['comment'].$d : $d;
+ $this->currentFavorite['name'] = (array_key_exists('name', $this->currentFavorite)) ? $this->currentFavorite['name'] . $d : $d;
+ } else if ($this->currentXmlTag === 'WHEN') {
+ $this->currentFavorite['date_created'] = (array_key_exists('date_created', $this->currentFavorite)) ? $this->currentFavorite['date_created'] . $d : $d;
+ } else if ($this->currentXmlTag === 'COORDINATES') {
+ $this->currentFavorite['coordinates'] = (array_key_exists('coordinates', $this->currentFavorite)) ? $this->currentFavorite['coordinates'] . $d : $d;
+ } else if ($this->currentXmlTag === 'DESCRIPTION') {
+ $this->currentFavorite['comment'] = (array_key_exists('comment', $this->currentFavorite)) ? $this->currentFavorite['comment'] . $d : $d;
}
}
}
}
- public function importFavoritesFromGpx($userId, $file) {
+ public function importFavoritesFromGpx($userId, $file)
+ {
$this->nbImported = 0;
$this->linesFound = false;
$this->currentFavoritesList = [];
@@ -569,9 +753,9 @@ public function importFavoritesFromGpx($userId, $file) {
while ($data = fread($fp, 4096000)) {
if (!xml_parse($xml_parser, $data, feof($fp))) {
$this->logger->error(
- 'Exception in '.$file->getName().' parsing at line '.
- xml_get_current_line_number($xml_parser).' : '.
- xml_error_string(xml_get_error_code($xml_parser)),
+ 'Exception in ' . $file->getName() . ' parsing at line ' .
+ xml_get_current_line_number($xml_parser) . ' : ' .
+ xml_error_string(xml_get_error_code($xml_parser)),
array('app' => 'maps')
);
return 0;
@@ -581,12 +765,13 @@ public function importFavoritesFromGpx($userId, $file) {
xml_parser_free($xml_parser);
return [
- 'nbImported'=>$this->nbImported,
- 'linesFound'=>$this->linesFound
+ 'nbImported' => $this->nbImported,
+ 'linesFound' => $this->linesFound
];
}
- private function gpxStartElement($parser, $name, $attrs) {
+ private function gpxStartElement($parser, $name, $attrs)
+ {
$this->currentXmlTag = $name;
if ($name === 'WPT') {
$this->insideWpt = true;
@@ -603,15 +788,15 @@ private function gpxStartElement($parser, $name, $attrs) {
}
}
- private function gpxEndElement($parser, $name) {
+ private function gpxEndElement($parser, $name)
+ {
if ($name === 'GPX') {
// create last bunch
if (count($this->currentFavoritesList) > 0) {
$this->addMultipleFavoritesToDB($this->importUserId, $this->currentFavoritesList);
}
unset($this->currentFavoritesList);
- }
- else if ($name === 'WPT') {
+ } else if ($name === 'WPT') {
$this->insideWpt = false;
// store favorite
$this->nbImported++;
@@ -634,28 +819,26 @@ private function gpxEndElement($parser, $name) {
}
}
- private function gpxDataElement($parser, $data) {
+ private function gpxDataElement($parser, $data)
+ {
$d = trim($data);
if (!empty($d)) {
if ($this->insideWpt and $this->currentXmlTag === 'NAME') {
- $this->currentFavorite['name'] = (array_key_exists('name', $this->currentFavorite)) ? $this->currentFavorite['name'].$d : $d;
- }
- else if ($this->insideWpt and $this->currentXmlTag === 'TIME') {
- $this->currentFavorite['date_created'] = (array_key_exists('date_created', $this->currentFavorite)) ? $this->currentFavorite['date_created'].$d : $d;
- }
- else if ($this->insideWpt and $this->currentXmlTag === 'TYPE') {
- $this->currentFavorite['category'] = (array_key_exists('category', $this->currentFavorite)) ? $this->currentFavorite['category'].$d : $d;
- }
- else if ($this->insideWpt and $this->currentXmlTag === 'DESC') {
- $this->currentFavorite['comment'] = (array_key_exists('comment', $this->currentFavorite)) ? $this->currentFavorite['comment'].$d : $d;
- }
- else if ($this->insideWpt and $this->currentXmlTag === 'MAPS-EXTENSIONS') {
- $this->currentFavorite['extensions'] = (array_key_exists('extensions', $this->currentFavorite)) ? $this->currentFavorite['extensions'].$d : $d;
+ $this->currentFavorite['name'] = (array_key_exists('name', $this->currentFavorite)) ? $this->currentFavorite['name'] . $d : $d;
+ } else if ($this->insideWpt and $this->currentXmlTag === 'TIME') {
+ $this->currentFavorite['date_created'] = (array_key_exists('date_created', $this->currentFavorite)) ? $this->currentFavorite['date_created'] . $d : $d;
+ } else if ($this->insideWpt and $this->currentXmlTag === 'TYPE') {
+ $this->currentFavorite['category'] = (array_key_exists('category', $this->currentFavorite)) ? $this->currentFavorite['category'] . $d : $d;
+ } else if ($this->insideWpt and $this->currentXmlTag === 'DESC') {
+ $this->currentFavorite['comment'] = (array_key_exists('comment', $this->currentFavorite)) ? $this->currentFavorite['comment'] . $d : $d;
+ } else if ($this->insideWpt and $this->currentXmlTag === 'MAPS-EXTENSIONS') {
+ $this->currentFavorite['extensions'] = (array_key_exists('extensions', $this->currentFavorite)) ? $this->currentFavorite['extensions'] . $d : $d;
}
}
}
- private function endswith($string, $test) {
+ private function endswith($string, $test)
+ {
$strlen = strlen($string);
$testlen = strlen($test);
if ($testlen > $strlen) return false;
diff --git a/package.json b/package.json
index 34df7f843..f62ec40bf 100644
--- a/package.json
+++ b/package.json
@@ -9,7 +9,10 @@
"scripts": {
"test": "node node_modules/gulp-cli/bin/gulp.js karma",
"prebuild": "npm install",
- "build": "node node_modules/gulp-cli/bin/gulp.js"
+ "build-old": "node node_modules/gulp-cli/bin/gulp.js",
+ "build": "NODE_ENV=production webpack --progress --hide-modules --config webpack.prod.js",
+ "dev": "NODE_ENV=development webpack --config webpack.dev.js",
+ "watch": "NODE_ENV=development webpack --progress --watch --config webpack.dev.js"
},
"repository": {
"type": "git",
@@ -25,23 +28,76 @@
},
"homepage": "https://github.com/nextcloud/maps",
"dependencies": {
+ "@nextcloud/vue": "^1.1.0",
+ "d3": "^3.5.17",
"gulp": "^4.0.0",
"gulp-cli": "^2.1.0",
- "leaflet": "^1.4.0",
+ "hammerjs": "^2.0.8",
+ "leaflet": "^1.5.1",
+ "leaflet-contextmenu": "^1.4.0",
"leaflet-control-geocoder": "^1.7.0",
"leaflet-easybutton": "^2.4.0",
"leaflet-routing-machine": "^3.2.12",
+ "leaflet.elevation": "^0.0.3",
"leaflet.featuregroup.subgroup": "^1.0.2",
"leaflet.locatecontrol": "^0.67.0",
"leaflet.markercluster": "^1.4.0",
"leaflet-mouse-position": "^1.0.4",
- "leaflet-contextmenu": "^1.4.0",
- "leaflet.elevation": "^0.0.3",
"mapbox-gl": "^1.4.1",
"mapbox-gl-leaflet": "^0.0.11",
- "d3": "^3.5.17",
"nouislider": "^14.0.2",
+ "opening_hours": "^3.5.0",
"ua-parser-js": "^0.7.20",
- "opening_hours": "^3.5.0"
+ "vue": "^2.6.10",
+ "vue-types": "^1.6.0",
+ "vue2-leaflet": "^2.2.1",
+ "vue2-leaflet-markercluster": "^3.1.0",
+ "vuex": "^3.1.1"
+ },
+ "devDependencies": {
+ "@babel/core": "^7.6.4",
+ "@babel/plugin-proposal-object-rest-spread": "^7.6.2",
+ "@babel/plugin-syntax-dynamic-import": "^7.2.0",
+ "@babel/preset-env": "^7.6.3",
+ "babel-core": "^7.0.0-bridge.0",
+ "babel-eslint": "^10.0.3",
+ "babel-jest": "^24.9.0",
+ "babel-loader": "^8.0.6",
+ "browserslist-config-nextcloud": "0.1.0",
+ "css-loader": "^3.2.0",
+ "eslint": "^6.6.0",
+ "eslint-config-airbnb-base": "^14.0.0",
+ "eslint-config-prettier": "^6.5.0",
+ "eslint-config-standard": "^14.1.0",
+ "eslint-friendly-formatter": "^4.0.1",
+ "eslint-loader": "^3.0.2",
+ "eslint-plugin-import": "^2.18.2",
+ "eslint-plugin-node": "^10.0.0",
+ "eslint-plugin-prettier": "^3.1.1",
+ "eslint-plugin-promise": "^4.2.1",
+ "eslint-plugin-standard": "^4.0.1",
+ "eslint-plugin-vue": "^5.2.3",
+ "file-loader": "^4.2.0",
+ "jest": "^24.9.0",
+ "jest-serializer-vue": "^2.0.2",
+ "jsdom": "^15.1.1",
+ "jsdom-global": "^3.0.2",
+ "node-sass": "^4.13.0",
+ "prettier": "^1.18.2",
+ "raw-loader": "^3.1.0",
+ "sass-loader": "^8.0.0",
+ "stylelint": "^11.1.1",
+ "stylelint-config-recommended-scss": "^4.0.0",
+ "stylelint-config-standard": "^19.0.0",
+ "stylelint-scss": "^3.12.0",
+ "stylelint-webpack-plugin": "^1.0.3",
+ "svg-sprite": "^1.5.0",
+ "terser-webpack-plugin": "^2.2.1",
+ "vue-jest": "^3.0.5",
+ "vue-loader": "^15.7.1",
+ "vue-template-compiler": "^2.6.10",
+ "webpack": "^4.41.2",
+ "webpack-cli": "^3.3.9",
+ "webpack-merge": "^4.2.2"
}
}
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 000000000..9c89a92bd
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
diff --git a/src/components/MapContainer.vue b/src/components/MapContainer.vue
new file mode 100644
index 000000000..b00bf22c1
--- /dev/null
+++ b/src/components/MapContainer.vue
@@ -0,0 +1,388 @@
+
+
+
+
+
+
+ handleMarkerReady(favorite.id, marker)"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/PublicFavoriteShareSideBar.vue b/src/components/PublicFavoriteShareSideBar.vue
new file mode 100644
index 000000000..723ffa4bd
--- /dev/null
+++ b/src/components/PublicFavoriteShareSideBar.vue
@@ -0,0 +1,85 @@
+
+
+
+
+ Example settings
+
+
+
+
+
+
+
diff --git a/src/components/map/ClickPopup.vue b/src/components/map/ClickPopup.vue
new file mode 100644
index 000000000..516ffe4db
--- /dev/null
+++ b/src/components/map/ClickPopup.vue
@@ -0,0 +1,174 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/map/FavoritePopup.vue b/src/components/map/FavoritePopup.vue
new file mode 100644
index 000000000..ba501c681
--- /dev/null
+++ b/src/components/map/FavoritePopup.vue
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/map/MapPopupLayer.vue b/src/components/map/MapPopupLayer.vue
new file mode 100644
index 000000000..134c1dc8a
--- /dev/null
+++ b/src/components/map/MapPopupLayer.vue
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/components/map/Popup.vue b/src/components/map/Popup.vue
new file mode 100644
index 000000000..071431392
--- /dev/null
+++ b/src/components/map/Popup.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
diff --git a/src/components/map/PopupFormItem.vue b/src/components/map/PopupFormItem.vue
new file mode 100644
index 000000000..f96d5aafb
--- /dev/null
+++ b/src/components/map/PopupFormItem.vue
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
diff --git a/src/components/map/SimpleOSMAddress.vue b/src/components/map/SimpleOSMAddress.vue
new file mode 100644
index 000000000..6a9d4f595
--- /dev/null
+++ b/src/components/map/SimpleOSMAddress.vue
@@ -0,0 +1,147 @@
+
+
+
+
+
+
+
diff --git a/src/data/enum/MapMode.js b/src/data/enum/MapMode.js
new file mode 100644
index 000000000..5106d25bd
--- /dev/null
+++ b/src/data/enum/MapMode.js
@@ -0,0 +1,4 @@
+export default {
+ DEFAULT: "default",
+ ADDING_FAVORITES: "adding-favorites"
+};
diff --git a/src/data/enum/MapPopupState.js b/src/data/enum/MapPopupState.js
new file mode 100644
index 000000000..885663aa4
--- /dev/null
+++ b/src/data/enum/MapPopupState.js
@@ -0,0 +1,6 @@
+export default {
+ RequestOpen: "request-open",
+ RequestClose: "request-close",
+ Closed: "closed",
+ Opened: "opened"
+};
diff --git a/src/main.js b/src/main.js
new file mode 100644
index 000000000..012b9b6c5
--- /dev/null
+++ b/src/main.js
@@ -0,0 +1,29 @@
+import Vue from "vue";
+import App from "./App";
+import { Icon } from "leaflet";
+import "leaflet/dist/leaflet.css";
+
+import store from "./store";
+
+Vue.prototype.t = window.t;
+Vue.prototype.n = window.n;
+Vue.prototype.OC = window.OC;
+Vue.prototype.OCA = window.OCA;
+
+if (process && process.env.NODE_ENV === "development") {
+ Vue.config.devtools = true;
+}
+
+// this part resolve an issue where the markers would not appear
+delete Icon.Default.prototype._getIconUrl;
+
+Icon.Default.mergeOptions({
+ iconRetinaUrl: require("leaflet/dist/images/marker-icon-2x.png"),
+ iconUrl: require("leaflet/dist/images/marker-icon.png"),
+ shadowUrl: require("leaflet/dist/images/marker-shadow.png")
+});
+
+new Vue({
+ render: h => h(App),
+ store
+}).$mount("#app");
diff --git a/src/store/index.js b/src/store/index.js
new file mode 100644
index 000000000..fdff6371c
--- /dev/null
+++ b/src/store/index.js
@@ -0,0 +1,14 @@
+import Vuex from "vuex";
+import Vue from "vue";
+import publicFavorites from "./modules/publicFavorites";
+import map from "./modules/map";
+
+Vuex.install(Vue);
+
+export default new Vuex.Store({
+ modules: {
+ publicFavorites,
+ map
+ },
+ strict: process.env.NODE_ENV !== "production"
+});
diff --git a/src/store/modules/map.js b/src/store/modules/map.js
new file mode 100644
index 000000000..b403bb710
--- /dev/null
+++ b/src/store/modules/map.js
@@ -0,0 +1,25 @@
+import MapMode from "../../data/enum/MapMode";
+
+export const MAP_NAMESPACE = "map";
+
+const state = {
+ mode: MapMode.DEFAULT
+};
+
+const getters = {};
+
+const actions = {};
+
+const mutations = {
+ setMode(state, mode) {
+ state.mode = mode;
+ }
+};
+
+export default {
+ namespaced: true,
+ state,
+ getters,
+ actions,
+ mutations
+};
diff --git a/src/store/modules/publicFavorites.js b/src/store/modules/publicFavorites.js
new file mode 100644
index 000000000..764e349f1
--- /dev/null
+++ b/src/store/modules/publicFavorites.js
@@ -0,0 +1,96 @@
+import { publicApiRequest, showNotification } from "../../utils/common";
+import { getCategoryRawName } from "../../utils/mapUtils";
+import { getPublicShareCategory } from "../../utils/publicShareUtils";
+
+export const PUBLIC_FAVORITES_NAMESPACE = "publicFavorites";
+
+const state = {
+ favorites: [],
+ selectedFavoriteId: null
+};
+
+const getters = {
+ mappedByCategory(state) {
+ if (state.favorites.length === 0) {
+ return {};
+ }
+
+ return {
+ [getCategoryRawName(state.favorites[0].category)]: state.favorites
+ };
+ }
+};
+
+const actions = {
+ selectFavorite({ commit }, favoriteId) {
+ commit("setSelectedFavoriteId", favoriteId);
+ },
+ getFavorites({ commit }) {
+ return publicApiRequest("favorites", "GET")
+ .then(data => {
+ commit("setFavorites", data);
+ })
+ .catch(() => showNotification(t("maps", "Failed to get favorites")));
+ },
+ addFavorite({ commit }, { lat, lng, name, comment }) {
+ return publicApiRequest("favorites", "POST", {
+ lat,
+ lng,
+ name,
+ category: getPublicShareCategory(),
+ comment,
+ extensions: "" // TODO:
+ })
+ .then(data => {
+ commit("addFavorite", data);
+ })
+ .catch(() => showNotification(t("maps", "Failed to create favorite")));
+ },
+ updateFavorite({ commit }, { id, name, comment }) {
+ return publicApiRequest(`favorites/${id}`, "PUT", {
+ name,
+ category: getPublicShareCategory(),
+ comment,
+ extensions: "" // TODO:
+ })
+ .then(data => {
+ commit("editFavorite", data);
+ })
+ .catch(() => showNotification(t("maps", "Failed to update favorite")));
+ },
+ deleteFavorite({ commit }, { id }) {
+ return publicApiRequest(`favorites/${id}`, "DELETE")
+ .then(() => {
+ commit("deleteFavorite", id);
+ })
+ .catch(() => showNotification(t("maps", "Failed to delete favorite")));
+ }
+};
+
+const mutations = {
+ setFavorites(state, favorites) {
+ state.favorites = favorites;
+ },
+ addFavorite(state, favorite) {
+ state.favorites = [...state.favorites, favorite];
+ },
+ editFavorite(state, favorite) {
+ state.favorites = state.favorites.map(el =>
+ el.id === favorite.id ? favorite : el
+ );
+ },
+ deleteFavorite(state, id) {
+ state.favorites = state.favorites.filter(el => el.id !== id);
+ },
+ setSelectedFavoriteId(state, favoriteId) {
+ state.selectedFavoriteId = favoriteId;
+ }
+};
+
+export default {
+ namespaced: true,
+ state,
+ getters,
+ actions,
+ mutations
+};
diff --git a/src/utils/common.js b/src/utils/common.js
new file mode 100644
index 000000000..1c57bc1b9
--- /dev/null
+++ b/src/utils/common.js
@@ -0,0 +1,62 @@
+export const isPublicShare = () => {
+ return document.body.id === "body-public";
+};
+
+export const getCurrentPublicShareToken = () => {
+ // FIXME: there must be a better way to retrieve the token client side
+ const path = location.pathname.split("/");
+
+ return path[path.length - 1];
+};
+
+export const publicApiRequest = (slug, method, data = null) => {
+ return request(
+ OC.generateUrl(
+ `/apps/maps/api/1.0/public/${getCurrentPublicShareToken()}/${slug}`
+ ),
+ method,
+ data
+ );
+};
+
+export const apiRequest = (slug, method, data = null) => {
+ return request(
+ OC.generateUrl(`apps/maps/api/1.0/${getCurrentPublicShareToken()}/${slug}`),
+ method,
+ data
+ );
+};
+
+/**
+ * Perform a network request
+ *
+ * TODO: Use axios or similar instead of jQuery ajax
+ *
+ * @param url : string
+ * @param method : string
+ * @param data : {} | null
+ * @returns {Promise<{}>}
+ */
+export const request = (url, method, data = null) => {
+ return new Promise((resolve, reject) => {
+ $.ajax({
+ url: url,
+ type: method.toUpperCase(),
+ data,
+ async: true
+ })
+ .done(resolve)
+ .fail(reject);
+ });
+};
+
+/**
+ * Show temporary notification
+ *
+ * TODO: Use non-deprecated function
+ *
+ * @param message : string
+ */
+export const showNotification = message => {
+ OC.Notification.showTemporary(t("maps", message));
+};
diff --git a/src/utils/mapUtils.js b/src/utils/mapUtils.js
new file mode 100644
index 000000000..5d8eee424
--- /dev/null
+++ b/src/utils/mapUtils.js
@@ -0,0 +1,26 @@
+import { request } from "./common";
+
+export const getCategoryRawName = categoryName =>
+ categoryName.replace(" ", "-");
+
+export const isGeocodeable = str => {
+ const pattern = /^\s*-?\d+\.?\d*,\s*-?\d+\.?\d*\s*$/;
+
+ return pattern.test(str);
+};
+
+export const constructGeoCodeUrl = (lat, lng) =>
+ `https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&addressdetails=1`;
+
+export const geocode = latLngStr => {
+ if (!isGeocodeable(latLngStr)) {
+ return Promise.reject(`${latLngStr} is not geocodable`);
+ }
+
+ const latLng = latLngStr.split(",");
+
+ const lat = latLng[0].trim();
+ const lng = latLng[1].trim();
+
+ return request(constructGeoCodeUrl(lat, lng), "GET");
+};
diff --git a/src/utils/publicShareUtils.js b/src/utils/publicShareUtils.js
new file mode 100644
index 000000000..cfe381000
--- /dev/null
+++ b/src/utils/publicShareUtils.js
@@ -0,0 +1,9 @@
+export const getPublicShareCategory = () => {
+ const el = document.querySelector(".header-appname");
+
+ if (!el) {
+ throw new Error("Could not get publis share category");
+ }
+
+ return el.textContent;
+};
diff --git a/templates/index.php b/templates/index.php
index d6bb2172c..bd105fc62 100644
--- a/templates/index.php
+++ b/templates/index.php
@@ -12,14 +12,14 @@
script('maps', 'script');
?>
-
-
- inc('navigation/index')); ?>
- inc('settings/index')); ?>
-
+
+
+ inc('navigation/index')); ?>
+ inc('settings/index')); ?>
+
-
- inc('content/index')); ?>
+
+ inc('content/index')); ?>
+
-
diff --git a/templates/public/favorites_index.php b/templates/public/favorites_index.php
new file mode 100644
index 000000000..9dab85c41
--- /dev/null
+++ b/templates/public/favorites_index.php
@@ -0,0 +1,6 @@
+
+
+
diff --git a/webpack.common.js b/webpack.common.js
new file mode 100644
index 000000000..309b3cc42
--- /dev/null
+++ b/webpack.common.js
@@ -0,0 +1,65 @@
+const path = require("path");
+const webpack = require("webpack");
+const { VueLoaderPlugin } = require("vue-loader");
+//const StyleLintPlugin = require("stylelint-webpack-plugin");
+
+module.exports = {
+ entry: path.join(__dirname, "src", "main.js"),
+ output: {
+ path: path.resolve(__dirname, "./js"),
+ publicPath: "/js/",
+ filename: "maps.js"
+ },
+ module: {
+ rules: [
+ {
+ test: /\.css$/,
+ use: ["vue-style-loader", "css-loader"]
+ },
+ {
+ test: /\.scss$/,
+ use: ["vue-style-loader", "css-loader", "sass-loader"]
+ },
+ {
+ test: /src\/.*\.(js|vue)$/,
+ use: "eslint-loader",
+ enforce: "pre"
+ },
+ {
+ test: /\.vue$/,
+ loader: "vue-loader"
+ },
+ {
+ test: /\.js$/,
+ use: {
+ loader: "babel-loader",
+ options: {
+ plugins: [
+ "@babel/plugin-syntax-dynamic-import",
+ "@babel/plugin-proposal-object-rest-spread"
+ ],
+ presets: ["@babel/preset-env"]
+ }
+ },
+ exclude: /node_modules\/(?!(p-limit|p-defer|p-queue|p-try|cdav-library))/
+ },
+ {
+ test: /\.(png|jpg|gif|svg)$/,
+ loader: "file-loader",
+ options: {
+ name: "[name].[ext]?[hash]"
+ }
+ }
+ ]
+ },
+ plugins: [
+ new VueLoaderPlugin(),
+ // new StyleLintPlugin(),
+ new webpack.DefinePlugin({
+ $appVersion: JSON.stringify(require("./package.json").version)
+ })
+ ],
+ resolve: {
+ extensions: ["*", ".js", ".vue", ".json"]
+ }
+};
diff --git a/webpack.dev.js b/webpack.dev.js
new file mode 100644
index 000000000..d5e928436
--- /dev/null
+++ b/webpack.dev.js
@@ -0,0 +1,12 @@
+const merge = require('webpack-merge')
+const common = require('./webpack.common.js')
+
+module.exports = merge(common, {
+ mode: 'development',
+ devServer: {
+ historyApiFallback: true,
+ noInfo: true,
+ overlay: true
+ },
+ devtool: '#cheap-source-map',
+});
diff --git a/webpack.prod.js b/webpack.prod.js
new file mode 100644
index 000000000..f7e4d9017
--- /dev/null
+++ b/webpack.prod.js
@@ -0,0 +1,18 @@
+const merge = require('webpack-merge')
+const common = require('./webpack.common.js')
+const TerserPlugin = require('terser-webpack-plugin')
+
+module.exports = merge(common, {
+ mode: 'production',
+ devtool: '#source-map',
+ optimization: {
+ minimizer: [new TerserPlugin({
+ terserOptions: {
+ output: {
+ comments: false,
+ }
+ },
+ sourceMap: true,
+ })],
+ }
+});
\ No newline at end of file