-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ ({{singleSelectionModel.remoteRecord.uuid}})
+
-
-
+
+
+
+
+
+
diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js b/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js
index 02778297da3..2dc8604d4e1 100644
--- a/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js
+++ b/web-ui/src/main/resources/catalog/components/metadataactions/RelatedDirective.js
@@ -1287,4 +1287,234 @@
};
}
]);
+
+ /**
+ * Directive to display a panel in the metadata editor with a header and a list of associated resources.
+ *
+ */
+ module.directive("gnAssociatedResourcesPanel", [
+ "gnCurrentEdit",
+ function (gnCurrentEdit) {
+ return {
+ restrict: "A",
+ templateUrl: function (elem, attrs) {
+ return (
+ attrs.template ||
+ "../../catalog/components/metadataactions/partials/associatedResourcesPanel.html"
+ );
+ },
+ scope: {
+ md: "=gnAssociatedResourcesPanel",
+ mode: "="
+ },
+ link: function (scope, element, attrs) {
+ gnCurrentEdit.associatedPanelConfigId = attrs["editorConfig"] || "default";
+ }
+ };
+ }
+ ]);
+
+ /**
+ * Displays a panel with different types of associations defined in the schema configuration and available in the metadata object 'md'.
+ *
+ */
+ module.directive("gnAssociatedResourcesContainer", [
+ "gnRelatedResources",
+ "gnConfigService",
+ "gnOnlinesrc",
+ "gnCurrentEdit",
+ "gnSchemaManagerService",
+ "$injector",
+ "$filter",
+ function (
+ gnRelatedResources,
+ gnConfigService,
+ gnOnlinesrc,
+ gnCurrentEdit,
+ gnSchemaManagerService,
+ $injector,
+ $filter
+ ) {
+ return {
+ restrict: "A",
+ templateUrl: function (elem, attrs) {
+ return (
+ attrs.template ||
+ "../../catalog/components/metadataactions/partials/associatedResourcesContainer.html"
+ );
+ },
+ scope: {
+ md: "=gnAssociatedResourcesContainer",
+ mode: "="
+ },
+ link: function (scope, element, attrs, controller) {
+ scope.lang = scope.lang || scope.$parent.lang;
+ scope.gnCurrentEdit = gnCurrentEdit;
+ scope.relations = [];
+ scope.relatedConfigUI = [];
+ scope.relatedResourcesConfig = gnRelatedResources;
+ if ($injector.has("gnOnlinesrc")) {
+ scope.onlinesrcService = $injector.get("gnOnlinesrc");
+ }
+
+ // Values for relation types used in the UI (and also to filter some metadata by resource type: dataset / service
+ var UIRelationTypeValues = {
+ dataset: "dataset",
+ service: "service",
+ source: "source",
+ parent: "parent",
+ fcats: "fcats",
+ siblings: "siblings"
+ };
+
+ // Values used for the relation types by the /associated API, that doesn't match with the UI
+ var APIRelationTypeValues = {
+ dataset: "datasets",
+ service: "services",
+ source: "sources",
+ parent: "parent",
+ fcats: "fcats",
+ siblings: "siblings"
+ };
+
+ // A mapper from relation types from the UI to the API values
+ scope.mapUIRelationToApi = function (type) {
+ return APIRelationTypeValues[type];
+ };
+
+ scope.getClass = function (type) {
+ if (type === UIRelationTypeValues.dataset) {
+ return "fa gn-icon-dataset";
+ } else if (type === UIRelationTypeValues.service) {
+ return "fa fa-fw fa-cloud";
+ } else if (type === UIRelationTypeValues.source) {
+ return "fa gn-icon-source";
+ } else if (type === UIRelationTypeValues.parent) {
+ return "fa gn-icon-series";
+ } else if (type === UIRelationTypeValues.fcats) {
+ return "fa fa-table";
+ } else if (type === UIRelationTypeValues.siblings) {
+ return "fa fa-sign-out";
+ }
+
+ return "";
+ };
+ scope.canRemoveLink = function (record, type) {
+ if (record.origin === "remote") {
+ return true;
+ } else {
+ return (
+ type === UIRelationTypeValues.dataset ||
+ type === UIRelationTypeValues.service ||
+ type === UIRelationTypeValues.parent ||
+ type === UIRelationTypeValues.source ||
+ type === UIRelationTypeValues.fcats ||
+ type === UIRelationTypeValues.siblings
+ );
+ }
+ };
+
+ scope.remove = function (record, type) {
+ if (type === UIRelationTypeValues.dataset) {
+ scope.onlinesrcService.removeDataset(record);
+ } else if (type === UIRelationTypeValues.service) {
+ scope.onlinesrcService.removeService(record);
+ } else if (type === UIRelationTypeValues.parent) {
+ scope.onlinesrcService.removeMdLink(UIRelationTypeValues.parent, record);
+ } else if (type === UIRelationTypeValues.source) {
+ scope.onlinesrcService.removeMdLink(UIRelationTypeValues.source, record);
+ } else if (type === UIRelationTypeValues.fcats) {
+ scope.onlinesrcService.removeFeatureCatalog(record);
+ } else if (type === UIRelationTypeValues.siblings) {
+ scope.onlinesrcService.removeSibling(record);
+ }
+ };
+
+ var loadRelations = function (relationTypes) {
+ gnOnlinesrc.getAllResources(relationTypes).then(function (data) {
+ var res = gnOnlinesrc.formatResources(
+ data,
+ scope.lang,
+ gnCurrentEdit.mdLanguage
+ );
+
+ // Change the relation keys from the API response to the UI values
+ scope.relations = _.mapKeys(res.relations, function (value, key) {
+ if (key === APIRelationTypeValues.service) {
+ return UIRelationTypeValues.service;
+ } else if (key === APIRelationTypeValues.dataset) {
+ return UIRelationTypeValues.dataset;
+ } else if (key === APIRelationTypeValues.source) {
+ return UIRelationTypeValues.source;
+ } else {
+ return key;
+ }
+ });
+
+ scope.md.related = {};
+
+ scope.relatedConfig.forEach(function (config) {
+ config.relations = scope.relations[config.type] || [];
+
+ var processConfig = true;
+
+ // The configuration has an expression to evaluate if it should be processed
+ if (config.condition) {
+ processConfig = scope.$eval(config.condition);
+ }
+
+ if (processConfig) {
+ scope.md.related[config.type] = scope.relations[config.type] || [];
+ // TODO: Review filter by siblings properties, Metadata instances doesn't have this information
+ if (config.config && config.config.fields) {
+ var filterObject = { properties: {} };
+
+ for (var item in config.config.fields) {
+ filterObject.properties[item] = config.config.fields[item];
+ }
+
+ config.relations = $filter("filter")(config.relations, filterObject);
+ }
+
+ config.relationFound = config.relations.length > 0;
+ // By default, allow to add relations unless explicitly disallowed in the configuration.
+ config.allowToAddRelation = angular.isDefined(config.allowToAddRelation)
+ ? config.allowToAddRelation
+ : true;
+ scope.relatedConfigUI.push(config);
+ }
+ });
+ });
+ };
+
+ gnSchemaManagerService
+ .getEditorAssociationPanelConfig(
+ gnCurrentEdit.schema,
+ gnCurrentEdit.associatedPanelConfigId
+ )
+ .then(function (r) {
+ scope.relatedConfig = r.config.associatedResourcesTypes;
+ var relationTypes = _.map(scope.relatedConfig, "type");
+
+ // Adapt the UI types to the backend types value: services --> service, datasets --> dataset
+ // The UI depends on the values also filter by resource type in
+ // the associated resources dialogs.
+ relationTypes = _.map(relationTypes, function (value) {
+ if (value === UIRelationTypeValues.service) {
+ return APIRelationTypeValues.service;
+ } else if (value === UIRelationTypeValues.dataset) {
+ return APIRelationTypeValues.dataset;
+ } else if (value === UIRelationTypeValues.source) {
+ return APIRelationTypeValues.source;
+ } else {
+ return value;
+ }
+ });
+
+ loadRelations(relationTypes);
+ });
+ }
+ };
+ }
+ ]);
})();
diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/partials/associatedResourcesContainer.html b/web-ui/src/main/resources/catalog/components/metadataactions/partials/associatedResourcesContainer.html
new file mode 100644
index 00000000000..e1f4674a4ee
--- /dev/null
+++ b/web-ui/src/main/resources/catalog/components/metadataactions/partials/associatedResourcesContainer.html
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+ {{keyToken[0] | translate}}
+ ({{keyToken[1] | translate}})
+
+
+
+
+
+
+
diff --git a/web-ui/src/main/resources/catalog/components/metadataactions/partials/associatedResourcesPanel.html b/web-ui/src/main/resources/catalog/components/metadataactions/partials/associatedResourcesPanel.html
new file mode 100644
index 00000000000..0e69ad0034c
--- /dev/null
+++ b/web-ui/src/main/resources/catalog/components/metadataactions/partials/associatedResourcesPanel.html
@@ -0,0 +1,16 @@
+
+
+
+
+ associatedResourcesPanel
+
+
+
+
+
diff --git a/web-ui/src/main/resources/catalog/components/search/searchmanager/SearchResultsDirective.js b/web-ui/src/main/resources/catalog/components/search/searchmanager/SearchResultsDirective.js
index bed83b374bd..5a9c7e40fa4 100644
--- a/web-ui/src/main/resources/catalog/components/search/searchmanager/SearchResultsDirective.js
+++ b/web-ui/src/main/resources/catalog/components/search/searchmanager/SearchResultsDirective.js
@@ -89,8 +89,13 @@
scope.selection.splice(scope.selection.indexOf(md), 1);
}
} else {
- scope.selection.pop();
- scope.selection.push(md);
+ // Unselect current
+ if (scope.selection.length === 1 && scope.selection[0]._id === md._id) {
+ scope.selection.pop();
+ } else {
+ scope.selection.pop();
+ scope.selection.push(md);
+ }
}
};
} else {
diff --git a/web-ui/src/main/resources/catalog/components/search/searchmanager/partials/searchresults.html b/web-ui/src/main/resources/catalog/components/search/searchmanager/partials/searchresults.html
index 7d189a8a142..b4fe7f51f05 100644
--- a/web-ui/src/main/resources/catalog/components/search/searchmanager/partials/searchresults.html
+++ b/web-ui/src/main/resources/catalog/components/search/searchmanager/partials/searchresults.html
@@ -35,6 +35,7 @@
data-ng-repeat="md in searchResults.records"
id="gn-record-{{md.uuid}}"
href=""
+ style="cursor: pointer"
data-ng-class="isSelected(md) ? 'active' : ''"
data-ng-click="onClick(md)"
>
@@ -52,7 +53,11 @@
{{md.resourceTitle}}
-
+
diff --git a/web-ui/src/main/resources/catalog/locales/en-editor.json b/web-ui/src/main/resources/catalog/locales/en-editor.json
index cf90334d682..dd6dc4a1fd8 100644
--- a/web-ui/src/main/resources/catalog/locales/en-editor.json
+++ b/web-ui/src/main/resources/catalog/locales/en-editor.json
@@ -165,7 +165,7 @@
"linkToParentTitle": "Link a parent metadata record to the current metadata",
"linkToService": "Link to a service",
"linkToServiceTitle": "Link a service to the current metadata",
- "linkToSibling": "Link to other resources",
+ "linkToSibling": "Link to records",
"linkToSiblingTitle": "Link a resource to the current metadata",
"linkToSource": "Link to a source dataset",
"linkToSourceTitle": "Link a source dataset to the current metadata",
@@ -446,5 +446,13 @@
"addOnlinesrc#links": "Add links",
"addOnlinesrc#links-help": "eg. web links",
"addOnlinesrc#quality": "Add quality info",
- "addOnlinesrc#quality-help": "eg. specification, reports"
+ "addOnlinesrc#quality-help": "eg. specification, reports",
+ "associated-parent": "Parent Metadata",
+ "associated-service": "Service",
+ "associated-dataset": "Dataset",
+ "associated-source": "Source datasets",
+ "associated-fcats": "Feature catalog",
+ "associated-siblings": "Associated resources",
+ "associated-hasfeaturecats": "Using this feature catalog",
+ "associatedResourcesPanel": "Associated resources"
}
diff --git a/web-ui/src/main/resources/catalog/locales/en-v4.json b/web-ui/src/main/resources/catalog/locales/en-v4.json
index 8c894c03911..20ab5934710 100644
--- a/web-ui/src/main/resources/catalog/locales/en-v4.json
+++ b/web-ui/src/main/resources/catalog/locales/en-v4.json
@@ -51,7 +51,7 @@
"remoteRecord": "Record from external catalog",
"chooseACatalogRecord": "Choose a catalog record",
"siblingListToAdd": "List of record links to add",
- "orUseRemoteRecordUrl": "or link to a remote record",
+ "orUseRemoteRecordUrl": "Link to a remote record",
"remoteRecordUrl-help": "Remote record can be linked to record in this catalog by pointing to an URL of the remote record. If the remote record is in a GeoNetwork catalog, the landing page of the record is recommended (eg. https://catalog/geonetwork/srv/api/records/{uuid}). For GeoNode, the HTML page can work. For others, XML document can be used (eg. API call or CSW GetRecordById request).",
"remoteRecordPropertiesError": "Failed to extract title and UUID for remote record. Check the target document.",
"remoteRecordUrlReturnedError": "Failed to access URL: ",
@@ -422,5 +422,6 @@
"overviewDescription": "Overview description",
"overviewUrl": "Overview URL",
"restApiUrl": "REST API URL",
- "filterHelp": "Please click on one of the buttons below to activate the filter"
+ "filterHelp": "Please click on one of the buttons below to activate the filter",
+ "selectDOIResource": "Choose a DOI resource"
}