diff --git a/src/js/models/AppModel.js b/src/js/models/AppModel.js index c8d6c914e..49acc70f6 100644 --- a/src/js/models/AppModel.js +++ b/src/js/models/AppModel.js @@ -569,6 +569,19 @@ define(['jquery', 'underscore', 'backbone'], */ quickAddTaxa: [], + /** + * Whether or not documents saved in the Editor should automatically have + * the online URL of the dataset landing page added to the EML within a + * element. The URL will be the view service for whatever + * member node is being used, or if the dataset has DOI, it should use + * doi.org. Setting this to true will also update the url + * when the dataset is updated, or when it is published with a DOI. + * @since x.x.x + * @type {boolean} + * @default false + */ + autoAddDistributionURL: false, + /** * The base URL for the repository. This only needs to be changed if the repository * is hosted at a different origin than the MetacatUI origin. This URL is used to contruct all diff --git a/src/js/models/metadata/eml211/EML211.js b/src/js/models/metadata/eml211/EML211.js index 8d5eb77f0..6060c9c1a 100644 --- a/src/js/models/metadata/eml211/EML211.js +++ b/src/js/models/metadata/eml211/EML211.js @@ -1287,6 +1287,12 @@ define(['jquery', 'underscore', 'backbone', 'uuid', formData.append("newPid", this.get("id")); formData.append("pid", this.get("oldPid")); } + + // Add the distribution URL to the EML (if the repository is configured + // to do so). Only do this once the pid has been updated. + if (MetacatUI.appModel.get("autoAddDistributionURL")) { + this.addDatasetDistributionURL(); + } //Serialize the EML XML var xml = this.serialize(); @@ -1430,6 +1436,87 @@ define(['jquery', 'underscore', 'backbone', 'uuid', return Backbone.Model.prototype.save.call(this, attributes, saveOptions); }, + + /** + * Adds a new EMLDistribution model to the distribution array + * @param {object} attributes - The attributes to set on the new + * EMLDistribution model + * @param {object} options - Options to pass to the new EMLDistribution + * model + */ + addDistribution: function (attributes, options) { + try { + const distributions = this.get('distribution') || []; + const newDistribution = new EMLDistribution(attributes, options); + distributions.push(newDistribution); + this.set('distribution', distributions); + } catch (e) { + console.log("Couldn't add a distribution to the EML model", e); + } + }, + + /** + * Find the distribution that has all of the matching attributes. This will + * return true if the distribution has all of the attributes, even if it + * has more attributes than the ones passed in. + * @param {object} attributes - The attributes to match + * @param {boolean} partialMatch - If true, then the attributes only need + * to partially match. If false, then the attributes must match exactly. + */ + findDistribution: function (attributes, partialMatch = false) { + const distributions = this.get('distribution') || []; + return distributions.find(d => { + return Object.keys(attributes).every(key => { + const val = d.get(key); + if (partialMatch) { + return val.includes(attributes[key]); + } + return val === attributes[key]; + }); + }); + }, + + /** + * Make sure that the EML dataset element has a distribution node with the + * location where the data package can be viewed. This will be either the + * view URL for the member node being used or the DOI.org URL if the dataset + * has one. This method will look for the old distribution URL and update it + * if it exists, or add a new distribution node if it doesn't. + */ + addDatasetDistributionURL: function () { + const model = this; + const oldPid = this.get('oldPid'); + const newPid = this.get('id'); + const seriesId = this.get('seriesId'); + const IDs = [oldPid, newPid, seriesId] + + // Remove any distribution models with the old PID, seriesId, or current + // PID in the URL (only if the URL function is "information") + const distributions = this.get('distribution') || []; + IDs.forEach(id => { + const distributions = model.get('distribution'); + if(!distributions || !distributions.length) return; + const dist = this.findDistribution( + { url: id, urlFunction: 'information' }, true); + if (dist) { + // Remove the distribution model from the array + distributions.splice(distributions.indexOf(dist), 1); + } + }); + + + // Add a new distribution node with the view URL + const viewURL = this.getCanonicalDOIIRI() || this.createViewURL(); + if (viewURL) { + this.addDistribution({ + url: viewURL, + urlFunction: 'information' + }); + } else { + console.log('Could not add a distribution node with the view URL'); + } + + }, /* diff --git a/src/js/models/metadata/eml211/EMLDistribution.js b/src/js/models/metadata/eml211/EMLDistribution.js index 86cbb6167..4e2e166ab 100644 --- a/src/js/models/metadata/eml211/EMLDistribution.js +++ b/src/js/models/metadata/eml211/EMLDistribution.js @@ -183,6 +183,48 @@ define(["jquery", "underscore", "backbone", "models/DataONEObject"], function ( }); }, + /** + * Adds a URL for an online distribution to the model. If the model already + * has offline distribution info, this method will remove all of the offline + * nodes and replace them with a node when overwrite is true. + * @param {string} url - The URL to add to the model + * @param {string} urlFunction - Optional, the purpose of the URL. May be + * either "information" or "download". + * @param {boolean} overwrite - Optional, if true, will overwrite any + * existing URL or offline distribution. + */ + addURL: function (url, urlFunction = null, overwrite = false) { + if (this.hasValuesForDistributionLocation("offline") && !overwrite) { + console.log( + "Cannot add a URL to a distribution that already has an offline " + + "distribution. Create a new distribution instead, or set overwrite " + + "to true." + ); + return; + } + + if (!url) { + console.log("Cannot add a URL to a distribution without a URL value"); + return; + } + + if (this.get("url") && !overwrite) { + console.log( + "Cannot add a URL to a distribution that already has a URL. " + + "Set overwrite to true to overwrite the existing URL." + ); + return; + } + + this.set("url", url); + if (urlFunction) this.set("urlFunction", urlFunction); + + // Remove all of the offline node values + this.offlineNodes.forEach((nodeName) => { + this.set(nodeName, null); + }); + }, + /* * Makes a copy of the original XML DOM and updates it with the new values * from the model. @@ -249,7 +291,7 @@ define(["jquery", "underscore", "backbone", "models/DataONEObject"], function ( // Add the urlFunction attribute if one is set in the model. Remove it if // it's not set. - const url = $objectDOM.find("url") + const url = $objectDOM.find("url"); if (url) { const urlFunction = this.get("urlFunction"); if (urlFunction) { @@ -258,7 +300,6 @@ define(["jquery", "underscore", "backbone", "models/DataONEObject"], function ( url.removeAttr("function"); } } - return objectDOM; },