diff --git a/contribs/gmf/src/disclaimer/component.js b/contribs/gmf/src/disclaimer/component.js index 8ec1402143b2..99f0c896ee64 100644 --- a/contribs/gmf/src/disclaimer/component.js +++ b/contribs/gmf/src/disclaimer/component.js @@ -24,19 +24,6 @@ const module = angular.module('gmfDisclaimer', [ ]); -/** - * - * @param {import("ol/layer/Base.js").default} layer Layer - * @param {function(string):void} func Function - */ -function forEachDisclaimer(layer, func) { - const disclaimers = layer.get('disclaimers'); - if (disclaimers && Array.isArray(disclaimers)) { - disclaimers.forEach(func); - } -} - - /** * Used metadata: * @@ -219,30 +206,23 @@ DisclaimerController.prototype.registerLayer_ = function(layer) { if (this.layerVisibility) { // Show disclaimer messages for this layer - forEachDisclaimer(layer, (disclaimer) => { - if (layer.getVisible()) { - this.showDisclaimerMessage_(disclaimer); - } - }); + if (layer.getVisible()) { + this.update_(layer); + } else { + this.closeAll_(layer); + } - const listenerKey = olEvents.listen(layer, 'change:visible', (event) => { - const layer = event.target; + const listenerKey = olEvents.listen(layer, 'change', (event) => { if (layer.getVisible()) { - forEachDisclaimer(layer, (disclaimer) => { - this.showDisclaimerMessage_(disclaimer); - }); + this.update_(layer); } else { - forEachDisclaimer(layer, (disclaimer) => { - this.closeDisclaimerMessage_(disclaimer); - }); + this.closeAll_(layer); } }); this.eventHelper_.addListenerKey(layerUid, listenerKey); } else { // Show disclaimer messages for this layer - forEachDisclaimer(layer, (disclaimer) => { - this.showDisclaimerMessage_(disclaimer); - }); + this.showAll_(layer); } } }; @@ -265,11 +245,8 @@ DisclaimerController.prototype.unregisterLayer_ = function(layer) { layer.getLayers().forEach(layer => this.unregisterLayer_(layer)); } else { - - // Close disclaimer messages for this layer - forEachDisclaimer(layer, (disclaimer) => { - this.closeDisclaimerMessage_(disclaimer); - }); + // Close all disclaimer messages for this layer + this.closeAll_(layer); } }; @@ -281,10 +258,11 @@ DisclaimerController.prototype.$onDestroy = function() { /** + * @param {string} layerUid Layer identifier. * @param {string} msg Disclaimer message. * @private */ -DisclaimerController.prototype.showDisclaimerMessage_ = function(msg) { +DisclaimerController.prototype.showDisclaimerMessage_ = function(layerUid, msg) { msg = this.gettextCatalog_.getString(msg); if (this.external) { if (this.msgs_.indexOf(msg) < 0) { @@ -296,6 +274,7 @@ DisclaimerController.prototype.showDisclaimerMessage_ = function(msg) { this.disclaimer_.alert({ popup: this.popup, msg: msg, + layerUid: layerUid, target: this.element_, type: MessageType.WARNING }); @@ -304,10 +283,73 @@ DisclaimerController.prototype.showDisclaimerMessage_ = function(msg) { /** + * @param {import("ol/layer/Base.js").default} layer Layer + * @private + */ +DisclaimerController.prototype.closeAll_ = function(layer) { + const disclaimers = layer.get('disclaimers'); + if (disclaimers) { + const layerUid = olUtilGetUid(layer); + for (const key in disclaimers) { + const uid = `${layerUid}-${key}`; + this.closeDisclaimerMessage_(uid, disclaimers[key]); + } + } +}; + + +/** + * @param {import("ol/layer/Base.js").default} layer Layer + * @private + */ +DisclaimerController.prototype.showAll_ = function(layer) { + const disclaimers = layer.get('disclaimers'); + if (disclaimers) { + const layerUid = olUtilGetUid(layer); + for (const key in disclaimers) { + const uid = `${layerUid}-${key}`; + this.showDisclaimerMessage_(uid, disclaimers[key]); + } + } +}; + + +/** + * @param {import("ol/layer/Base.js").default} layer Layer + * @private + */ +DisclaimerController.prototype.update_ = function(layer) { + const disclaimers = layer.get('disclaimers'); + if (disclaimers) { + if ('all' in disclaimers) { + // the disclaimer is for all the layers, WMS or WMTS. + console.assert(Object.keys(disclaimers).length === 1); + this.showAll_(layer); + } else { + const layerWMS = /** @type {import("ol/layer/Layer.js").default} */ (layer); + const sourceWMS = /** @type {import("ol/source/ImageWMS.js").default} */ (layerWMS.getSource()); + if (sourceWMS.getParams) { + const layers = sourceWMS.getParams()['LAYERS']; + const layerUid = olUtilGetUid(layer); + for (const key in disclaimers) { + const uid = `${layerUid}-${key}`; + if (layers.indexOf(key) !== -1) { + this.showDisclaimerMessage_(uid, disclaimers[key]); + } else { + this.closeDisclaimerMessage_(uid, disclaimers[key]); + } + } + } + } + } +}; + +/** + * @param {string} layerUid Layer identifier. * @param {string} msg Disclaimer message. * @private */ -DisclaimerController.prototype.closeDisclaimerMessage_ = function(msg) { +DisclaimerController.prototype.closeDisclaimerMessage_ = function(layerUid, msg) { msg = this.gettextCatalog_.getString(msg); if (this.external) { this.visibility = false; @@ -317,6 +359,7 @@ DisclaimerController.prototype.closeDisclaimerMessage_ = function(msg) { this.disclaimer_.close({ popup: this.popup, msg: msg, + layerUid: layerUid, target: this.element_, type: MessageType.WARNING }); diff --git a/contribs/gmf/src/layertree/SyncLayertreeMap.js b/contribs/gmf/src/layertree/SyncLayertreeMap.js index 2fe128ef8fe5..93a5f6cb26c5 100644 --- a/contribs/gmf/src/layertree/SyncLayertreeMap.js +++ b/contribs/gmf/src/layertree/SyncLayertreeMap.js @@ -344,7 +344,7 @@ SyncLayertreeMap.prototype.createWMTSLayer_ = function(gmfLayerWMTS) { /** * Update properties of a layer with the node of a given leafNode. - * @param {import('gmf/themes.js').GmfLayer} leafNode a leaf node. + * @param {import('gmf/themes.js').GmfLayer|import('gmf/themes.js').GmfLayerWMS} leafNode a leaf node. * @param {import("ol/layer/Base.js").default} layer A layer. * @private */ @@ -356,8 +356,14 @@ SyncLayertreeMap.prototype.updateLayerReferences_ = function(leafNode, layer) { const disclaimer = leafNode.metadata.disclaimer; if (disclaimer) { - const disclaimers = layer.get('disclaimers') || []; - disclaimers.push(leafNode.metadata.disclaimer); + const disclaimers = layer.get('disclaimers') || {}; + + // 'all' means that the disclaimer is for all the layer. + let layers = 'all'; + if ('layers' in leafNode) { + layers = /** @type {import('gmf/themes.js').GmfLayerWMS} */ (leafNode).layers; + } + disclaimers[layers] = leafNode.metadata.disclaimer; layer.set('disclaimers', disclaimers); } }; diff --git a/src/message/Disclaimer.js b/src/message/Disclaimer.js index 96c7004a9555..ace7f35e2375 100644 --- a/src/message/Disclaimer.js +++ b/src/message/Disclaimer.js @@ -6,6 +6,20 @@ import ngeoMessageMessage, {MessageType} from 'ngeo/message/Message.js'; import 'ngeo/sass/font.scss'; +/** + * A message to display by the disclaimer service. + * + * @typedef {Object} Message + * @property {number} [delay=7000] The delay in milliseconds the message is shown + * @property {boolean} [popup=false] Whether the message should be displayed inside a popup window or not. + * @property {string} msg The message text to display. + * @property {string} [layerUid] The OpenLayers layer identifier. + * @property {JQuery|Element|string} [target] The target element (or selector to get the element) in which + * to display the message. If not defined, then the default target of the notification service is used. + * @property {string} [type='info'] The type of message. + */ + + /** * Provides methods to display any sort of messages, disclaimers, errors, * etc. Requires Bootstrap library (both CSS and JS) to display the alerts @@ -55,12 +69,24 @@ export class MessageDisclaimerService extends ngeoMessageMessage { * @private */ this.messages_ = {}; + + /** + * @type {Object} + * @private + */ + this.messagesConsumerCount_ = {}; + + /** + * @type {Object} + * @private + */ + this.uids_ = {}; } /** * Show disclaimer message string or object or list of disclaimer message * strings or objects. - * @param {string|import('ngeo/message/Message.js').Message|Array.} + * @param {string|Message|Array.} * object A message or list of messages as text or configuration objects. */ alert(object) { @@ -70,17 +96,17 @@ export class MessageDisclaimerService extends ngeoMessageMessage { /** * Close disclaimer message string or object or list of disclaimer message * strings or objects. - * @param {string|import('ngeo/message/Message.js').Message|Array.} + * @param {string|Message|Array.} * object A message or list of messages as text or configuration objects. */ close(object) { const msgObjects = this.getMessageObjects(object); - msgObjects.forEach(this.closeMessage_, this); + msgObjects.forEach((message) => this.closeMessage_(message)); } /** * Show the message. - * @param {import('ngeo/message/Message.js').Message} message Message. + * @param {Message} message Message. * @protected * @override */ @@ -89,15 +115,15 @@ export class MessageDisclaimerService extends ngeoMessageMessage { const type = message.type; console.assert(typeof type == 'string', 'Type should be set.'); - // No need to do anything if message already exist. + // No need to do anything if message already displayed. const uid = this.getMessageUid_(message); - if (this.messages_[uid] !== undefined) { + if (this.uids_[uid]) { return; } - const showInPopup = message.popup === true; + this.uids_[uid] = true; - if (showInPopup) { + if (message.popup === true) { // display the message in a popup, i.e. using the ngeo create popup const popup = this.createPopup_(); const content = this.sce_.trustAsHtml(message.msg); @@ -108,7 +134,7 @@ export class MessageDisclaimerService extends ngeoMessageMessage { }); // Watch the open property - popup.scope.$watch('open', (newVal, oldVal) => { + popup.scope.$watch('open', newVal => { if (!newVal) { this.closeMessage_(message); } @@ -117,6 +143,14 @@ export class MessageDisclaimerService extends ngeoMessageMessage { this.messages_[uid] = popup; } else { + // get an already displayed compatible message. + const compatibleMessageUid = this.getCompatibleMessageUid_(message); + if (this.messages_[compatibleMessageUid]) { + // we already have a message + this.messagesConsumerCount_[compatibleMessageUid]++; + return; + } + // display the message using a bootstrap dismissible alert const classNames = ['alert', 'fade', 'alert-dismissible', 'show']; switch (type) { @@ -158,36 +192,58 @@ export class MessageDisclaimerService extends ngeoMessageMessage { // Listen when the message gets closed to cleanup the cache of messages el.on('closed.bs.alert', () => { - this.closeMessage_(message); + this.closeMessage_(message, true); }); - - this.messages_[uid] = el; + this.messages_[compatibleMessageUid] = el; + this.messagesConsumerCount_[compatibleMessageUid] = 1; } } /** - * @param {import('ngeo/message/Message.js').Message} message Message. + * @param {Message} message Message. * @return {string} The uid. * @private */ getMessageUid_(message) { + return `${message.msg}-${message.type}-${message.layerUid}`; + } + + /** + * @param {Message} message Message. + * @return {string} The uid. + * @private + */ + getCompatibleMessageUid_(message) { return `${message.msg}-${message.type}`; } /** * Close the message. - * @param {import('ngeo/message/Message.js').Message} message Message. + * @param {Message} message Message. + * @param {boolean} force Force close the message. * @protected */ - closeMessage_(message) { + closeMessage_(message, force = false) { const uid = this.getMessageUid_(message); - const obj = this.messages_[uid]; // (1) No need to do anything if message doesn't exist - if (obj === undefined) { + if (!this.uids_[uid]) { + return; + } + delete this.uids_[uid]; + const compatibleMessageUid = this.getCompatibleMessageUid_(message); + if (force) { + this.messagesConsumerCount_[compatibleMessageUid] = 0; + } else { + this.messagesConsumerCount_[compatibleMessageUid]--; + } + if (this.messagesConsumerCount_[compatibleMessageUid] > 0) { + // the message is still used return; } + const obj = this.messages_[compatibleMessageUid]; + // (2) Close message (popup or alert) if (obj instanceof MessagePopup) { // (2.1) Close popup, if not already closed @@ -205,7 +261,8 @@ export class MessageDisclaimerService extends ngeoMessageMessage { } // (3) Remove message from cache since it's closed now. - delete this.messages_[uid]; + delete this.messages_[compatibleMessageUid]; + delete this.messagesConsumerCount_[compatibleMessageUid]; } } diff --git a/src/message/Message.js b/src/message/Message.js index af44f9bfa31d..fee5cea3ca4b 100644 --- a/src/message/Message.js +++ b/src/message/Message.js @@ -7,7 +7,7 @@ * @property {string} msg The message text to display. * @property {JQuery|Element|string} [target] The target element (or selector to get the element) in which * to display the message. If not defined, then the default target of the notification service is used. - * @property {string} [type] The type of message. Defaults to `info`. + * @property {string} [type='info'] The type of message. */