Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions src/js/models/AppModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -439,10 +439,14 @@ define(["jquery", "underscore", "backbone"], function ($, _, Backbone) {
editorSerializationFormat: "https://eml.ecoinformatics.org/eml-2.2.0",

/**
* The XML schema location the dataset editor will use when creating new EML. This should
* correspond with {@link AppConfig#editorSerializationFormat}
* @type {string}
* @default "https://eml.ecoinformatics.org/eml-2.2.0 https://eml.ecoinformatics.org/eml-2.2.0/eml.xsd"
* The XML schema location the dataset editor will use when creating
* new EML. This should correspond with
* {@link AppConfig#editorSerializationFormat}. Note there must be an
* even number of values in this string, with each pair consisting of
* a namespace URI and the schema location URL for that namespace.
* @type {string}
* @default "https://eml.ecoinformatics.org/eml-2.2.0
* https://eml.ecoinformatics.org/eml-2.2.0/eml.xsd"
* @readonly
* @since 2.13.0
*/
Expand Down
41 changes: 18 additions & 23 deletions src/js/models/metadata/eml211/EML211.js
Original file line number Diff line number Diff line change
Expand Up @@ -2167,10 +2167,7 @@ define([
// Set base attributes
eml.attr("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
eml.attr("xmlns:stmml", "http://www.xml-cml.org/schema/stmml-1.1");
eml.attr(
"xsi:schemaLocation",
"https://eml.ecoinformatics.org/eml-2.2.0 https://eml.ecoinformatics.org/eml-2.2.0/eml.xsd",
);
this.setSchemaLocation(eml);
eml.attr("packageId", this.get("id"));
eml.attr("system", emlSystem);

Expand Down Expand Up @@ -2373,33 +2370,31 @@ define([
* @returns {Element} The element, possibly modified
*/
setSchemaLocation(eml) {
if (!MetacatUI || !MetacatUI.appModel) {
return eml;
}
if (!MetacatUI || !MetacatUI.appModel) return eml;

const current = $(eml).attr("xsi:schemaLocation");
const format = MetacatUI.appModel.get("editorSerializationFormat");
const location = MetacatUI.appModel.get("editorSchemaLocation");

// Return now if we can't do anything anyway
if (!format || !location) {
return eml;
}
if (!location) return eml;

// Simply add if the attribute isn't present to begin with
if (!current || typeof current !== "string") {
$(eml).attr("xsi:schemaLocation", `${format} ${location}`);

return eml;
}

// Don't append if it's already present
if (current.indexOf(format) >= 0) {
const format = MetacatUI.appModel.get("editorSerializationFormat");
const current = $(eml).attr("xsi:schemaLocation")?.trim();
const isString = current && typeof current === "string";
// Must be even: a namespace URI plus the schema location
const hasEvenNumber = current?.split(/\s+/).length % 2 === 0 || false;

// Don't append if it's already present and valid
if (isString && hasEvenNumber && current?.includes(format)) return eml;

// If there is already a valid location but is missing this format,
// append it
if (current && isString && hasEvenNumber) {
$(eml).attr("xsi:schemaLocation", `${current} ${location}`);
return eml;
}

$(eml).attr("xsi:schemaLocation", `${current} ${location}`);

// In all other cases, set the schema location to just this format
$(eml).attr("xsi:schemaLocation", location);
return eml;
},

Expand Down
83 changes: 83 additions & 0 deletions test/js/specs/unit/models/metadata/eml211/EML211.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,88 @@ define([
.should.be.instanceof(EMLTaxonCoverage);
});
});

describe("setSchemaLocation", function () {
const schemaLocation =
"https://example.org/eml-2.2.0 https://example.org/eml-2.2.0/eml.xsd";
const serializationFormat = "https://example.org/eml-2.2.0";
let originalMetacatUI;

function mockMetacatUI(overrides = {}) {
window.MetacatUI = {
appModel: {
get: (attr) => {
if (overrides[attr]) {
return overrides[attr];
}
if (attr === "editorSchemaLocation") return schemaLocation;
if (attr === "editorSerializationFormat")
return serializationFormat;
return undefined;
},
},
};
}

function createEmlElement(initialLocation) {
const element = document.createElement("eml");
if (initialLocation) {
$(element).attr("xsi:schemaLocation", initialLocation);
}
return element;
}

beforeEach(function () {
originalMetacatUI = window.MetacatUI;
mockMetacatUI();
});

afterEach(function () {
window.MetacatUI = originalMetacatUI;
});

it("sets the schema location when missing", function () {
const emlElement = createEmlElement();
const updated = state.eml.setSchemaLocation(emlElement);

expect(updated).to.equal(emlElement);
expect($(emlElement).attr("xsi:schemaLocation")).to.equal(
schemaLocation,
);
});

it("appends the configured schema location when other valid entries exist", function () {
const existing =
"http://existing.org/ns http://existing.org/schema.xsd";
const emlElement = createEmlElement(existing);

state.eml.setSchemaLocation(emlElement);

expect($(emlElement).attr("xsi:schemaLocation")).to.equal(
`${existing} ${schemaLocation}`,
);
});

it("does not modify the schema location when the format is already present", function () {
const emlElement = createEmlElement(schemaLocation);

state.eml.setSchemaLocation(emlElement);

expect($(emlElement).attr("xsi:schemaLocation")).to.equal(
schemaLocation,
);
});

it("overwrites the schema location when existing is invalid", function () {
const invalidLocation = "invalid-schema-location";
const emlElement = createEmlElement(invalidLocation);

state.eml.setSchemaLocation(emlElement);

expect($(emlElement).attr("xsi:schemaLocation")).to.equal(
schemaLocation,
);
});
});
});
});