Skip to content
Snippets Groups Projects
Commit 5aed86b8 authored by Benoit Lavenier's avatar Benoit Lavenier
Browse files

[enh] add a specific map for pages

[enh] Wot lookup: add tab button to access to pages search
parent 367e3bca
No related branches found
No related tags found
No related merge requests found
Showing
with 13087 additions and 13893 deletions
...@@ -214,8 +214,10 @@ ...@@ -214,8 +214,10 @@
<script src="dist/dist_js/plugins/map/js/plugin.js"></script> <script src="dist/dist_js/plugins/map/js/plugin.js"></script>
<script src="dist/dist_js/plugins/map/js/services.js"></script> <script src="dist/dist_js/plugins/map/js/services.js"></script>
<script src="dist/dist_js/plugins/map/js/services/wot-services.js"></script> <script src="dist/dist_js/plugins/map/js/services/wot-services.js"></script>
<script src="dist/dist_js/plugins/map/js/services/registry-services.js"></script>
<script src="dist/dist_js/plugins/map/js/services/utils-services.js"></script> <script src="dist/dist_js/plugins/map/js/services/utils-services.js"></script>
<script src="dist/dist_js/plugins/map/js/controllers/wot-controllers.js"></script> <script src="dist/dist_js/plugins/map/js/controllers/wot-controllers.js"></script>
<script src="dist/dist_js/plugins/map/js/controllers/registry-controllers.js"></script>
<script src="dist/dist_js/plugins/map/js/controllers/network-controllers.js"></script> <script src="dist/dist_js/plugins/map/js/controllers/network-controllers.js"></script>
<script src="dist/dist_js/plugins/map/js/controllers/user-controllers.js"></script> <script src="dist/dist_js/plugins/map/js/controllers/user-controllers.js"></script>
<script src="dist/dist_js/plugins/map/js/controllers/settings-controllers.js"></script> <script src="dist/dist_js/plugins/map/js/controllers/settings-controllers.js"></script>
......
This diff is collapsed.
...@@ -237,3 +237,7 @@ ...@@ -237,3 +237,7 @@
font-family: "Ionicons"; font-family: "Ionicons";
content: "\f110"; content: "\f110";
} }
.yellow-bg {
background-color: #ffeb00;
}
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
"SHOW_ALL": "Voir tout", "SHOW_ALL": "Voir tout",
"LOAD_NOTIFICATIONS_FAILED": "Erreur de chargement des notifications" "LOAD_NOTIFICATIONS_FAILED": "Erreur de chargement des notifications"
}, },
"GEO": {
"DISTANCE": "Distance maximale autour de la ville",
"DISTANCE_OPTION": "{{value}} km"
},
"ERROR": { "ERROR": {
"REQUIRED_FOR_LOCATION": "Champ obligatoire pour apparaître sur la carte", "REQUIRED_FOR_LOCATION": "Champ obligatoire pour apparaître sur la carte",
"INVALID_FOR_LOCATION": "Adresse inconnue" "INVALID_FOR_LOCATION": "Adresse inconnue"
...@@ -265,6 +269,10 @@ ...@@ -265,6 +269,10 @@
"LOCATION_DIVIDER": "Adresse", "LOCATION_DIVIDER": "Adresse",
"SOCIAL_NETWORKS_DIVIDER": "Réseaux sociaux et site web", "SOCIAL_NETWORKS_DIVIDER": "Réseaux sociaux et site web",
"TECHNICAL_DIVIDER": "Informations techniques", "TECHNICAL_DIVIDER": "Informations techniques",
"BTN_SHOW_WOT": "Annuaire",
"BTN_SHOW_WOT_HELP": "Rechercher dans l'annuaire",
"BTN_SHOW_PAGES": "Pages",
"BTN_SHOW_PAGES_HELP": "Rechercher dans les pages",
"BTN_NEW": "Créer une page", "BTN_NEW": "Créer une page",
"MY_PAGES": "Mes pages", "MY_PAGES": "Mes pages",
"NO_PAGE": "Aucune page", "NO_PAGE": "Aucune page",
...@@ -275,8 +283,8 @@ ...@@ -275,8 +283,8 @@
"BTN_ADD": "Nouveau", "BTN_ADD": "Nouveau",
"BTN_OPTIONS": "Recherche avancée", "BTN_OPTIONS": "Recherche avancée",
"TYPE": "Type de page", "TYPE": "Type de page",
"LOCATION": "Localisation", "LOCATION": "Ville",
"LOCATION_HELP": "Ville", "LOCATION_HELP": "Ville, Code postal",
"LAST_RECORDS": "Derniers enregistrements :", "LAST_RECORDS": "Derniers enregistrements :",
"RESULTS": "Résultats :" "RESULTS": "Résultats :"
}, },
...@@ -326,7 +334,8 @@ ...@@ -326,7 +334,8 @@
"SAVE_RECORD_FAILED": "Erreur lors de la sauvegarde", "SAVE_RECORD_FAILED": "Erreur lors de la sauvegarde",
"RECORD_NOT_EXISTS": "Page inexistante", "RECORD_NOT_EXISTS": "Page inexistante",
"FAILED_SAVE_COMMENT": "Erreur lors de la sauvegarde du commentaire", "FAILED_SAVE_COMMENT": "Erreur lors de la sauvegarde du commentaire",
"FAILED_REMOVE_COMMENT": "Erreur lors de la suppression du commentaire" "FAILED_REMOVE_COMMENT": "Erreur lors de la suppression du commentaire",
"GEO_LOCATION_NOT_FOUND": "Ville ou code postal non trouvé"
}, },
"INFO": { "INFO": {
"RECORD_REMOVED" : "Page supprimée", "RECORD_REMOVED" : "Page supprimée",
......
...@@ -16,9 +16,9 @@ angular.module('cesium.es.common.controllers', ['ngResource', 'cesium.es.service ...@@ -16,9 +16,9 @@ angular.module('cesium.es.common.controllers', ['ngResource', 'cesium.es.service
.controller('ESPositionEditCtrl', ESPositionEditController) .controller('ESPositionEditCtrl', ESPositionEditController)
.controller('ESSearchPositionModalCtrl', ESSearchPositionModalController) .controller('ESLookupPositionCtrl', ESLookupPositionController)
.controller('ESSearchPositionModalCtrl', ESSearchPositionModalController)
; ;
...@@ -513,7 +513,7 @@ function ESPositionEditController($scope, csConfig, esGeo, ModalUtils) { ...@@ -513,7 +513,7 @@ function ESPositionEditController($scope, csConfig, esGeo, ModalUtils) {
$scope.getAddressToSearch = function() { $scope.getAddressToSearch = function() {
return $scope.formData.address && $scope.formData.city ? return $scope.formData.address && $scope.formData.city ?
[$scope.formData.address.trim(), $scope.formData.city.trim()].join(', ') : [$scope.formData.address.trim(), $scope.formData.city.trim()].join(', ') :
$scope.formData.city || $scope.formData.address; $scope.formData.city || $scope.formData.address || $scope.formData.location ;
}; };
$scope.updateGeoPoint = function(res) { $scope.updateGeoPoint = function(res) {
...@@ -566,6 +566,140 @@ function ESPositionEditController($scope, csConfig, esGeo, ModalUtils) { ...@@ -566,6 +566,140 @@ function ESPositionEditController($scope, csConfig, esGeo, ModalUtils) {
}; };
} }
function ESLookupPositionController($scope, $q, csConfig, esGeo, ModalUtils) {
'ngInject';
// The default country used for address localisation
var defaultCountry = csConfig.plugins && csConfig.plugins.es && csConfig.plugins.es.defaultCountry;
var loadingPosition = false;
$scope.geoDistanceLabels = {
"5km": {
labelKey: 'COMMON.GEO.DISTANCE_OPTION',
labelParams: {value: 5}
},
"10km": {
labelKey: 'COMMON.GEO.DISTANCE_OPTION',
labelParams: {value: 10}
},
"20km": {
labelKey: 'COMMON.GEO.DISTANCE_OPTION',
labelParams: {value: 20}
},
"30km": {
labelKey: 'COMMON.GEO.DISTANCE_OPTION',
labelParams: {value: 30}
},
"50km": {
labelKey: 'COMMON.GEO.DISTANCE_OPTION',
labelParams: {value: 50}
},
"100km": {
labelKey: 'COMMON.GEO.DISTANCE_OPTION',
labelParams: {value: 100}
},
"250km": {
labelKey: 'COMMON.GEO.DISTANCE_OPTION',
labelParams: {value: 250}
},
"500km": {
labelKey: 'COMMON.GEO.DISTANCE_OPTION',
labelParams: {value: 500}
}
};
$scope.geoDistances = _.keys($scope.geoDistanceLabels);
$scope.searchPosition = function(searchText) {
if (loadingPosition) return $q.when();
loadingPosition = true;
// No address, so try to localize by device
if (!searchText) {
return esGeo.point.current()
.then($scope.updateGeoPoint)
.then(function() {
loadingPosition = false;
})
.catch(function(err) {
console.error(err); // Silent
loadingPosition = false;
});
}
return esGeo.point.searchByAddress(searchText)
.then(function(res) {
if (res && res.length == 1) {
return res[0];
}
return $scope.openSearchLocationModal({
text: searchText,
results: res||[],
forceFallback: !res || !res.length // force fallback search first
})
.then(function(res) {
// Compute point name
if (res && res.address && res.address.city) {
var cityParts = [res.address.city];
if (res.address.postcode) {
cityParts.push(res.address.postcode);
}
if (res.address.country != defaultCountry) {
cityParts.push(res.address.country);
}
res.shortName = cityParts.join(', ');
}
return res;
});
})
.then(function(res) {
loadingPosition = false;
// user cancel
if (!res || !res.lat || !res.lon) return;
return {
lat: parseFloat(res.lat),
lon: parseFloat(res.lon),
name: res.shortName
};
})
.catch(function(err) {
console.error(err); // Silent
loadingPosition = false;
});
};
/* -- modal -- */
$scope.openSearchLocationModal = function(options) {
options = options || {};
var parameters = {
text: options.text || $scope.getAddressToSearch(),
results: options.results,
fallbackText: options.fallbackText || $scope.search.location,
forceFallback: angular.isDefined(options.forceFallback) ? options.forceFallback : undefined
};
return ModalUtils.show(
'plugins/es/templates/common/modal_location.html',
'ESSearchPositionModalCtrl',
parameters,
{
focusFirstInput: true
//,scope: $scope
}
);
};
}
function ESSearchPositionModalController($scope, $q, $translate, esGeo, parameters) { function ESSearchPositionModalController($scope, $q, $translate, esGeo, parameters) {
'ngInject'; 'ngInject';
......
angular.module('cesium.es.registry.controllers', ['cesium.es.services', 'cesium.es.common.controllers']) angular.module('cesium.es.registry.controllers', ['cesium.es.services', 'cesium.es.common.controllers'])
.config(function($stateProvider) { .config(function(PluginServiceProvider, $stateProvider) {
'ngInject'; 'ngInject';
PluginServiceProvider
.extendState('app.wot_lookup', {
points: {
'top': {
templateUrl: "plugins/es/templates/registry/wot_lookup_extend.html",
controller: "ESExtensionCtrl"
}
}
});
$stateProvider $stateProvider
.state('app.registry_lookup', { .state('app.registry_lookup', {
url: "/page?q&category&location&type&issuer&reload", url: "/wot/page?q&category&location&type&issuer&reload",
views: { views: {
'menuContent': { 'menuContent': {
templateUrl: "plugins/es/templates/registry/lookup.html", templateUrl: "plugins/es/templates/registry/lookup.html",
...@@ -19,12 +30,15 @@ angular.module('cesium.es.registry.controllers', ['cesium.es.services', 'cesium. ...@@ -19,12 +30,15 @@ angular.module('cesium.es.registry.controllers', ['cesium.es.services', 'cesium.
}) })
.state('app.registry_lookup_lg', { .state('app.registry_lookup_lg', {
url: "/page/lg?q&category&location&type&issuer&reload", url: "/wot/page/lg?q&category&location&type&issuer&reload",
views: { views: {
'menuContent': { 'menuContent': {
templateUrl: "plugins/es/templates/registry/lookup_lg.html", templateUrl: "plugins/es/templates/registry/lookup_lg.html",
controller: 'ESRegistryLookupCtrl' controller: 'ESRegistryLookupCtrl'
} }
},
data: {
silentLocationChange: true
} }
}) })
...@@ -104,10 +118,13 @@ angular.module('cesium.es.registry.controllers', ['cesium.es.services', 'cesium. ...@@ -104,10 +118,13 @@ angular.module('cesium.es.registry.controllers', ['cesium.es.services', 'cesium.
; ;
function ESRegistryLookupController($scope, $focus, $timeout, $filter, function ESRegistryLookupController($scope, $focus, $timeout, $filter, $controller, $location,
UIUtils, ModalUtils, BMA, esModals, esRegistry) { UIUtils, ModalUtils, BMA, csSettings, esModals, esRegistry) {
'ngInject'; 'ngInject';
// Initialize the super class and extend it.
angular.extend(this, $controller('ESLookupPositionCtrl', {$scope: $scope}));
var defaultSearchLimit = 10; var defaultSearchLimit = 10;
$scope.search = { $scope.search = {
...@@ -118,17 +135,18 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -118,17 +135,18 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
type: null, type: null,
category: null, category: null,
location: null, location: null,
options: null, advanced: null,
issuer: null issuer: null,
geoDistance: csSettings.data.plugins.es.geoDistance || "20km"
}; };
$scope.searchTextId = 'registrySearchText'; $scope.searchTextId = 'registrySearchText';
$scope.enter = function(e, state) { $scope.enter = function(e, state) {
if (!$scope.entered || !$scope.search.results || $scope.search.results.length === 0) { if (!$scope.entered || !$scope.search.results || $scope.search.results.length === 0) {
var hasOptions = false; var showAdvanced = false;
var runSearch = false; var runSearch = false;
var finishEntered = function() { var finishEntered = function() {
$scope.search.options = hasOptions ? true : $scope.search.options; // keep null if first call $scope.search.advanced = showAdvanced ? true : $scope.search.advanced; // keep null if first call
if (runSearch) { if (runSearch) {
$timeout(function() { $timeout(function() {
$scope.doSearch(); $scope.doSearch();
...@@ -155,22 +173,22 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -155,22 +173,22 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
runSearch = true; runSearch = true;
} }
// Search on type
if (state.stateParams && state.stateParams.type) {
$scope.search.type = state.stateParams.type;
hasOptions = runSearch = true;
}
// Search on location // Search on location
if (state.stateParams && state.stateParams.location) { if (state.stateParams && state.stateParams.location) {
$scope.search.location = state.stateParams.location; $scope.search.location = state.stateParams.location;
hasOptions = runSearch = true; runSearch = true;
}
// Search on type
if (state.stateParams && state.stateParams.type) {
$scope.search.type = state.stateParams.type;
showAdvanced = runSearch = true;
} }
// Search on issuer // Search on issuer
if (state.stateParams && state.stateParams.issuer) { if (state.stateParams && state.stateParams.issuer) {
$scope.search.issuer = state.stateParams.issuer; $scope.search.issuer = state.stateParams.issuer;
hasOptions = runSearch = true; showAdvanced = runSearch = true;
} }
// Search on category // Search on category
...@@ -178,7 +196,7 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -178,7 +196,7 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
esRegistry.category.get({id: state.stateParams.category}) esRegistry.category.get({id: state.stateParams.category})
.then(function(cat) { .then(function(cat) {
$scope.search.category = cat; $scope.search.category = cat;
hasOptions = runSearch = true; showAdvanced = runSearch = true;
finishEntered(); finishEntered();
}) })
.catch(UIUtils.onError("REGISTRY.ERROR.LOAD_CATEGORY_FAILED")); .catch(UIUtils.onError("REGISTRY.ERROR.LOAD_CATEGORY_FAILED"));
...@@ -194,11 +212,39 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -194,11 +212,39 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
return $scope.enter(e, state); // can be override by sub controller return $scope.enter(e, state); // can be override by sub controller
}); });
$scope.onLocationChanged = function() {
if ($scope.search.loadingPosition) return;
return $scope.search.geoPoint = undefined; // reset geo point
};
$scope.$watch('search.location', $scope.onLocationChanged);
$scope.doSearch = function(from) { $scope.doSearch = function(from) {
$scope.search.loading = !from; $scope.search.loading = !from;
$scope.search.lastRecords = false; $scope.search.lastRecords = false;
if (!$scope.search.options) { if (!$scope.search.advanced) {
$scope.search.options = false; $scope.search.advanced = false;
}
if ($scope.search.location && !$scope.search.geoPoint) {
$scope.search.loadingPosition = true;
return $scope.searchPosition($scope.search.location)
.then(function(res) {
if (!res) {
$scope.search.loading = false;
$scope.search.loadingPosition = false;
return UIUtils.alert.error('REGISTRY.ERROR.GEO_LOCATION_NOT_FOUND');
}
$scope.search.geoPoint = res;
if (res.name) {
$scope.search.location = res.name;
}
console.debug('[page] Search near position:', res);
$scope.search.loadingPosition = false;
// Loop
return $scope.doSearch(from);
});
} }
var text = $scope.search.text.trim(); var text = $scope.search.text.trim();
...@@ -235,13 +281,13 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -235,13 +281,13 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
} }
} }
// issuer: use only on filter // issuer: use only on filter
else if ($scope.search.options && $scope.search.issuer) { else if ($scope.search.advanced && $scope.search.issuer) {
filters.push({term : { issuer: $scope.search.issuer}}); filters.push({term : { issuer: $scope.search.issuer}});
} }
if ($scope.search.options && $scope.search.type) { if ($scope.search.advanced && $scope.search.type) {
filters.push({term: { type: $scope.search.type}}); filters.push({term: { type: $scope.search.type}});
} }
if ($scope.search.options && $scope.search.category) { if ($scope.search.advanced && $scope.search.category) {
filters.push({ filters.push({
nested: { nested: {
path: "category", path: "category",
...@@ -255,8 +301,24 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -255,8 +301,24 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
} }
}); });
} }
if ($scope.search.options && $scope.search.location && $scope.search.location.length > 0) {
filters.push({match_phrase: { city: $scope.search.location}}); if ($scope.search.geoPoint && $scope.search.geoPoint.lat && $scope.search.geoPoint.lon) {
// text match
if ($scope.search.location && $scope.search.location.trim().length > 0) {
//matches.push({match: {location: $scope.search.location}});
//matches.push({prefix: {location: $scope.search.location}});
matches.push({match_phrase: { city: $scope.search.location}});
}
/*matches.push({
geo_distance: {
distance: $scope.search.geoDistance,
geoPoint: {
lat: $scope.search.geoPoint.lat,
lon: $scope.search.geoPoint.lon
}
}});*/
} }
if (matches.length === 0 && filters.length === 0) { if (matches.length === 0 && filters.length === 0) {
...@@ -272,6 +334,10 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -272,6 +334,10 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
query.bool.filter = filters; query.bool.filter = filters;
} }
// Update location href
$scope.updateLocationHref(from);
// Execute the request
$scope.doRequest({query: query, from: from}); $scope.doRequest({query: query, from: from});
}; };
...@@ -280,7 +346,7 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -280,7 +346,7 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
$scope.doSearch(); $scope.doSearch();
} }
}; };
$scope.$watch('search.options', $scope.onToggleOptions, true); $scope.$watch('search.advanced', $scope.onToggleOptions, true);
$scope.doGetLastRecord = function(from) { $scope.doGetLastRecord = function(from) {
$scope.search.lastRecords = true; $scope.search.lastRecords = true;
...@@ -356,9 +422,44 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -356,9 +422,44 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
}); });
}; };
$scope.removeType = function(event) {
$scope.search.type = null;
event.preventDefault();
$scope.doSearch();
};
$scope.removeCategory = function(event) {
$scope.search.category = null;
$scope.category = null;
event.preventDefault();
$scope.doSearch();
};
// Update location href
$scope.updateLocationHref = function(from) {
// Skip when "show more"
if (from) return;
$timeout(function() {
var text = $scope.search.text.trim();
var location = $scope.search.location.trim();
var stateParams = {
q: text && text.length ? text : undefined,
location: location && location.length ? location : undefined,
category: $scope.search.category ? $scope.search.category.id : undefined,
type: $scope.search.type ? $scope.search.type : undefined,
};
$location.search(stateParams).replace();
});
};
/* -- modals -- */ /* -- modals -- */
$scope.showRecordTypeModal = function() { $scope.showRecordTypeModal = function(event) {
$timeout(function() {
if (event.isDefaultPrevented()) return;
ModalUtils.show('plugins/es/templates/registry/modal_record_type.html') ModalUtils.show('plugins/es/templates/registry/modal_record_type.html')
.then(function(type){ .then(function(type){
if (type) { if (type) {
...@@ -366,9 +467,13 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -366,9 +467,13 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
$scope.doSearch(); $scope.doSearch();
} }
}); });
}, 350); // use timeout to allow event to be prevented in removeType()
}; };
$scope.showCategoryModal = function() { $scope.showCategoryModal = function(event) {
$timeout(function() {
if (event.isDefaultPrevented()) return;
// load categories // load categories
esRegistry.category.all() esRegistry.category.all()
.then(function (categories) { .then(function (categories) {
...@@ -382,6 +487,7 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter, ...@@ -382,6 +487,7 @@ function ESRegistryLookupController($scope, $focus, $timeout, $filter,
$scope.doSearch(); $scope.doSearch();
} }
}); });
}, 350); // use timeout to allow event to be prevented in removeCategory()
}; };
$scope.showNewPageModal = function() { $scope.showNewPageModal = function() {
...@@ -412,7 +518,7 @@ function ESWalletPagesController($scope, $controller, $timeout, UIUtils, csWalle ...@@ -412,7 +518,7 @@ function ESWalletPagesController($scope, $controller, $timeout, UIUtils, csWalle
.then(function(walletData) { .then(function(walletData) {
UIUtils.loading.hide(); UIUtils.loading.hide();
$scope.search.issuer = walletData.pubkey; $scope.search.issuer = walletData.pubkey;
$scope.search.options = true; $scope.search.advanced = true;
$timeout($scope.doSearch, 100); $timeout($scope.doSearch, 100);
}); });
} }
...@@ -427,7 +533,7 @@ function ESWalletPagesController($scope, $controller, $timeout, UIUtils, csWalle ...@@ -427,7 +533,7 @@ function ESWalletPagesController($scope, $controller, $timeout, UIUtils, csWalle
$scope.doUpdate = function() { $scope.doUpdate = function() {
if (!csWallet.isLogin()) return; if (!csWallet.isLogin()) return;
$scope.search.issuer = csWallet.data.pubkey; $scope.search.issuer = csWallet.data.pubkey;
$scope.search.options = true; $scope.search.advanced = true;
return $scope.doSearch(); return $scope.doSearch();
}; };
......
...@@ -46,7 +46,8 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt ...@@ -46,7 +46,8 @@ angular.module('cesium.es.settings.services', ['cesium.services', 'cesium.es.htt
googleApiKey: undefined, googleApiKey: undefined,
wot: { wot: {
enableMixedSearch: true enableMixedSearch: true
} },
geoDistance: '20km'
} }
} }
}, {plugins: {es: csConfig.plugins && csConfig.plugins.es || {}}}), }, {plugins: {es: csConfig.plugins && csConfig.plugins.es || {}}}),
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
REGISTRY.SEARCH.LAST_RECORDS REGISTRY.SEARCH.LAST_RECORDS
</h4> </h4>
<h4 <h4
ng-if="!search.loading && !search.lastRecords && (search.text.length || search.options != null)" translate> ng-if="!search.loading && !search.lastRecords && (search.text.length || search.advanced != null)" translate>
COMMON.RESULTS_LIST COMMON.RESULTS_LIST
</h4> </h4>
</div> </div>
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
<ion-spinner icon="android"></ion-spinner> <ion-spinner icon="android"></ion-spinner>
</div> </div>
<div class="padding assertive" ng-if="!search.loading && search.results.length===0 && search.options != null" translate> <div class="padding assertive" ng-if="!search.loading && search.results.length===0 && search.advanced != null" translate>
COMMON.SEARCH_NO_RESULT COMMON.SEARCH_NO_RESULT
</div> </div>
......
...@@ -6,11 +6,30 @@ ...@@ -6,11 +6,30 @@
<ion-content class="lookupForm padding no-padding-xs"> <ion-content class="lookupForm padding no-padding-xs">
<button class="button button-small button-positive button-clear ink pull-right padding-right hidden-sm hidden-xs" <div class="item no-border " style="display: block; height: 60px;">
<div class="pull-right">
<button class="button button-small button-positive button-clear ink hidden-sm hidden-xs"
ng-click="showNewPageModal()"> ng-click="showNewPageModal()">
<i class="icon ion-plus"></i> <i class="icon ion-plus"></i>
{{'REGISTRY.BTN_NEW' | translate}} {{'REGISTRY.BTN_NEW' | translate}}
</button> </button>
<div
class="button dark"
title="{{'REGISTRY.BTN_SHOW_WOT_HELP' | translate}}"
ui-sref="app.wot_lookup">
<i class="icon ion-person-stalker"></i>
{{'REGISTRY.BTN_SHOW_WOT' | translate}}
</div>
<div
class="button ink yellow-bg dark"
title="{{'REGISTRY.BTN_SHOW_PAGES_HELP' | translate}}"
>
<i class="icon ion-social-buffer"></i>
{{'REGISTRY.BTN_SHOW_PAGES' | translate}}
</div>
</div>
</div>
<form ng-submit="doSearch()"/> <form ng-submit="doSearch()"/>
<label class="item item-input"> <label class="item item-input">
...@@ -29,24 +48,59 @@ ...@@ -29,24 +48,59 @@
on-return="doSearch()"> on-return="doSearch()">
</label> </label>
<!-- location -->
<div class="item no-padding" >
<div class="item-input ">
<i class="icon ion-location placeholder-icon"></i>
<input type="text"
class="visible-xs visible-sm"
placeholder="{{(options.location.help||'REGISTRY.SEARCH.LOCATION_HELP')|translate}}"
ng-model="search.location"
ng-model-options="{ debounce: 350 }"
ng-change="doSearch()">
<input type="text"
class="hidden-xs hidden-sm"
placeholder="{{(options.location.help||'REGISTRY.SEARCH.LOCATION_HELP')|translate}}"
ng-model="search.location"
on-return="doSearch()">
</div>
</div>
<!-- options --> <!-- options -->
<ng-include src="'plugins/es/templates/registry/lookup_options.html'"></ng-include> <ng-include src="'plugins/es/templates/registry/lookup_options.html'"></ng-include>
</form> </form>
<div class="padding-top padding-xs" style="display: block; height: 60px;"> <div class="padding-top padding-xs" style="display: block; height: 60px;">
<div class="pull-left">
<h4 <div class="hidden-xs hidden-sm pull-left" >
ng-if="!search.loading && search.lastRecords && search.results.length" translate>
REGISTRY.SEARCH.LAST_RECORDS <a class="button button-text button-small ink "
</h4> ng-class="{'button-text-stable': !search.advanced, 'button-text-positive': search.advanced}"
<h4 ng-click="search.advanced=!search.advanced">
ng-if="!search.loading && !search.lastRecords && (search.text.length || search.options != null)" translate> <i class="icon ion-ios-gear"></i>
COMMON.RESULTS_LIST {{'REGISTRY.SEARCH.BTN_OPTIONS' | translate}}
</h4> <i class="icon" ng-class="{'ion-arrow-down-b': !search.advanced, 'ion-arrow-up-b': search.advanced}"></i>
</a>
&nbsp;
</div> </div>
<div class="hidden-xs hidden-sm pull-right"> <div class="hidden-xs hidden-sm pull-right">
<a class="button button-text button-small ink hidden-sm hidden-xs"
title="{{'MAP.REGISTRY.LOOKUP.BTN_MAP_HELP' | translate}}"
ui-sref="app.view_registry_map">
<i class="icon ion-ios-location"></i>
{{'MAP.REGISTRY.LOOKUP.BTN_MAP' | translate}}
</a>
<!-- Allow extension here -->
<cs-extension-point name="filter-buttons"></cs-extension-point>
&nbsp;
<button class="button button-small button-stable ink" <button class="button button-small button-stable ink"
ng-click="doSearch()"> ng-click="doSearch()">
{{'COMMON.BTN_SEARCH' | translate}} {{'COMMON.BTN_SEARCH' | translate}}
...@@ -54,11 +108,24 @@ ...@@ -54,11 +108,24 @@
</div> </div>
</div> </div>
<div class="padding-xs" style="display: block; height: 60px;">
<div class="pull-left">
<h4
ng-if="!search.loading && search.lastRecords && search.results.length" translate>
REGISTRY.SEARCH.LAST_RECORDS
</h4>
<h4
ng-if="!search.loading && !search.lastRecords && (search.text.length || search.advanced != null)" translate>
COMMON.RESULTS_LIST
</h4>
</div>
</div>
<div class="center" ng-if="search.loading"> <div class="center" ng-if="search.loading">
<ion-spinner icon="android"></ion-spinner> <ion-spinner icon="android"></ion-spinner>
</div> </div>
<div class="padding assertive" ng-if="!search.loading && search.results.length===0 && search.options != null" translate> <div class="padding assertive" ng-if="!search.loading && search.results.length===0 && search.advanced != null" translate>
COMMON.SEARCH_NO_RESULT COMMON.SEARCH_NO_RESULT
</div> </div>
...@@ -81,6 +148,8 @@ ...@@ -81,6 +148,8 @@
</ion-infinite-scroll> </ion-infinite-scroll>
</ion-content> </ion-content>
<button id="fab-add-registry-record" <button id="fab-add-registry-record"
class="button button-fab button-fab-bottom-right button-assertive icon ion-plus hidden-md hidden-lg spin" class="button button-fab button-fab-bottom-right button-assertive icon ion-plus hidden-md hidden-lg spin"
ng-click="showNewPageModal()"> ng-click="showNewPageModal()">
......
<div class="item item-toggle dark" ng-if="!search.lastRecords"> <div class="item item-input item-select" ng-if="search.advanced">
<span translate>REGISTRY.SEARCH.BTN_OPTIONS</span> <div class="input-label">
<label class="toggle toggle-royal"> <span class="input-label" translate>COMMON.GEO.DISTANCE</span>
<input type="checkbox" ng-model="search.options">
<div class="track">
<div class="handle"></div>
</div> </div>
<label>
<select ng-model="search.geoDistance"
ng-options="i as (geoDistanceLabels[i].labelKey | translate:geoDistanceLabels[i].labelParams ) for i in geoDistances track by i">
</select>
</label> </label>
</div> </div>
<div class="item item-icon-right" ng-if="search.options" ng-click="showRecordTypeModal()"> <div class="item item-icon-right" ng-if="search.advanced" ng-click="showRecordTypeModal($event)">
<span translate>REGISTRY.SEARCH.TYPE</span> <span translate>REGISTRY.SEARCH.TYPE</span>
<span class="badge badge-balanced" ng-if="search.type"> <div class="badge badge-balanced" ng-if="search.type">
<i class="cion-page-{{search.type}}"></i> <i class="cion-page-{{search.type}}"></i>
{{'REGISTRY.TYPE.ENUM.'+search.type|uppercase|translate}} {{'REGISTRY.TYPE.ENUM.'+search.type|uppercase|translate}}
</span> <i class="ion-close" ng-click="removeType($event)">&nbsp;&nbsp;</i>
</div>
<i class="gray icon ion-ios-arrow-right"></i> <i class="gray icon ion-ios-arrow-right"></i>
</div> </div>
<span class="item item-icon-right" ng-click="showCategoryModal()" ng-if="search.options"> <div class="item item-icon-right" ng-click="showCategoryModal($event)" ng-if="search.advanced">
<span translate>REGISTRY.CATEGORY</span> <span translate>REGISTRY.CATEGORY</span>
<span class="badge badge-royal badge-text-wrap" ng-bind-html="search.category.name"></span>&nbsp; <div class="badge badge-royal badge-text-wrap" ng-if="search.category">
<span ng-bind-html="search.category.name"></span>
<i class="ion-close" ng-click="removeCategory($event)">&nbsp;&nbsp;</i>
</div>&nbsp;
<i class="gray icon ion-ios-arrow-right"></i> <i class="gray icon ion-ios-arrow-right"></i>
</span> </span>
<span class="item item-button-right" ng-if="(search.options) && !location.enable "> <div class="item item-input item-select" ng-if="search.advanced && options.location.show">
<span translate>REGISTRY.SEARCH.LOCATION</span> <div class="input-label">
<div class="item-input-inset"> <span class="input-label" translate>MARKET.SEARCH.GEO_DISTANCE</span>
<label class="item-input-wrapper"> </div>
<input type="text" placeholder="{{'REGISTRY.SEARCH.LOCATION_HELP'|translate}}"
ng-model="search.location" <label>
ng-model-options="{ debounce: 350 }" <select ng-model="search.geoDistance"
ng-change="doSearch()"/> ng-options="i as (geoDistanceLabels[i].labelKey | translate:geoDistanceLabels[i].labelParams ) for i in geoDistances track by i">
</select>
</label> </label>
</div> </div>
</span>
<div class="item no-border " style="display: block; height: 60px;" ng-if="enable">
<div class="pull-right">
<div
class="button dark"
title="{{'REGISTRY.BTN_SHOW_WOT_HELP' | translate}}">
<i class="icon ion-person-stalker"></i>
{{'REGISTRY.BTN_SHOW_WOT' | translate}}
</div>
<div
class="button ink yellow-bg dark"
title="{{'REGISTRY.BTN_SHOW_PAGES_HELP' | translate}}"
ui-sref="app.registry_lookup">
<i class="icon ion-social-buffer"></i>
{{'REGISTRY.BTN_SHOW_PAGES' | translate}}
</div>
</div>
</div>
...@@ -90,7 +90,7 @@ ...@@ -90,7 +90,7 @@
} }
/** /**
* WOT MAP * WOT map
*/ */
.view-map-wot .leaflet-popup-content{ .view-map-wot .leaflet-popup-content{
min-width: 200px; min-width: 200px;
...@@ -103,6 +103,19 @@ ...@@ -103,6 +103,19 @@
padding-left: 70px; padding-left: 70px;
} }
/**
* registry map
*/
.view-map-registry .leaflet-popup-content{
min-width: 200px;
}
.view-map-registry .leaflet-popup-content .item-avatar .item-image {
left: 0px;
}
.view-map-registry .leaflet-popup-content .item-avatar .item-content {
padding-left: 70px;
}
/** /**
* Marker colors * Marker colors
......
...@@ -39,6 +39,15 @@ ...@@ -39,6 +39,15 @@
"LOAD_POSITION_FAILED": "Impossible de récupérer les positions à afficher." "LOAD_POSITION_FAILED": "Impossible de récupérer les positions à afficher."
} }
}, },
"REGISTRY": {
"LOOKUP": {
"BTN_MAP": "Carte",
"BTN_MAP_HELP": "Ouvrir la carte des pages"
},
"VIEW": {
"TITLE": "Carte des pages"
}
},
"PROFILE": { "PROFILE": {
"MARKER_HELP": "<b>Glissez-déposez</b> ce marqueur pour <b>mettre<br/>à jour votre position</b> sur la carte, ou utilisez le bouton<br/>de recherche au dessus de la carte." "MARKER_HELP": "<b>Glissez-déposez</b> ce marqueur pour <b>mettre<br/>à jour votre position</b> sur la carte, ou utilisez le bouton<br/>de recherche au dessus de la carte."
}, },
......
angular.module('cesium.map.registry.controllers', ['cesium.services', 'cesium.map.services', 'cesium.map.help.controllers'])
.config(function($stateProvider, PluginServiceProvider, csConfig) {
'ngInject';
var enable = csConfig.plugins && csConfig.plugins.es;
if (enable) {
PluginServiceProvider
.extendState('app.registry_lookup', {
points: {
'filter-buttons': {
templateUrl: "plugins/map/templates/registry/lookup_extend.html",
controller: "ESExtensionCtrl"
}
}
});
$stateProvider
.state('app.view_registry_map', {
url: "/wot/pagemap?c&center",
views: {
'menuContent': {
templateUrl: "plugins/map/templates/registry/view_map.html",
controller: 'MapRegistryViewCtrl'
}
},
// Seems to works without cache ??
//cache: false,
data: {
silentLocationChange: true
}
});
}
})
// Map view of the registry
.controller('MapRegistryViewCtrl', MapRegistryViewController)
;
function MapRegistryViewController($scope, $filter, $templateCache, $interpolate, $timeout, $location, $translate, $q,
leafletData, UIUtils, csSettings, csWallet, MapUtils, mapRegistry) {
'ngInject';
var
// Create a hidden layer, to hold search markers
markersSearchLayer,
icons= {
group: {
type: 'awesomeMarker',
icon: 'person-stalker',
markerColor: 'green'
},
shop: {
type: 'awesomeMarker',
icon: 'page-shop',
markerColor: 'green'
},
association: {
type: 'awesomeMarker',
icon: 'page-association',
markerColor: 'green'
},
company: {
type: 'awesomeMarker',
icon: 'page-company',
markerColor: 'green'
},
institution: {
type: 'awesomeMarker',
icon: 'page-institution',
markerColor: 'green'
}
};
$scope.loading = true;
$scope.mapId = 'map-wot-' + $scope.$id;
$scope.map = MapUtils.map({
cache: 'map-registry',
layers: {
overlays: {
// Pages
shop: {
type: 'featureGroup',
name: 'MAP.WOT.VIEW.LAYER.SHOP',
visible: true
},
association: {
type: 'featureGroup',
name: 'MAP.WOT.VIEW.LAYER.ASSOCIATION',
visible: true
},
company: {
type: 'featureGroup',
name: 'MAP.WOT.VIEW.LAYER.COMPANY',
visible: true
},
institution: {
type: 'featureGroup',
name: 'MAP.WOT.VIEW.LAYER.INSTITUTION',
visible: true
}
}
},
bounds: {},
markers: {},
loading: true
}, $scope.mapId);
// [NEW] When opening the view
$scope.enter = function(e, state) {
if ($scope.loading) {
if (state.stateParams && state.stateParams.c) {
var cPart = state.stateParams.c.split(':');
$scope.map.center.lat = parseFloat(cPart[0]);
$scope.map.center.lng = parseFloat(cPart[1]);
$scope.map.center.zoom = parseInt(cPart[2]);
}
$scope.$watch("map.center", function() {
if (!$scope.loading) {
return $timeout(function() {
$scope.updateLocationHref();
}, 300);
}
}, true);
// Load the map (and init if need)
$scope.loadMap()
.then(function() {
if (csWallet.isLogin()) {
$scope.showHelpTip();
}
return $scope.load();
});
}
else {
// Make sure to have previous center coordinate defined in the location URL
$scope.updateLocationHref();
if (csWallet.isLogin()) {
$scope.showHelpTip();
}
}
};
$scope.$on('$ionicView.enter', $scope.enter);
$scope.loadMap = function() {
return $q.all([
$translate(['COMMON.BTN_HELP_TOUR_SCREEN', 'COMMON.BTN_REFRESH', 'MAP.COMMON.BTN_LOCALIZE_ME']),
leafletData.getMap($scope.mapId)
]).then(function(res) {
var translations = res[0];
var map = res[1];
if (!$scope.map.loading) return map; // already loaded
if (!UIUtils.screen.isSmall()) {
// Add a start tour button
/*L.easyButton({
position: 'topright', // inherited from L.Control -- the corner it goes in
type: 'replace', // set to animate when you're comfy with css
leafletClasses: true, // use leaflet classes to style the button?
states:[{ // specify different icons and responses for your button
stateName: 'show-help-tour',
onClick: $scope.startHelpTour,
title: translations['COMMON.BTN_HELP_TOUR_SCREEN'],
icon: 'icon ion-easel'
}]
}
).addTo(map);*/
// Add a refresh button
L.easyButton({
position: 'topright', // inherited from L.Control -- the corner it goes in
type: 'replace', // set to animate when you're comfy with css
leafletClasses: true, // use leaflet classes to style the button?
states:[{ // specify different icons and responses for your button
stateName: 'refresh',
onClick: function(btn, map){
return $scope.load(map);
},
title: translations['COMMON.BTN_REFRESH'],
icon: 'icon ion-refresh'
}]
}
).addTo(map);
}
// Add loading control
L.Control.loading({
position: 'topright',
separate: true
}).addTo(map);
// Add localize me control
MapUtils.control.localizeMe({
title: translations['MAP.COMMON.BTN_LOCALIZE_ME']
})
.addTo(map);
// Add search control
markersSearchLayer = L.layerGroup({visible: false});
var searchTip = $interpolate($templateCache.get('plugins/map/templates/registry/item_search_tooltip.html'));
MapUtils.control.search({
layer: markersSearchLayer,
propertyName: 'title',
buildTip: function (text, val) {
return searchTip(val.layer.options);
},
moveToLocation: function(lnglat, title, map) {
if(this.options.zoom)
this._map.setView(lnglat, this.options.zoom);
else
this._map.panTo(lnglat);
var popupMarkerId = lnglat.layer && lnglat.layer.options && lnglat.layer.options.popupMarkerId;
$timeout(function(){
var popupMarker = popupMarkerId && _.find(map._layers, function(layer) {
return (layer.options && layer.options.id === popupMarkerId);
});
popupMarker && popupMarker.openPopup();
}, 400);
},
firstTipSubmit: true,
tooltipLimit: 50
}).addTo(map);
// Add marker cluster layer
var extractMarkerLayer = function(marker) {
return marker.options && marker.options.layer;
};
var markerClusterLayer = L.markerClusterGroup({
disableClusteringAtZoom: MapUtils.constants.LOCALIZE_ZOOM,
maxClusterRadius: 65,
showCoverageOnHover: false,
iconCreateFunction: function (cluster) {
var countByLayer = _.countBy(cluster.getAllChildMarkers(), extractMarkerLayer);
var markerColor = countByLayer.member ? 'blue' : (countByLayer.pending ? 'lightgreen' : 'lightgray');
var childCount = cluster.getChildCount();
var className = 'marker-cluster ' + markerColor + ' marker-cluster-';
if (childCount < 10) {
className += 'small';
} else if (childCount < 100) {
className += 'medium';
} else {
className += 'large';
}
return L.divIcon({ html: '<div><span>' + childCount + '</span></div>', className: className, iconSize: new L.Point(40, 40) });
}
});
map.eachLayer(function(layer) {
// Add capabilities of 'featureGroup.subgroup', if layer is a group
if (layer.addLayer){
angular.extend(layer, L.featureGroup.subGroup(markerClusterLayer));
}
});
markerClusterLayer.addTo(map);
// Bind map with options (e.g. to received overlays visibility updates)
// Cache no more need, as view is not cached
//MapUtils.cache.bind($scope, $scope.mapId, $scope.map);
$scope.map.loading = false;
return map;
});
};
// Load markers data
$scope.load = function(map) {
if (!map) {
return leafletData.getMap($scope.mapId)
// loop with the map object
.then($scope.load);
}
$scope.loading = true;
// Show loading indicator
map.fire('dataloading');
var options = {
fields: {
description: !UIUtils.screen.isSmall()
}
};
// Load wot data, from service
return mapRegistry.load(options)
.then(function(res) {
var markers = {};
// Clean search layer
markersSearchLayer.clearLayers();
if (res && res.length) {
var formatPubkey = $filter('formatPubkey');
var userMarkerTemplate = $templateCache.get('plugins/map/templates/wot/popup_marker.html');
var pageMarkerTemplate = $templateCache.get('plugins/map/templates/wot/popup_page_marker.html');
_.forEach(res, function (hit) {
var type = hit.type || (hit.pending ? 'pending' : (hit.uid ? 'member' : 'wallet'));
var shortPubkey = formatPubkey(hit.pubkey);
var id = hit.index + '_' + (hit.id || (hit.uid ? (hit.uid + ':' + hit.pubkey) : hit.pubkey)).replace(/-/g, '_');
var marker = {
layer: type,
icon: icons[type],
opacity: hit.uid || hit.type ? 1 : 0.7,
title: hit.name + ' | ' + shortPubkey,
lat: hit.geoPoint.lat,
lng: hit.geoPoint.lon,
getMessageScope: function () {
var scope = $scope.$new();
scope.hit = hit;
return scope;
},
focus: false,
message: hit.type ? pageMarkerTemplate : userMarkerTemplate,
id: id
};
markers[id] = marker;
// Create a search marker (will be hide)
var searchText = hit.name + ((hit.uid && hit.uid != hit.name) ? (' | ' + hit.uid) : '') + ' | ' + shortPubkey;
var searchMarker = angular.merge({
type: type,
opacity: 0,
icon: L.divIcon({
className: type + ' ng-hide',
iconSize: L.point(0, 0)
})
}, {title: searchText, pubkey: hit.pubkey, uid: hit.uid, name: hit.name, pending: hit.pending, popupMarkerId: id});
markersSearchLayer.addLayer(new L.Marker({
lat: hit.geoPoint.lat,
lng: hit.geoPoint.lon
},
searchMarker));
});
}
$scope.map.markers = markers;
$scope.loading = false;
// hide loading indicator
map.fire('dataload');
})
.catch(function(err) {
$scope.map.markers = {};
$scope.loading = false;
UIUtils.onError('MAP.WOT.ERROR.LOAD_POSITION_FAILED')(err);
});
};
// Update the browser location, to be able to refresh the page
$scope.updateLocationHref = function(centerHash) {
// removeIf(device)
var params = $location.search() || {};
if (!params.c || !MapUtils.center.isDefault($scope.map.center)) {
centerHash = centerHash || '{0}:{1}:{2}'.format($scope.map.center.lat.toFixed(4), $scope.map.center.lng.toFixed(4), $scope.map.center.zoom);
$location.search({c: centerHash}).replace();
}
// endRemoveIf(device)
};
// removeIf(device)
// Update the browser location, to be able to refresh the page
// FIXME: not need, should be removed
$scope.$on("centerUrlHash", function(event, centerHash) {
if (!$scope.loading) {
return $timeout(function() {
$scope.updateLocationHref(centerHash);
}, 300);
}
});
// endRemoveIf(device)
/* -- help tip -- */
// Show help tour
$scope.startHelpTour = function() {
return $scope.showHelpTip(0, true);
};
// Show help tip
$scope.showHelpTip = function(index, isTour) {
index = angular.isDefined(index) ? index :
(angular.isNumber(csSettings.data.helptip.mapwot) ? csSettings.data.helptip.mapwot : 0);
isTour = angular.isDefined(isTour) ? isTour : false;
if (index < 0 || index > 2/*max steps*/) return;
// Create a new scope for the tour controller
var helptipScope = $scope.createHelptipScope(isTour, 'MapHelpTipCtrl');
if (!helptipScope) return; // could be undefined, if a global tour already is already started
// Set isTour and mapId
helptipScope.tour = isTour;
helptipScope.mapId = $scope.mapId;
return helptipScope.startMapWotTour(index, false)
.then(function(endIndex) {
helptipScope.$destroy();
csSettings.data.helptip.mapwot = angular.isNumber(csSettings.data.helptip.mapwot) ?
Math.max(endIndex, csSettings.data.helptip.mapwot) :
endIndex;
csSettings.store();
});
};
}
...@@ -5,6 +5,7 @@ angular.module('cesium.map.plugin', [ ...@@ -5,6 +5,7 @@ angular.module('cesium.map.plugin', [
'cesium.map.services', 'cesium.map.services',
// Controllers // Controllers
'cesium.map.wot.controllers', 'cesium.map.wot.controllers',
'cesium.map.registry.controllers',
'cesium.map.network.controllers', 'cesium.map.network.controllers',
'cesium.map.user.controllers', 'cesium.map.user.controllers',
'cesium.map.settings.controllers', 'cesium.map.settings.controllers',
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
angular.module('cesium.map.services', [ angular.module('cesium.map.services', [
// Services // Services
'cesium.map.wot.services', 'cesium.map.wot.services',
'cesium.map.registry.services',
'cesium.map.utils.services' 'cesium.map.utils.services'
]) ])
; ;
angular.module('cesium.map.registry.services', ['cesium.services'])
.factory('mapRegistry', function($q, csHttp, esHttp, esSettings, csWot, BMA, esGeo) {
'ngInject';
var
that = this,
constants = {
DEFAULT_LOAD_SIZE: 1000
},
fields = {
record: ["title", "geoPoint", "avatar._content_type", "address", "city", "type", "pubkey", "issuer", "category"]
};
that.raw = {
profile: {
search: esHttp.post('/page/record/_search'),
mixedSearch: esHttp.post('/user,page,group/profile,record/_search')
}
};
function createFilterQuery(options) {
options = options || {};
var query = {
bool: {}
};
// Limit to profile with geo point
if (options.searchAddress) {
query.bool.should = [
{exists: {field: "geoPoint"}},
{exists: {field: "city"}}
];
}
else {
query.bool.must= [
{exists: {field: "geoPoint"}}
];
}
// Filter on bounding box
// see https://www.elastic.co/guide/en/elasticsearch/reference/2.4/geo-point.html
if (options.bounds && options.bounds.northEast && options.bounds.southWest) {
query.bool.should = query.bool.should || {};
query.bool.should.geo_bounding_box = {
"geoPoint" : {
"top_left" : {
"lat" : Math.max(Math.min(options.bounds.northEast.lat, 90), -90),
"lon" : Math.max(Math.min(options.bounds.southWest.lng, 180), -180)
},
"bottom_right" : {
"lat" : Math.max(Math.min(options.bounds.southWest.lat, 90), -90),
"lon" : Math.max(Math.min(options.bounds.northEast.lng, 180), -180)
}
}
};
}
return query;
}
function load(options) {
options = options || {};
options.from = options.from || 0;
options.size = options.size || constants.DEFAULT_LOAD_SIZE;
options.searchAddress = esGeo.google.isEnable() && (angular.isDefined(options.searchAddress) ? options.searchAddress : true);
options.fields = options.fields || {};
options.fields.description = angular.isDefined(options.fields.description) ? options.fields.description : true;
var request = {
query: createFilterQuery(options),
from: 0,
size: options.size,
_source: options.fields.description ? fields.record.concat("description") : fields.record
};
// Search on profiles ?
var mixedSearch = false;
/*var mixedSearch = esSettings.registry.isMixedSearchEnable();
if (mixedSearch) {
console.debug("[ES] [map] Mixed search: enable");
}*/
var search = mixedSearch ? that.raw.profile.mixedSearch : that.raw.profile.search;
return $q.all([
search(request),
BMA.wot.member.uids(),
BMA.wot.member.pending()
.then(function(res) {
return (res.memberships && res.memberships.length) ? res.memberships : [];
})
])
.then(function(res) {
var uids = res[1];
var memberships = res[2];
var res = res[0];
if (!res.hits || !res.hits.total) return [];
// Transform pending MS into a map by pubkey
memberships = memberships.reduce(function(res, ms){
if (ms.membership == 'IN' && !uids[ms.pubkey]) {
var idty = {
uid: ms.uid,
pubkey: ms.pubkey,
block: ms.blockNumber,
blockHash: ms.blockHash,
pending: true
};
var otherIdtySamePubkey = res[ms.pubkey];
if (otherIdtySamePubkey && idty.block > otherIdtySamePubkey.block) {
return res; // skip
}
res[idty.pubkey] = idty;
}
return res;
}, {});
var jobs = [
processLoadHits(options, uids, memberships, res)
];
// Additional slice requests
request.from += request.size;
while (request.from < res.hits.total) {
jobs.push(search(angular.copy(request))
.then(function(res) {
if (!res.hits || !res.hits.hits.length) return [];
return processLoadHits(options, uids, memberships, res);
}));
request.from += request.size;
}
return $q.all(jobs)
.then(function(res){
return res.reduce(function(res, items) {
return res.concat(items);
}, []);
});
});
}
function processLoadHits(options, uids, memberships, res) {
// Transform profile hits
var commaRegexp = new RegExp('[,]');
var searchAddressItems = [];
var items = res.hits.hits.reduce(function(res, hit) {
var item;
if (hit._index == "user") {
var pubkey = hit._id;
var uid = uids[pubkey];
item = uid && {uid: uid} || memberships[pubkey] || {};
item.pubkey = pubkey;
item.index = hit._index;
}
else {
var pubkey = hit._source.issuer;
var uid = uids[pubkey];
item = uid && {uid: uid} || memberships[pubkey] || {};
item.issuer = pubkey;
item.pubkey = hit._source.pubkey||item.issuer;
item.id = hit._id;
item.index = hit._index;
item.type = hit._source.type;
item.category = hit._source.category;
if (item.category) {
delete item.category.parent; // parent not need
}
}
// City & address
item.city = hit._source.city;
item.address = hit._source.address;
// Set geo point
item.geoPoint = hit._source.geoPoint;
if (!item.geoPoint || !item.geoPoint.lat || !item.geoPoint.lon) {
if (!options.searchAddress || !item.city) return res; // no city: exclude this item
item.searchAddress = item.city && ((hit._source.address ? hit._source.address+ ', ' : '') + item.city);
searchAddressItems.push(item);
}
else {
// Convert lat/lon to float (if need)
if (item.geoPoint.lat && typeof item.geoPoint.lat === 'string') {
item.geoPoint.lat = parseFloat(item.geoPoint.lat.replace(commaRegexp, '.'));
}
if (item.geoPoint.lon && typeof item.geoPoint.lon === 'string') {
item.geoPoint.lon = parseFloat(item.geoPoint.lon.replace(commaRegexp, '.'));
}
}
// Avatar
item.avatar = esHttp.image.fromHit(hit, 'avatar');
// Name
item.name = hit._source.title;
// Avoid too long name (workaround for #308)
if (item.name && item.name.length > 30) {
item.name = item.name.substr(0, 27) + '...';
}
// Description
item.description = hit._source.description && esHttp.util.parseAsHtml(hit._source.description);
return item.geoPoint ? res.concat(item) : res;
}, []);
// Resolve missing positions by addresses (only if google API enable)
if (searchAddressItems.length) {
var now = new Date().getTime();
console.debug('[map] [registry] Search positions of {0} addresses...'.format(searchAddressItems.length));
var counter = 0;
return $q.all(searchAddressItems.reduce(function(res, item) {
return !item.city ? res : res.concat(esGeo.google.searchByAddress(item.searchAddress)
.then(function(res) {
if (!res || !res.length) return;
item.geoPoint = res[0];
// If search on city, add a randomized delta to avoid superposition
if (item.city == item.searchAddress) {
item.geoPoint.lon += Math.random() / 1000;
item.geoPoint.lat += Math.random() / 1000;
}
delete item.searchAddress; // not need anymore
items.push(item);
counter++;
})
.catch(function() {/*silent*/}));
}, []))
.then(function(){
console.debug('[map] [registry] Resolved {0}/{1} addresses in {2}ms'.format(counter, searchAddressItems.length, new Date().getTime()-now));
return items;
});
}
return $q.when(items);
}
return {
load: load
};
});
<a href="#" class="{{type}}">
<i class="{{pending ? 'ion-clock': (type ? 'cion-page-' + type : '')}}"></i>
{{name != uid ? name +' ' : ''}}
<span class="{{uid ? 'positive' : 'gray'}}">
<i class="{{uid ? 'ion-person' : 'ion-key'}}"></i>
{{uid ? uid : (pubkey|formatPubkey) }}
</span>
</a>
<a ng-if="enable"
class="button button-text button-small ink hidden-sm hidden-xs"
title="{{'MAP.REGISTRY.LOOKUP.BTN_MAP_HELP' | translate}}"
ui-sref="app.view_registry_map">
<i class="icon ion-ios-location"></i>
{{'MAP.REGISTRY.LOOKUP.BTN_MAP' | translate}}
</a>
<ion-view left-buttons="leftButtons" class="view-map-registry">
<ion-nav-title>
<span class="hidden-xs" translate>MAP.REGISTRY.VIEW.TITLE</span>
</ion-nav-title>
<ion-nav-buttons side="secondary">
<button class="button button-icon button-clear icon ion-loop visible-xs visible-sm" ng-click="load()">
</button>
</ion-nav-buttons>
<ion-content data-tap-disabled="true">
<a id="helptip-map-registry" style="left: 150px; top: 50px; position: relative;"></a>
<leaflet id="{{::mapId}}"
height="100%"
layers="map.layers"
markers="map.markers"
lf-center="map.center"
bounds="map.bounds">
</leaflet>
</ion-content>
</ion-view>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment