diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/config/associated-panel/default.json b/schemas/dublin-core/src/main/plugin/dublin-core/config/associated-panel/default.json index d9b986c378c..abfae192cdc 100644 --- a/schemas/dublin-core/src/main/plugin/dublin-core/config/associated-panel/default.json +++ b/schemas/dublin-core/src/main/plugin/dublin-core/config/associated-panel/default.json @@ -12,6 +12,20 @@ "fields": { "url": {} } + }], + "associatedResourcesTypes": [{ + "type": "parent", + "label": "linkToParent", + "config": { + "sources": { + "metadataStore": { + "params": { + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": true} + } + } }] } } diff --git a/schemas/dublin-core/src/main/plugin/dublin-core/layout/config-editor.xml b/schemas/dublin-core/src/main/plugin/dublin-core/layout/config-editor.xml index 70484726a72..ff7aa5b8ef2 100644 --- a/schemas/dublin-core/src/main/plugin/dublin-core/layout/config-editor.xml +++ b/schemas/dublin-core/src/main/plugin/dublin-core/layout/config-editor.xml @@ -67,8 +67,7 @@ editActions: ['addOnlinesrc'] }]"/> - +
@@ -94,8 +93,7 @@ editActions: ['addOnlinesrc'] }]"/> - +
@@ -118,8 +116,8 @@ editActions: ['addOnlinesrc'] }]"/> - + + diff --git a/schemas/iso19110/src/main/plugin/iso19110/config/associated-panel/default.json b/schemas/iso19110/src/main/plugin/iso19110/config/associated-panel/default.json index c4e7c09595a..53bfe374f1f 100644 --- a/schemas/iso19110/src/main/plugin/iso19110/config/associated-panel/default.json +++ b/schemas/iso19110/src/main/plugin/iso19110/config/associated-panel/default.json @@ -1,3 +1,8 @@ { - "config": {} + "config": { + "associatedResourcesTypes": [ { + "type": "hasfeaturecats", + "allowToAddRelation": false + }] + } } diff --git a/schemas/iso19110/src/main/plugin/iso19110/layout/config-editor.xml b/schemas/iso19110/src/main/plugin/iso19110/layout/config-editor.xml index 2ec78cc74f7..4b628b2ff22 100644 --- a/schemas/iso19110/src/main/plugin/iso19110/layout/config-editor.xml +++ b/schemas/iso19110/src/main/plugin/iso19110/layout/config-editor.xml @@ -68,8 +68,7 @@ - + diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/config/associated-panel/default.json b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/config/associated-panel/default.json index b23fa915667..b087ecb8ee4 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/config/associated-panel/default.json +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/config/associated-panel/default.json @@ -991,6 +991,81 @@ } } } - ] + ], + "associatedResourcesTypes": [{ + "type": "siblings", + "label": "linkToSibling", + "config": { + "sources": { + "metadataStore": { + "params": { + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": true} + } + } + }, { + "type": "service", + "label": "linkToService", + "condition": "!gnCurrentEdit.isService", + "config": { + "sources": { + "metadataStore": { + "label": "searchAservice", + "params": { + "resourceType": ["service"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "dataset", + "label": "linkToDataset", + "condition": "gnCurrentEdit.isService", + "config": { + "sources": { + "metadataStore": { + "params": { + "resourceType": ["dataset"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "source", + "label": "linkToSource", + "config": { + "sources": { + "metadataStore": { + "label": "linkToSource", + "params": { + "resourceType": ["dataset"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }, { + "type": "fcats", + "label": "linkToFeatureCatalog", + "config": { + "sources": { + "metadataStore": { + "label": "linkToFeatureCatalog", + "params": { + "resourceType": ["featureCatalog"], + "isTemplate": "n" + } + }, + "remoteurl": {"multiple": false} + } + } + }] } } diff --git a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/config-editor.xml b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/config-editor.xml index 9f13d94f2e3..8025779fe40 100644 --- a/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/config-editor.xml +++ b/schemas/iso19115-3.2018/src/main/plugin/iso19115-3.2018/layout/config-editor.xml @@ -651,8 +651,7 @@ }]" /> - + - + - + - + + @@ -3225,8 +3229,7 @@ editActions: []}]" /> - + - + - + (.*)<\/title>(.|[\r\n])*/, "$3" ); - scope.remoteRecord.uuid = scope.remoteRecord.remoteUrl; + record.uuid = url; // Looking for schema.org tags or json+ld format could also be an option. } catch (e) { console.warn(e); - return false; + return {}; } } else { - return false; + return {}; } - return true; + + record.remoteUrl = url; + return record; } + scope.$watch( + "multipleSelectionModel.remoteLinksToCheck", + function (newVal, oldVal) { + if (newVal != oldVal) { + if (newVal == 0) { + scope.multipleSelectionModel.remoteRecordsList = ""; + scope.multipleSelectionModel.checkingRemoteLinks = false; + } + } + } + ); + + /** + * Checks if the button to add multiple links can be enabled: + * - There are links to add. + * - The processs to add the links is not on-going. + * - The association type field is selected. + * Used in the multiple mode selection. + * + * @returns {boolean} + */ + scope.canAddMultipleLinks = function () { + return ( + scope.multipleSelectionModel.remoteRecordsList !== "" && + !scope.multipleSelectionModel.checkingRemoteLinks && + scope.config && + scope.config.associationType != null + ); + }; + + /** + * Process the urls links to extract the record information: title, url. + * + * Used in the multiple mode selection. + */ + scope.addMultipleLinks = function () { + // Ignore in single selection mode + if (!scope.multipleSelection) return; + + var remoteUrls = scope.multipleSelectionModel.remoteRecordsList.split("\n"); + scope.multipleSelectionModel.invalidRemoteLinks = []; + scope.multipleSelectionModel.remoteLinksToCheck = remoteUrls.length; + scope.multipleSelectionModel.checkingRemoteLinks = true; + for (var i = 0; i < remoteUrls.length; i++) { + var url = remoteUrls[i]; + if (url.indexOf("http") === 0) { + $http + .get(url, { + headers: { Accept: guessContentType() } + }) + .then( + function (response) { + scope.multipleSelectionModel.remoteLinksToCheck--; + + var isRemoteRecordUrlOk = response.status === 200; + if (isRemoteRecordUrlOk) { + // Check we can retrieve title + var remoteRecordInfo = getProperties( + response.data, + response.config.url + ); + + if (!_.isEmpty(remoteRecordInfo)) { + remoteRecordInfo.resourceTitle = remoteRecordInfo.title; + scope.addToSelection( + remoteRecordInfo, + scope.config.associationType, + scope.config.initiativeType + ); + } else { + scope.multipleSelectionModel.invalidRemoteLinks.push( + response.config.url + ); + } + } + }, + function (response) { + scope.multipleSelectionModel.remoteLinksToCheck--; + scope.multipleSelectionModel.invalidRemoteLinks.push( + response.config.url + ); + } + ); + } else { + scope.multipleSelectionModel.remoteLinksToCheck--; + scope.multipleSelectionModel.invalidRemoteLinks.push(url); + } + } + }; + + /** + * Checks a link and adds it to the selection. + * + * Used in single mode selection. + * + * @returns {*} + */ scope.checkLink = function () { + // Ignore in multiple selection mode + if (scope.multipleSelection) return; + scope.resetLink(false); - if (scope.remoteRecord.remoteUrl.indexOf("http") === 0) { + if ( + scope.singleSelectionModel.remoteRecord.remoteUrl.indexOf("http") === 0 + ) { return $http - .get(scope.remoteRecord.remoteUrl, { + .get(scope.singleSelectionModel.remoteRecord.remoteUrl, { headers: { Accept: guessContentType() } }) .then( function (response) { - scope.isRemoteRecordUrlOk = response.status === 200; - if (scope.isRemoteRecordUrlOk) { + scope.singleSelectionModel.isRemoteRecordUrlOk = + response.status === 200; + if (scope.singleSelectionModel.isRemoteRecordUrlOk) { // Check we can retrieve title - scope.isRemoteRecordPropertiesExtracted = getProperties( - response.data + var remoteRecordInfo = getProperties( + response.data, + scope.singleSelectionModel.remoteRecord.remoteUrl ); - if (scope.isRemoteRecordPropertiesExtracted) { - scope.updateSelection(); + scope.singleSelectionModel.isRemoteRecordPropertiesExtracted = + !_.isEmpty(remoteRecordInfo); + if ( + scope.singleSelectionModel.isRemoteRecordPropertiesExtracted + ) { + scope.singleSelectionModel.remoteRecord = remoteRecordInfo; + scope.singleSelectionModel.resourceTitle = + remoteRecordInfo.title; } } }, function (response) { - scope.isRemoteRecordUrlOk = response.status === 500; + scope.singleSelectionModel.isRemoteRecordUrlOk = + response.status === 500; } ); } }; + scope.updateSelectionAndTriggerSearch = function () { + // Ignore in multiple selection mode + if (scope.multipleSelection) return; + + scope.updateSelection(); + scope.triggerSearch(); + }; + scope.updateSelection = function () { + // Ignore in multiple selection mode + if (scope.multipleSelection) return; + if (scope.selectionList) { - scope.selectionList.length = 0; - scope.selectionList.push(scope.remoteRecord); + if (!scope.multipleSelection) { + scope.selectionList.length = 0; + } + scope.selectionList.push(scope.singleSelectionModel.remoteRecord); } else if (angular.isFunction(scope.addToSelection)) { // sibling mode - scope.remoteRecord.resourceTitle = scope.remoteRecord.title; + scope.singleSelectionModel.remoteRecord.resourceTitle = + scope.singleSelectionModel.remoteRecord.title; scope.addToSelection( - scope.remoteRecord, + scope.singleSelectionModel.remoteRecord, scope.config.associationType, scope.config.initiativeType ); @@ -342,14 +498,18 @@ }; scope.resetLink = function (allProperties) { + // Ignore in multiple selection mode + if (scope.multipleSelection) return; + scope.selectionList = angular.isDefined(scope.stateObj) ? scope.stateObj.selectRecords : scope.selectRecords; - scope.isRemoteRecordUrlOk = true; - scope.remoteRecord.title = ""; - scope.remoteRecord.uuid = ""; + scope.singleSelectionModel.isRemoteRecordUrlOk = true; if (allProperties) { - scope.remoteRecord.remoteUrl = ""; + scope.singleSelectionModel.remoteRecord.remoteUrl = ""; + } else { + scope.singleSelectionModel.remoteRecord.title = ""; + scope.singleSelectionModel.remoteRecord.uuid = ""; } clearSelection(); }; @@ -1707,7 +1867,15 @@ scope.onlineSrcLink = ""; scope.addOnlineSrcInDataset = true; - gnOnlinesrc.register(scope.mode, function () { + gnOnlinesrc.register(scope.mode, function (config) { + if (config && !angular.isObject(config)) { + config = angular.fromJson(config); + } + + scope.config = { + sources: config && config.sources + }; + $(scope.popupid).modal("show"); // parameters of the online resource form @@ -1812,6 +1980,18 @@ } }; + scope.addToSelection = function (record) { + scope.stateObj.selectRecords.length = 0; + scope.stateObj.selectRecords.push(record); + }; + + scope.removeFromSelection = function (record) { + var index = scope.stateObj.selectRecords.indexOf(record); + if (index > -1) { + scope.stateObj.selectRecords.splice(index, 1); + } + }; + /** * Watch the result metadata selection change. * selectRecords is a value of the SearchFormController scope. @@ -1940,8 +2120,7 @@ return { restrict: "A", scope: {}, - templateUrl: - "../../catalog/components/edit/onlinesrc/" + "partials/linkToMd.html", + templateUrl: "../../catalog/components/edit/onlinesrc/partials/linkToMd.html", compile: function compile(tElement, tAttrs, transclude) { return { pre: function preLink(scope) { @@ -1980,39 +2159,39 @@ return true; }; + scope.addToSelection = function (record) { + scope.selectRecords.length = 0; + scope.selectRecords.push(record); + }; + + scope.removeFromSelection = function (record) { + var index = scope.selectRecords.indexOf(record); + if (index > -1) { + scope.selectRecords.splice(index, 1); + } + }; + /** * Register a method on popup open to reset * the search form and trigger a search. */ - gnOnlinesrc.register(scope.mode, function () { - $(scope.popupid).modal("show"); - var searchParams = {}; - if (scope.mode === "fcats") { - searchParams = { - resourceType: "featureCatalog", - isTemplate: "n" - }; - scope.btn = { - label: $translate.instant("linkToFeatureCatalog") - }; - } else if (scope.mode === "parent") { - searchParams = { - isTemplate: "n" - }; - scope.btn = { - label: $translate.instant("linkToParent") - }; - } else if (scope.mode === "source") { - searchParams = { - isTemplate: "n" - }; - scope.btn = { - label: $translate.instant("linkToSource") - }; + gnOnlinesrc.register(scope.mode, function (config) { + if (config && !angular.isObject(config)) { + config = angular.fromJson(config); } + + scope.config = { + sources: config && config.sources + }; + + $(scope.popupid).modal("show"); + var searchParams = + scope.config.sources && scope.config.sources.metadataStore + ? scope.config.sources.metadataStore.params || {} + : {}; scope.$broadcast("resetSearch", searchParams); + scope.selectRecords = []; }); - scope.gnOnlinesrc = gnOnlinesrc; } }; @@ -2089,20 +2268,23 @@ * Register a method on popup open to reset * the search form and trigger a search. */ - gnOnlinesrc.register("sibling", function (config) { + gnOnlinesrc.register("siblings", function (config) { if (config && !angular.isObject(config)) { config = angular.fromJson(config); } scope.config = { associationTypeForced: angular.isDefined( - config && config.associationType + config && config.fields && config.fields.associationType ), - associationType: (config && config.associationType) || null, + associationType: + (config && config.fields && config.fields.associationType) || null, initiativeTypeForced: angular.isDefined( - config && config.initiativeType + config && config.fields && config.fields.initiativeType ), - initiativeType: (config && config.initiativeType) || null + initiativeType: + (config && config.fields && config.fields.initiativeType) || null, + sources: config && config.sources }; $(scope.popupid).modal("show"); @@ -2176,7 +2358,7 @@ /** * Add the result metadata to the selection. * Add it only it associationType & initiativeType are set. - * If the metadata alreay exists, it override it with the new + * If the metadata already exists, it overrides it with the new * given associationType/initiativeType. */ scope.addToSelection = function (md, associationType, initiativeType) { @@ -2245,5 +2427,104 @@ } }; } + ]) + + /** + * @ngdoc directive + * @name gn_onlinesrc.directive:gnDoiSearchPanel + * @restrict A + * @requires gnOnlinesrc + * + * @description + * The `gnDoiSearchPanel` directive provides a form to search and link DOI resources + * to the current metadata. + */ + .directive("gnDoiSearchPanel", [ + "gnDoiSearchService", + function (gnDoiSearchService) { + return { + restrict: "A", + replace: true, + scope: { + doiUrl: "=?", + doiPrefix: "=?", + doiQueryPattern: "=?", + mode: "@", + addToSelectionCb: "&?", + removeFromSelectionCb: "&?" + }, + templateUrl: + "../../catalog/components/edit/onlinesrc/" + "partials/doisearchpanel.html", + link: function (scope, element, attrs) { + // select (single value) / add mode (used in siblings dialog) + scope.mode = scope.mode || "select"; + scope.updateSelection = angular.isFunction(scope.addToSelectionCb) + ? function (md) { + if (scope.isSelected(md)) { + scope.selectedMd = null; + if (angular.isFunction(scope.removeFromSelectionCb)) { + scope.removeFromSelectionCb({ record: md }); + } + } else { + scope.selectedMd = md; + scope.addToSelectionCb({ record: md }); + } + } + : undefined; + + scope.isSelected = function (md) { + return md == scope.selectedMd; + }; + + scope.queryValue = ""; + scope.isSearching = false; + + scope.clearSearch = function () { + scope.queryValue = ""; + scope.results = []; + }; + + scope.$on("resetSearch", scope.clearSearch); + + scope.search = function () { + var searchQuery = + scope.queryValue !== "" + ? scope.doiQueryPattern.replaceAll("{query}", scope.queryValue) + : ""; + scope.isSearching = true; + gnDoiSearchService.search(scope.doiUrl, scope.doiPrefix, searchQuery).then( + function (response) { + scope.isSearching = false; + var results = []; + + angular.forEach(response.data.data, function (r) { + results.push({ + uuid: r.id, + remoteUrl: r.attributes.url, + resourceTitle: + r.attributes.titles.length > 0 + ? r.attributes.titles[0].title + : r.url, + title: + r.attributes.titles.length > 0 + ? r.attributes.titles[0].title + : r.url, + description: + r.attributes.descriptions.length > 0 + ? r.attributes.descriptions[0].descriptions + : "" + }); + }); + + scope.results = results; + }, + function (response) { + scope.isSearching = false; + } + ); + }; + } + }; + } ]); })(); diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js index 6d0003da1dd..c41fd2ee43a 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/OnlineSrcService.js @@ -261,32 +261,37 @@ relatedTypes = defaultRelatedTypes; } - linksAndRelatedPromises.push( - $http.get( - apiPrefix + - "/related?type=" + - relatedTypes.join("&type=") + - (!isApproved ? "&approved=false" : ""), - { - headers: { - Accept: "application/json" + if (relatedTypes.length > 0) { + linksAndRelatedPromises.push( + $http.get( + apiPrefix + + "/related?type=" + + relatedTypes.join("&type=") + + (!isApproved ? "&approved=false" : ""), + { + headers: { + Accept: "application/json" + } } - } - ) - ); - linksAndRelatedPromises.push( - $http.get( - apiPrefix + - "/associated?type=" + - associatedTypes.join("&type=") + - (!isApproved ? "&approved=false" : ""), - { - headers: { - Accept: "application/json" + ) + ); + } + + if (associatedTypes.length > 0) { + linksAndRelatedPromises.push( + $http.get( + apiPrefix + + "/associated?type=" + + associatedTypes.join(",") + + (!isApproved ? "&approved=false" : ""), + { + headers: { + Accept: "application/json" + } } - } - ) - ); + ) + ); + } var all = $q.all(linksAndRelatedPromises).then(function (result) { var relations = {}; @@ -903,4 +908,18 @@ }; } ]); + + /** + * Service to query a DOI service and return the results. + */ + module.service("gnDoiSearchService", [ + "$http", + function ($http) { + return { + search: function (url, prefix, query) { + return $http.get(url + "?prefix=" + prefix + "&query=" + query); + } + }; + } + ]); })(); diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/doisearchpanel.html b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/doisearchpanel.html new file mode 100644 index 00000000000..b6601e0701d --- /dev/null +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/doisearchpanel.html @@ -0,0 +1,65 @@ +
+ + +
+
+
+ + + + + + + +
+ + +

+ zarooResult +

+ +
    +
  • +
    + + +
    +
    + {{md.resourceTitle}} +
    + +
    +
    +
  • +
+
+
+
diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkServiceToDataset.html b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkServiceToDataset.html index 8236d6a43c9..a8f6b467e55 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkServiceToDataset.html +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkServiceToDataset.html @@ -2,8 +2,15 @@
-
-
+
+ + +
-
+ + +
+ +
+

{{alertMsg}}

diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkToMd.html b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkToMd.html index 3fbc27150ff..9d1cd9e388f 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkToMd.html +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linkToMd.html @@ -1,9 +1,16 @@
+
-
-
+
+ +
-
+ + +
+
+ +
diff --git a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linktosibling.html b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linktosibling.html index 8e108777894..ec3b600611a 100644 --- a/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linktosibling.html +++ b/web-ui/src/main/resources/catalog/components/edit/onlinesrc/partials/linktosibling.html @@ -13,6 +13,7 @@ data-allow-blank="false" data-selected-info="config.associationType" data-gn-schema-info="associationType" + data-ng-disabled="config.associationTypeForced" lang="lang" >
@@ -29,12 +30,19 @@ data-gn-schema-info="initiativeType" lang="lang" data-allow-blank="true" + data-ng-disabled="config.initiativeTypeForced" >
-
+
@@ -59,21 +67,48 @@
- - {{md.resourceTitle}} - - +
+ + +
+ {{md.resourceTitle}} +
+ +
+
+
-
+ +
+ +
+