From b5e71f58a4eb3a5ae56b1144b9082e43e43a54c7 Mon Sep 17 00:00:00 2001 From: Robyn Thiessen-Bock Date: Wed, 25 Oct 2023 15:09:28 -0400 Subject: [PATCH 1/5] Fix taxa persisting between editor sessions bug Fixes #2196 --- src/js/views/metadata/EML211View.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/js/views/metadata/EML211View.js b/src/js/views/metadata/EML211View.js index 7daf5571b..700595a1b 100644 --- a/src/js/views/metadata/EML211View.js +++ b/src/js/views/metadata/EML211View.js @@ -2347,8 +2347,8 @@ define(['underscore', 'jquery', 'backbone', if (taxonCoverages && taxonCoverages.length >= 1){ const taxonCoverage = taxonCoverages[0]; const classifications = taxonCoverage.get("taxonomicClassification"); - classifications.push(...newClassifications); - taxonCoverage.set("taxonomicClassification", classifications); + const allClass = classifications.concat(newClassifications); + taxonCoverage.set("taxonomicClassification", allClass); } else { // If there is no element for some reason, // create one and add the new taxon to its From aebacfb1fe04cf4554844f0d6ca771ff1b11c695 Mon Sep 17 00:00:00 2001 From: Robyn Thiessen-Bock Date: Wed, 25 Oct 2023 16:11:26 -0400 Subject: [PATCH 2/5] Improve error handling of view service response Fixes #2144 --- src/js/views/MetadataIndexView.js | 147 ++++++++++++++++-------------- src/js/views/MetadataView.js | 62 +++++++------ 2 files changed, 111 insertions(+), 98 deletions(-) diff --git a/src/js/views/MetadataIndexView.js b/src/js/views/MetadataIndexView.js index 3d1bfc197..99910ff88 100644 --- a/src/js/views/MetadataIndexView.js +++ b/src/js/views/MetadataIndexView.js @@ -65,85 +65,92 @@ define(['jquery', encodeURIComponent(this.pid)+'")&rows=1&start=0&fl=*&wt=json'; var requestSettings = { url: MetacatUI.appModel.get('queryServiceUrl') + query, - success: function(data, textStatus, xhr){ + success: function (data, textStatus, xhr) { - if(data.response.numFound == 0){ + try { - if( view.parentView && view.parentView.model ){ + if (!data?.response?.numFound) { - //Show a "not indexed" message if there is system metadata but nothing in - // the index - if(view.parentView.model.get("systemMetadata")){ - view.showNotIndexed(); - } - //Show a "not found" message if there is no system metadata and no results in the index - else{ - view.parentView.model.set("notFound", true); - view.parentView.showNotFound(); - } - } + if (view.parentView && view.parentView.model) { - view.flagComplete(); - } - else{ - view.docs = data.response.docs; + //Show a "not indexed" message if there is system metadata but nothing in + // the index + if (view.parentView.model.get("systemMetadata")) { + view.showNotIndexed(); + } + //Show a "not found" message if there is no system metadata and no results in the index + else { + view.parentView.model.set("notFound", true); + view.parentView.showNotFound(); + } + } - _.each(data.response.docs, function(doc, i, list){ + view.flagComplete(); + } + else { + view.docs = data.response.docs; - //If this is a data object and there is a science metadata doc that describes it, then navigate to that Metadata View. - if((doc.formatType == "DATA") && (doc.isDocumentedBy && doc.isDocumentedBy.length)){ - view.onClose(); - MetacatUI.uiRouter.navigate("view/" + doc.isDocumentedBy[0], true); - return; - } + _.each(data.response.docs, function (doc, i, list) { - var metadataEl = $(document.createElement("section")).attr("id", "metadata-index-details"), - id = doc.id, - creator = doc.origin, - title = doc.title, - pubDate = doc.pubDate, - dateUploaded = doc.dateUploaded, - keys = Object.keys(doc), - docModel = new SolrResult(doc); - - //Extract General Info details that we want to list first - var generalInfoKeys = ["title", "id", "abstract", "pubDate", "keywords"]; - keys = _.difference(keys, generalInfoKeys); - $(metadataEl).append(view.formatAttributeSection(docModel, generalInfoKeys, "General")); - - //Extract Spatial details - var spatialKeys = ["site", "southBoundCoord", "northBoundCoord", "westBoundCoord", "eastBoundCoord"]; - keys = _.difference(keys, spatialKeys); - $(metadataEl).append(view.formatAttributeSection(docModel, spatialKeys, "Geographic Region")); - - //Extract Temporal Coverage details - var temporalKeys = ["beginDate", "endDate"]; - keys = _.difference(keys, temporalKeys); - $(metadataEl).append(view.formatAttributeSection(docModel, temporalKeys, "Temporal Coverage")); - - //Extract Taxonomic Coverage details - var taxonKeys = ["order", "phylum", "family", "genus", "species", "scientificName"]; - keys = _.difference(keys, taxonKeys); - $(metadataEl).append(view.formatAttributeSection(docModel, taxonKeys, "Taxonomic Coverage")); - - //Extract People details - var peopleKeys = ["origin", "investigator", "contactOrganization", "project"]; - keys = _.difference(keys, peopleKeys); - $(metadataEl).append(view.formatAttributeSection(docModel, peopleKeys, "People and Associated Parties")); - - //Extract Access Control details - var accessKeys = ["isPublic", "submitter", "rightsHolder", "writePermission", "readPermission", "changePermission", "authoritativeMN"]; - keys = _.difference(keys, accessKeys); - $(metadataEl).append(view.formatAttributeSection(docModel, accessKeys, "Access Control")); - - //Add the rest of the metadata - $(metadataEl).append(view.formatAttributeSection(docModel, keys, "Other")); - - view.$el.html(metadataEl); + //If this is a data object and there is a science metadata doc that describes it, then navigate to that Metadata View. + if ((doc.formatType == "DATA") && (doc.isDocumentedBy && doc.isDocumentedBy.length)) { + view.onClose(); + MetacatUI.uiRouter.navigate("view/" + doc.isDocumentedBy[0], true); + return; + } - view.flagComplete(); - }); + var metadataEl = $(document.createElement("section")).attr("id", "metadata-index-details"), + id = doc.id, + creator = doc.origin, + title = doc.title, + pubDate = doc.pubDate, + dateUploaded = doc.dateUploaded, + keys = Object.keys(doc), + docModel = new SolrResult(doc); + + //Extract General Info details that we want to list first + var generalInfoKeys = ["title", "id", "abstract", "pubDate", "keywords"]; + keys = _.difference(keys, generalInfoKeys); + $(metadataEl).append(view.formatAttributeSection(docModel, generalInfoKeys, "General")); + + //Extract Spatial details + var spatialKeys = ["site", "southBoundCoord", "northBoundCoord", "westBoundCoord", "eastBoundCoord"]; + keys = _.difference(keys, spatialKeys); + $(metadataEl).append(view.formatAttributeSection(docModel, spatialKeys, "Geographic Region")); + + //Extract Temporal Coverage details + var temporalKeys = ["beginDate", "endDate"]; + keys = _.difference(keys, temporalKeys); + $(metadataEl).append(view.formatAttributeSection(docModel, temporalKeys, "Temporal Coverage")); + + //Extract Taxonomic Coverage details + var taxonKeys = ["order", "phylum", "family", "genus", "species", "scientificName"]; + keys = _.difference(keys, taxonKeys); + $(metadataEl).append(view.formatAttributeSection(docModel, taxonKeys, "Taxonomic Coverage")); + + //Extract People details + var peopleKeys = ["origin", "investigator", "contactOrganization", "project"]; + keys = _.difference(keys, peopleKeys); + $(metadataEl).append(view.formatAttributeSection(docModel, peopleKeys, "People and Associated Parties")); + + //Extract Access Control details + var accessKeys = ["isPublic", "submitter", "rightsHolder", "writePermission", "readPermission", "changePermission", "authoritativeMN"]; + keys = _.difference(keys, accessKeys); + $(metadataEl).append(view.formatAttributeSection(docModel, accessKeys, "Access Control")); + + //Add the rest of the metadata + $(metadataEl).append(view.formatAttributeSection(docModel, keys, "Other")); + + view.$el.html(metadataEl); + + view.flagComplete(); + }); + } + } catch (e) { + console.log("Error parsing Solr response: " + e); + console.log("Solr response: " + data); + view.parentView.showNotFound(); } }, error: function(){ diff --git a/src/js/views/MetadataView.js b/src/js/views/MetadataView.js index 23a878855..2564a90c9 100644 --- a/src/js/views/MetadataView.js +++ b/src/js/views/MetadataView.js @@ -386,47 +386,53 @@ define(['jquery', var loadSettings = { url: endpoint, success: function (response, status, xhr) { + try { - //If the user has navigated away from the MetadataView, then don't render anything further - if (MetacatUI.appView.currentView != viewRef) - return; - - //Our fallback is to show the metadata details from the Solr index - if (status == "error") - viewRef.renderMetadataFromIndex(); - else { - //Check for a response that is a 200 OK status, but is an error msg - if ((response.length < 250) && (response.indexOf("Error transforming document") > -1) && viewRef.model.get("indexed")) { - viewRef.renderMetadataFromIndex(); + //If the user has navigated away from the MetadataView, then don't render anything further + if (MetacatUI.appView.currentView != viewRef) return; - } - //Mark this as a metadata doc with no stylesheet, or one that is at least different than usual EML and FGDC - else if ((response.indexOf('id="Metadata"') == -1)) { - viewRef.$el.addClass("container no-stylesheet"); - if (viewRef.model.get("indexed")) { + //Our fallback is to show the metadata details from the Solr index + if (status == "error" || !response || typeof response !== "string") + viewRef.renderMetadataFromIndex(); + else { + //Check for a response that is a 200 OK status, but is an error msg + if ((response.length < 250) && (response.indexOf("Error transforming document") > -1) && viewRef.model.get("indexed")) { viewRef.renderMetadataFromIndex(); return; } - } + //Mark this as a metadata doc with no stylesheet, or one that is at least different than usual EML and FGDC + else if ((response.indexOf('id="Metadata"') == -1)) { + viewRef.$el.addClass("container no-stylesheet"); + + if (viewRef.model.get("indexed")) { + viewRef.renderMetadataFromIndex(); + return; + } + } - //Now show the response from the view service - viewRef.$(viewRef.metadataContainer).html(response); + //Now show the response from the view service + viewRef.$(viewRef.metadataContainer).html(response); - //If there is no info from the index and there is no metadata doc rendered either, then display a message - if (viewRef.$el.is(".no-stylesheet") && viewRef.model.get("archived") && !viewRef.model.get("indexed")) - viewRef.$(viewRef.metadataContainer).prepend(viewRef.alertTemplate({ msg: "There is limited metadata about this dataset since it has been archived." })); + //If there is no info from the index and there is no metadata doc rendered either, then display a message + if (viewRef.$el.is(".no-stylesheet") && viewRef.model.get("archived") && !viewRef.model.get("indexed")) + viewRef.$(viewRef.metadataContainer).prepend(viewRef.alertTemplate({ msg: "There is limited metadata about this dataset since it has been archived." })); - viewRef.alterMarkup(); + viewRef.alterMarkup(); - viewRef.trigger("metadataLoaded"); + viewRef.trigger("metadataLoaded"); - //Add a map of the spatial coverage - if (gmaps) viewRef.insertSpatialCoverageMap(); + //Add a map of the spatial coverage + if (gmaps) viewRef.insertSpatialCoverageMap(); - // Injects Clipboard objects into DOM elements returned from the View Service - viewRef.insertCopiables(); + // Injects Clipboard objects into DOM elements returned from the View Service + viewRef.insertCopiables(); + } + } catch (e) { + console.log("Error rendering metadata from the view service", e); + console.log("Response from the view service: ", response); + viewRef.renderMetadataFromIndex(); } }, error: function (xhr, textStatus, errorThrown) { From acc05af65ebcbdac4a7f310c0d21ed4705e69208 Mon Sep 17 00:00:00 2001 From: Robyn Thiessen-Bock Date: Thu, 26 Oct 2023 16:44:17 -0400 Subject: [PATCH 3/5] Prevent weird TOC placement in portals - Add min height to markdown sections with portals - Also rename methods from postRender to what they do, because backbone calls these methods automatically even though this isn't documented! fixes #2195 --- src/js/themes/dataone/css/metacatui.css | 4 ++++ src/js/views/MarkdownView.js | 22 ++++------------------ src/js/views/TOCView.js | 20 +++++++++++++------- src/js/views/portals/PortalSectionView.js | 4 +--- 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/src/js/themes/dataone/css/metacatui.css b/src/js/themes/dataone/css/metacatui.css index 625095593..90614feb4 100644 --- a/src/js/themes/dataone/css/metacatui.css +++ b/src/js/themes/dataone/css/metacatui.css @@ -479,6 +479,10 @@ width: 100%; color: var(--c-neutral-8); } +.toc-view + .markdown { + min-height: 90vh; +} + /* FORM/INPUT CUSTOMIZATIONS -------------------------------------------------- */ .depth { diff --git a/src/js/views/MarkdownView.js b/src/js/views/MarkdownView.js index ada6ecd33..41be210b3 100644 --- a/src/js/views/MarkdownView.js +++ b/src/js/views/MarkdownView.js @@ -88,7 +88,7 @@ define([ "jquery", "underscore", "backbone", * render - Renders the MarkdownView; converts markdown to HTML and * displays it. */ - render: function() { + render: function () { // Show a loading message while we render the markdown to HTML this.$el.html(this.loadingTemplate({ @@ -143,15 +143,10 @@ define([ "jquery", "underscore", "backbone", this.$el.html(this.template({ markdown: htmlFromMD })); if( this.showTOC ){ - this.listenToOnce(this, "TOCRendered", function(){ - this.trigger("mdRendered"); - this.postRender(); - }); this.renderTOC(); - } else { - this.trigger("mdRendered"); - this.postRender(); } + + this.trigger("mdRendered"); }); @@ -162,15 +157,6 @@ define([ "jquery", "underscore", "backbone", }, - postRender: function(){ - if(this.tocView){ - this.tocView.postRender(); - } else { - this.listenToOnce(this, "TOCRendered", function(){ - this.tocView.postRender(); - }); - } - }, /** * listRequiredExtensions - test which extensions are needed, then load @@ -378,7 +364,7 @@ define([ "jquery", "underscore", "backbone", view.$el.addClass("span9"); } - view.trigger("TOCRendered"); + view.tocView.setAffix(); }); diff --git a/src/js/views/TOCView.js b/src/js/views/TOCView.js index bcfeb4dca..1e68b66db 100644 --- a/src/js/views/TOCView.js +++ b/src/js/views/TOCView.js @@ -121,7 +121,6 @@ define(["jquery", } - var view = this; return this; }, @@ -278,11 +277,11 @@ define(["jquery", // Add scroll spy $("body").off("activate"); - $("body").on("activate", function(e){ + $("body").on("activate", function (e) { view.scrollSpyExtras(e); }); $(window).off("resize"); - $(window).on("resize", function(){ + $(window).on("resize", function () { $spy.scrollspy("refresh"); }); @@ -294,19 +293,26 @@ define(["jquery", /** - * affixTOC - description + * Adds and refreshes bootstrap's affix functionality. This function + * should be called after the DOM has been rendered or updated. Renamed + * from postRender to avoid it being called automatically by Backbone. + * @since x.x.x */ - postRender: function(){ + setAffix: function(){ try { var isVisible = this.$el.find(":visible").length > 0; - if(this.affix === true && isVisible){ + if(!isVisible || !this.$el.offset()){ + return; + } + + if (this.affix === true) { this.$el.affix({ offset: this.$el.offset().top }); } - if(this.addScrollspy && isVisible){ + if(this.addScrollspy){ this.renderScrollspy(); } diff --git a/src/js/views/portals/PortalSectionView.js b/src/js/views/portals/PortalSectionView.js index 38da5f54e..afb164607 100644 --- a/src/js/views/portals/PortalSectionView.js +++ b/src/js/views/portals/PortalSectionView.js @@ -144,9 +144,7 @@ define(["jquery", } }); - if(this.markdownView){ - this.markdownView.postRender(); - } + this.markdownView?.tocView?.setAffix(); }, /** From ea18e1100a7465c4704d7bb99216ea2b0e14b4fe Mon Sep 17 00:00:00 2001 From: Robyn Thiessen-Bock Date: Thu, 26 Oct 2023 18:17:15 -0400 Subject: [PATCH 4/5] Adjust height of feature info panel in Cesium map again after content has been loaded. Fixes #2192 --- src/js/views/maps/FeatureInfoView.js | 38 +++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/src/js/views/maps/FeatureInfoView.js b/src/js/views/maps/FeatureInfoView.js index 2e64ee8f6..6aa935474 100644 --- a/src/js/views/maps/FeatureInfoView.js +++ b/src/js/views/maps/FeatureInfoView.js @@ -256,6 +256,8 @@ define( try { + const view = this; + // Elements to update const title = this.getFeatureTitle() const iFrame = this.elements.iFrame @@ -275,12 +277,17 @@ define( this.elements.title.innerHTML = title // Update the iFrame content - iFrame.height = 0; this.getContent().then(function (html) { iFrameDiv.innerHTML = html; - const maxHeight = window.innerHeight - 275; - const scrollHeight = iFrame.contentWindow.document.body.scrollHeight + 5; - iFrame.height = scrollHeight > maxHeight ? maxHeight : scrollHeight; + view.updateIFrameHeight(); + // Not the ideal solution, but check the height of the iFrame + // again after some time to allow external content to load. This + // is necessary for content that loads asynchronously, like + // images. Difficult to set listeners for this, since the content + // may be from a different domain. + setTimeout(function () { + view.updateIFrameHeight(); + }, 850); }) // Show or hide the layer details button, update the text @@ -299,6 +306,29 @@ define( } }, + /** + * Update the height of the iFrame to match the height of the content + * within it. + * @param {number} [height] The height to set the iFrame to. If no + * height is provided, then the height of the content within the iFrame + * will be used. + * @param {boolean} [limit=true] Whether or not to limit the height of + * the iFrame to the height of the window, minus 275px. + * @since x.x.x + */ + updateIFrameHeight: function (height, limit = true) { + const iFrame = this.elements?.iFrame; + if (!iFrame) return; + if ((!height && height !== 0) || height < 0) { + height = iFrame.contentWindow.document.body.scrollHeight + 5; + } + if (limit) { + const maxHeight = window.innerHeight - 275; + height = height > maxHeight ? maxHeight : height; + } + iFrame.style.height = height + "px"; + }, + /** * Get the inner HTML content to insert into the iFrame. The content will vary * based on the feature and if there is a template set on the parent Map Asset From 0e7c60491b9fe14de56546300d3522682f680d5a Mon Sep 17 00:00:00 2001 From: Robyn Thiessen-Bock Date: Thu, 26 Oct 2023 18:22:57 -0400 Subject: [PATCH 5/5] Allow all users to set datasets to private on KNB Fixes #2215 --- src/js/themes/knb/config.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/js/themes/knb/config.js b/src/js/themes/knb/config.js index 02a702cfc..9f81191fe 100644 --- a/src/js/themes/knb/config.js +++ b/src/js/themes/knb/config.js @@ -45,7 +45,6 @@ MetacatUI.AppConfig = Object.assign({ read: true }], hiddenSubjectsInAccessPolicy: ["CN=knb-data-admins,DC=dataone,DC=org"], - showDatasetPublicToggleForSubjects: ["CN=knb-data-admins,DC=dataone,DC=org"], allowChangeRightsHolder: false, enableMeasurementTypeView: true,