diff --git a/doc/sphinx-guides/source/admin/metadataexport.rst b/doc/sphinx-guides/source/admin/metadataexport.rst index 8c50ceacd84..c6ebef0ce15 100644 --- a/doc/sphinx-guides/source/admin/metadataexport.rst +++ b/doc/sphinx-guides/source/admin/metadataexport.rst @@ -7,7 +7,12 @@ Metadata Export Automatic Exports ----------------- -Unlike in DVN v3, publishing a dataset in Dataverse 4 automaticalliy starts a metadata export job, that will run in the background, asynchronously. Once completed, it will make the dataset metadata exported and cached in all the supported formats (Dublin Core, Data Documentation Initiative (DDI), and native JSON). There is no need to run the export manually. +Publishing a dataset automatically starts a metadata export job, that will run in the background, asynchronously. Once completed, it will make the dataset metadata exported and cached in all the supported formats: + +- Dublin Core +- Data Documentation Initiative (DDI) +- Schema.org JSON-LD +- native JSON (Dataverse-specific) A scheduled timer job that runs nightly will attempt to export any published datasets that for whatever reason haven't been exported yet. This timer is activated automatically on the deployment, or restart, of the application. So, again, no need to start or configure it manually. (See the "Application Timers" section of this guide for more information) @@ -28,4 +33,4 @@ Note, that creating, modifying, or re-exporting an OAI set will also attempt to Export Failures --------------- -An export batch job, whether started via the API, or by the application timer, will leave a detailed log in your configured logs directory. This is the same location where your main Glassfish server.log is found. The name of the log file is ``export_[timestamp].log`` - for example, *export_2016-08-23T03-35-23.log*. The log will contain the numbers of datasets processed successfully and those for which metadata export failed, with some information on the failures detected. Please attach this log file if you need to contact Dataverse support about metadata export problems. \ No newline at end of file +An export batch job, whether started via the API, or by the application timer, will leave a detailed log in your configured logs directory. This is the same location where your main Glassfish server.log is found. The name of the log file is ``export_[timestamp].log`` - for example, *export_2016-08-23T03-35-23.log*. The log will contain the numbers of datasets processed successfully and those for which metadata export failed, with some information on the failures detected. Please attach this log file if you need to contact Dataverse support about metadata export problems. diff --git a/doc/sphinx-guides/source/api/native-api.rst b/doc/sphinx-guides/source/api/native-api.rst index 756966a610c..7b3659d31e3 100644 --- a/doc/sphinx-guides/source/api/native-api.rst +++ b/doc/sphinx-guides/source/api/native-api.rst @@ -152,7 +152,7 @@ Delete the dataset whose id is passed:: GET http://$SERVER/api/datasets/export?exporter=ddi&persistentId=$persistentId -.. note:: Supported exporters (export formats) are ``ddi``, ``oai_ddi``, ``dcterms``, ``oai_dc``, and ``dataverse_json``. +.. note:: Supported exporters (export formats) are ``ddi``, ``oai_ddi``, ``dcterms``, ``oai_dc``, ``schema.org`` , and ``dataverse_json``. |CORS| Lists all the file metadata, for the given dataset and version:: diff --git a/doc/sphinx-guides/source/installation/config.rst b/doc/sphinx-guides/source/installation/config.rst index 90db8d55afb..6c3335bc414 100644 --- a/doc/sphinx-guides/source/installation/config.rst +++ b/doc/sphinx-guides/source/installation/config.rst @@ -385,6 +385,13 @@ Once you have the location of your custom header HTML file, run this curl comman ``curl -X PUT -d '/var/www/dataverse/branding/custom-header.html' http://localhost:8080/api/admin/settings/:HeaderCustomizationFile`` +If you have enabled a custom header or navbar logo, you might prefer to disable the theme of the root dataverse. You can do so by setting ``:DisableRootDataverseTheme`` to ``true`` like this: + +``curl -X PUT -d 'true' http://localhost:8080/api/admin/settings/:DisableRootDataverseTheme`` + +Please note: Disabling the display of the root dataverse theme also disables your ability to edit it. Remember that dataverse owners can set their dataverses to "inherit theme" from the root. Those dataverses will continue to inherit the root dataverse theme (even though it no longer displays on the root). If you would like to edit the root dataverse theme in the future, you will have to re-enable it first. + + Custom Footer +++++++++++++ @@ -679,6 +686,11 @@ See :ref:`Branding Your Installation` above. See :ref:`Branding Your Installation` above. +:DisableRootDataverseTheme +++++++++++++++++++++++++++ + +See :ref:`Branding Your Installation` above. + :FooterCustomizationFile ++++++++++++++++++++++++ diff --git a/doc/sphinx-guides/source/user/dataverse-management.rst b/doc/sphinx-guides/source/user/dataverse-management.rst index be12c3b32aa..233a2fa0b0c 100755 --- a/doc/sphinx-guides/source/user/dataverse-management.rst +++ b/doc/sphinx-guides/source/user/dataverse-management.rst @@ -61,7 +61,7 @@ Tip: The metadata fields you select as required will appear on the Create Datase Theme ==================================================== -The Theme feature provides you with a way to customize the look of your dataverse. You can decide either to use the customization from the dataverse above yours or upload your own image file. Supported image types are JPEG, TIFF, or PNG and should be no larger than 500 KB. The maximum display size for an image file in a dataverse's theme is 940 pixels wide by 120 pixels high. Additionally, you can select the colors for the header of your dataverse and the text that appears in your dataverse. You can also add a link to your personal website, the website for your organization or institution, your department, journal, etc. +The Theme feature provides you with a way to customize the look of your dataverse. You can decide either to use the theme from the dataverse containing your dataverse (even up to the root dataverse, AKA the homepage), or upload your own image file. Supported image types are JPEG, TIFF, or PNG and should be no larger than 500 KB. The maximum display size for an image file in a dataverse's theme is 940 pixels wide by 120 pixels high. Additionally, you can select the colors for the header of your dataverse and the text that appears in your dataverse. You can also add a link to your personal website, the website for your organization or institution, your department, journal, etc. .. _dataverse-widgets: diff --git a/src/main/java/Bundle.properties b/src/main/java/Bundle.properties index fe0f3e8d3f6..e9b1f43902f 100755 --- a/src/main/java/Bundle.properties +++ b/src/main/java/Bundle.properties @@ -753,9 +753,9 @@ dataverse.results.cards.foundInMetadata=Found in Metadata Fields: dataverse.results.cards.files.tabularData=Tabular Data dataverse.results.solrIsDown=Please note: Due to an internal error, browsing and searching is not available. dataverse.theme.title=Theme -dataverse.theme.inheritCustomization.title=Check this to use the existing theme. -dataverse.theme.inheritCustomization.label=Inherit Customization -dataverse.theme.inheritCustomization.checkbox=Inherit customization from {0} +dataverse.theme.inheritCustomization.title=For this dataverse, use the same theme as the parent dataverse. +dataverse.theme.inheritCustomization.label=Inherit Theme +dataverse.theme.inheritCustomization.checkbox=Inherit theme from {0} dataverse.theme.logo=Logo dataverse.theme.logo.tip=Supported image types are JPG, TIF, or PNG and should be no larger than 500 KB. The maximum display size for an image file in a dataverse's theme is 940 pixels wide by 120 pixels high. dataverse.theme.logo.format=Logo Format @@ -798,6 +798,7 @@ dataverse.theme.website.title=URL for your personal website, institution, or any dataverse.theme.website.tip=The website will be linked behind the tagline. To have a website listed, you must also provide a tagline. dataverse.theme.website.watermark=Your personal site, http://... dataverse.theme.website.invalidMsg=Invalid URL. +dataverse.theme.disabled=The theme for the root dataverse has been administratively disabled with the :DisableRootDataverseTheme database setting. dataverse.widgets.title=Widgets dataverse.widgets.notPublished.why.header=Why Use Widgets? dataverse.widgets.notPublished.why.reason1=Increases the web visibility of your data by allowing you to embed your dataverse and datasets into your personal or project website. @@ -1110,6 +1111,7 @@ dataset.editBtn.itemLabel.deaccession=Deaccession Dataset dataset.exportBtn=Export Metadata dataset.exportBtn.itemLabel.ddi=DDI dataset.exportBtn.itemLabel.dublinCore=Dublin Core +dataset.exportBtn.itemLabel.schemaDotOrg=Schema.org JSON-LD dataset.exportBtn.itemLabel.json=JSON metrics.title=Metrics metrics.title.tip=View more metrics information @@ -1344,7 +1346,8 @@ file.spss-savEncoding.current=Current Selection: file.spss-porExtraLabels=Variable Labels file.spss-porExtraLabels.title=Upload an additional text file with extra variable labels. file.spss-porExtraLabels.selectToAddBtn=Select File to Add -file.ingestFailed=Tabular Data Ingest Failed +file.ingestFailed.header=Upload Completed with Errors +file.ingestFailed.message=Tabular data ingest failed. file.explore.twoRavens=TwoRavens file.map=Map file.mapData=Map Data diff --git a/src/main/java/edu/harvard/iq/dataverse/DOIDataCiteRegisterService.java b/src/main/java/edu/harvard/iq/dataverse/DOIDataCiteRegisterService.java index 17a6b1759eb..e058ecd4b83 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DOIDataCiteRegisterService.java +++ b/src/main/java/edu/harvard/iq/dataverse/DOIDataCiteRegisterService.java @@ -268,7 +268,7 @@ public String generateXML() { if (author.getIdType() != null && author.getIdValue() != null && !author.getIdType().isEmpty() && !author.getIdValue().isEmpty() && author.getAffiliation() != null && !author.getAffiliation().getDisplayValue().isEmpty()) { if (author.getIdType().equals("ORCID")) { - creatorsElement.append("" + author.getIdValue() + ""); + creatorsElement.append("" + author.getIdValue() + ""); } if (author.getIdType().equals("ISNI")) { creatorsElement.append("" + author.getIdValue() + ""); diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java b/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java index 2cae4aae9f6..d83ca7a645c 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetPage.java @@ -80,11 +80,13 @@ import edu.harvard.iq.dataverse.datasetutility.TwoRavensHelper; import edu.harvard.iq.dataverse.datasetutility.WorldMapPermissionHelper; import edu.harvard.iq.dataverse.engine.command.exception.IllegalCommandException; +import edu.harvard.iq.dataverse.engine.command.impl.GetLatestPublishedDatasetVersionCommand; import edu.harvard.iq.dataverse.engine.command.impl.RequestRsyncScriptCommand; import edu.harvard.iq.dataverse.engine.command.impl.PublishDatasetResult; import edu.harvard.iq.dataverse.engine.command.impl.RestrictFileCommand; import edu.harvard.iq.dataverse.engine.command.impl.ReturnDatasetToAuthorCommand; import edu.harvard.iq.dataverse.engine.command.impl.SubmitDatasetForReviewCommand; +import edu.harvard.iq.dataverse.export.SchemaDotOrgExporter; import java.util.Collections; import javax.faces.event.AjaxBehaviorEvent; @@ -3944,23 +3946,6 @@ public String getDescription() { return workingVersion.getDescriptionPlainText(); } - /** - * dataset publication date unpublished datasets will return an empty - * string. - * - * @return String dataset publication date (dd MMM yyyy). - */ - public String getPublicationDate() { - assert (null != workingVersion); - if (DatasetVersion.VersionState.DRAFT == workingVersion.getVersionState()) { - return ""; - } - Date rel_date = workingVersion.getReleaseTime(); - SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); - String r = fmt.format(rel_date.getTime()); - return r; - } - /** * dataset authors * @@ -3971,16 +3956,6 @@ public List getDatasetAuthors() { return workingVersion.getDatasetAuthorNames(); } - /** - * dataset subjects - * - * @return array of String containing the subjects for a page - */ - public List getDatasetSubjects() { - assert (null != workingVersion); - return workingVersion.getDatasetSubjects(); - } - /** * publisher (aka - name of root dataverse) * @@ -4066,4 +4041,44 @@ public List getDatasetSummaryFields() { return DatasetUtil.getDatasetSummaryFields(workingVersion, customFields); } + Boolean thisLatestReleasedVersion = null; + + public boolean isThisLatestReleasedVersion() { + if (thisLatestReleasedVersion != null) { + return thisLatestReleasedVersion; + } + + if (!workingVersion.isPublished()) { + thisLatestReleasedVersion = false; + return false; + } + + DatasetVersion latestPublishedVersion = null; + Command cmd = new GetLatestPublishedDatasetVersionCommand(dvRequestService.getDataverseRequest(), dataset); + try { + latestPublishedVersion = commandEngine.submit(cmd); + } catch (Exception ex) { + // whatever... + } + + thisLatestReleasedVersion = workingVersion.equals(latestPublishedVersion); + + return thisLatestReleasedVersion; + + } + + public String getJsonLd() { + if (isThisLatestReleasedVersion()) { + ExportService instance = ExportService.getInstance(settingsService); + String jsonLd = instance.getExportAsString(dataset, SchemaDotOrgExporter.NAME); + if (jsonLd != null) { + logger.fine("Returning cached schema.org JSON-LD."); + return jsonLd; + } else { + logger.fine("No cached schema.org JSON-LD available. Going to the database."); + return workingVersion.getJsonLd(); + } + } + return ""; + } } diff --git a/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java b/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java index 030a10244a2..ca5791786a7 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java +++ b/src/main/java/edu/harvard/iq/dataverse/DatasetVersion.java @@ -3,8 +3,10 @@ import edu.harvard.iq.dataverse.util.MarkupChecker; import edu.harvard.iq.dataverse.DatasetFieldType.FieldType; import edu.harvard.iq.dataverse.util.StringUtil; +import edu.harvard.iq.dataverse.util.SystemConfig; import edu.harvard.iq.dataverse.workflows.WorkflowComment; import java.io.Serializable; +import java.math.BigDecimal; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -17,6 +19,9 @@ import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import javax.json.Json; +import javax.json.JsonArrayBuilder; +import javax.json.JsonObjectBuilder; import javax.persistence.CascadeType; import javax.persistence.Column; import javax.persistence.Entity; @@ -142,6 +147,9 @@ public enum License { @Transient private String contributorNames; + + @Transient + private String jsonLd; @OneToMany(mappedBy="datasetVersion", cascade={CascadeType.REMOVE, CascadeType.MERGE, CascadeType.PERSIST}) private List datasetVersionUsers; @@ -417,6 +425,10 @@ public boolean isReleased() { return versionState.equals(VersionState.RELEASED); } + public boolean isPublished() { + return isReleased(); + } + public boolean isDraft() { return versionState.equals(VersionState.DRAFT); } @@ -706,6 +718,42 @@ public List getDatasetAuthors() { return retList; } + public List getTimePeriodsCovered() { + List retList = new ArrayList<>(); + for (DatasetField dsf : this.getDatasetFields()) { + if (dsf.getDatasetFieldType().getName().equals(DatasetFieldConstant.timePeriodCovered)) { + for (DatasetFieldCompoundValue timePeriodValue : dsf.getDatasetFieldCompoundValues()) { + String start = ""; + String end = ""; + for (DatasetField subField : timePeriodValue.getChildDatasetFields()) { + if (subField.getDatasetFieldType().getName().equals(DatasetFieldConstant.timePeriodCoveredStart)) { + if (subField.isEmptyForDisplay()) { + start = null; + } else { + // we want to use "getValue()", as opposed to "getDisplayValue()" here - + // as the latter method prepends the value with the word "Start:"! + start = subField.getValue(); + } + } + if (subField.getDatasetFieldType().getName().equals(DatasetFieldConstant.timePeriodCoveredEnd)) { + if (subField.isEmptyForDisplay()) { + end = null; + } else { + // see the comment above + end = subField.getValue(); + } + } + + } + if (start != null && end != null) { + retList.add(start + "/" + end); + } + } + } + } + return retList; + } + /** * @return List of Strings containing the names of the authors. */ @@ -729,7 +777,55 @@ public List getDatasetSubjects() { } return subjects; } - + + /** + * @return List of Strings containing the version's Topic Classifications + */ + public List getTopicClassifications() { + return getCompoundChildFieldValues(DatasetFieldConstant.topicClassification, DatasetFieldConstant.topicClassValue); + } + + /** + * @return List of Strings containing the version's Keywords + */ + public List getKeywords() { + return getCompoundChildFieldValues(DatasetFieldConstant.keyword, DatasetFieldConstant.keywordValue); + } + + /** + * @return List of Strings containing the version's PublicationCitations + */ + public List getPublicationCitationValues() { + return getCompoundChildFieldValues(DatasetFieldConstant.publication, DatasetFieldConstant.publicationCitation); + } + + /** + * @param parentFieldName compound dataset field A (from DatasetFieldConstant.*) + * @param childFieldName dataset field B, child field of A (from DatasetFieldConstant.*) + * @return List of values of the child field + */ + public List getCompoundChildFieldValues(String parentFieldName, String childFieldName) { + List keywords = new ArrayList<>(); + for (DatasetField dsf : this.getDatasetFields()) { + if (dsf.getDatasetFieldType().getName().equals(parentFieldName)) { + for (DatasetFieldCompoundValue keywordFieldValue : dsf.getDatasetFieldCompoundValues()) { + for (DatasetField subField : keywordFieldValue.getChildDatasetFields()) { + if (subField.getDatasetFieldType().getName().equals(childFieldName)) { + String keyword = subField.getValue(); + // Field values should NOT be empty or, especially, null, + // - in the ideal world. But as we are realizing, they CAN + // be null in real life databases. So, a check, just in case: + if (!StringUtil.isEmpty(keyword)) { + keywords.add(subField.getValue()); + } + } + } + } + } + } + return keywords; + } + public String getDatasetProducersString(){ String retVal = ""; for (DatasetField dsf : this.getDatasetFields()) { @@ -1099,4 +1195,178 @@ public List getWorkflowComments() { return workflowComments; } + /** + * dataset publication date unpublished datasets will return an empty + * string. + * + * @return String dataset publication date in ISO 8601 format (yyyy-MM-dd). + */ + public String getPublicationDateAsString() { + if (DatasetVersion.VersionState.DRAFT == this.getVersionState()) { + return ""; + } + Date rel_date = this.getReleaseTime(); + SimpleDateFormat fmt = new SimpleDateFormat("yyyy-MM-dd"); + String r = fmt.format(rel_date.getTime()); + return r; + } + + // TODO: Consider moving this comment into the Exporter code. + // The export subsystem assumes there is only + // one metadata export in a given format per dataset (it uses the current + // released (published) version. This JSON fragment is generated for a + // specific released version - and we can have multiple released versions. + // So something will need to be modified to accommodate this. -- L.A. + + public String getJsonLd() { + // We show published datasets only for "datePublished" field below. + if (!this.isPublished()) { + return ""; + } + + if (jsonLd != null) { + return jsonLd; + } + JsonObjectBuilder job = Json.createObjectBuilder(); + job.add("@context", "http://schema.org"); + job.add("@type", "Dataset"); + job.add("identifier", this.getDataset().getPersistentURL()); + job.add("name", this.getTitle()); + JsonArrayBuilder authors = Json.createArrayBuilder(); + for (DatasetAuthor datasetAuthor : this.getDatasetAuthors()) { + JsonObjectBuilder author = Json.createObjectBuilder(); + String name = datasetAuthor.getName().getValue(); + DatasetField authorAffiliation = datasetAuthor.getAffiliation(); + String affiliation = null; + if (authorAffiliation != null) { + affiliation = datasetAuthor.getAffiliation().getValue(); + } + // We are aware of "givenName" and "familyName" but instead of a person it might be an organization such as "Gallup Organization". + //author.add("@type", "Person"); + author.add("name", name); + // We are aware that the following error is thrown by https://search.google.com/structured-data/testing-tool + // "The property affiliation is not recognized by Google for an object of type Thing." + // Someone at Google has said this is ok. + // This logic could be moved into the `if (authorAffiliation != null)` block above. + if (!StringUtil.isEmpty(affiliation)) { + author.add("affiliation", affiliation); + } + authors.add(author); + } + job.add("author", authors); + /** + * We are aware that there is a "datePublished" field but it means "Date + * of first broadcast/publication." This only makes sense for a 1.0 + * version. + */ + String datePublished = this.getDataset().getPublicationDateFormattedYYYYMMDD(); + if (datePublished != null) { + job.add("datePublished", datePublished); + } + + /** + * "dateModified" is more appropriate for a version: "The date on which + * the CreativeWork was most recently modified or when the item's entry + * was modified within a DataFeed." + */ + job.add("dateModified", this.getPublicationDateAsString()); + job.add("version", this.getVersionNumber().toString()); + job.add("description", this.getDescriptionPlainText()); + /** + * "keywords" - contains subject(s), datasetkeyword(s) and topicclassification(s) + * metadata fields for the version. -- L.A. + * (see #2243 for details/discussion/feedback from Google) + */ + JsonArrayBuilder keywords = Json.createArrayBuilder(); + + for (String subject : this.getDatasetSubjects()) { + keywords.add(subject); + } + + for (String topic : this.getTopicClassifications()) { + keywords.add(topic); + } + + for (String keyword : this.getKeywords()) { + keywords.add(keyword); + } + + job.add("keywords", keywords); + + /** + * citation: + * (multiple) publicationCitation values, if present: + */ + + List publicationCitations = getPublicationCitationValues(); + if (publicationCitations.size() > 0) { + JsonArrayBuilder citation = Json.createArrayBuilder(); + for (String pubCitation : publicationCitations) { + //citationEntry.add("@type", "Dataset"); + //citationEntry.add("text", pubCitation); + citation.add(pubCitation); + } + job.add("citation", citation); + } + + /** + * temporalCoverage: + * (if available) + */ + + List timePeriodsCovered = this.getTimePeriodsCovered(); + if (timePeriodsCovered.size() > 0) { + JsonArrayBuilder temporalCoverage = Json.createArrayBuilder(); + for (String timePeriod : timePeriodsCovered) { + temporalCoverage.add(timePeriod); + } + job.add("temporalCoverage", temporalCoverage); + } + + /** + * spatialCoverage (if available) + * TODO + * (punted, for now - see #2243) + * + */ + + /** + * funder (if available) + * TODO + * (punted, for now - see #2243) + */ + + job.add("schemaVersion", "https://schema.org/version/3.3"); + + TermsOfUseAndAccess terms = this.getTermsOfUseAndAccess(); + if (terms != null) { + JsonObjectBuilder license = Json.createObjectBuilder().add("@type", "Dataset"); + + if (TermsOfUseAndAccess.License.CC0.equals(terms.getLicense())) { + license.add("text", "CC0").add("url", "https://creativecommons.org/publicdomain/zero/1.0/"); + } else { + String termsOfUse = terms.getTermsOfUse(); + // Terms of use can be null if you create the dataset with JSON. + if (termsOfUse != null) { + license.add("text", termsOfUse); + } + } + + job.add("license",license); + } + + job.add("includedInDataCatalog", Json.createObjectBuilder() + .add("@type", "DataCatalog") + .add("name", this.getRootDataverseNameforCitation()) + .add("url", SystemConfig.getDataverseSiteUrlStatic()) + ); + + job.add("provider", Json.createObjectBuilder() + .add("@type", "Organization") + .add("name", "Dataverse") + ); + jsonLd = job.build().toString(); + return jsonLd; + } + } diff --git a/src/main/java/edu/harvard/iq/dataverse/DataverseHeaderFragment.java b/src/main/java/edu/harvard/iq/dataverse/DataverseHeaderFragment.java index d3607a27093..dfe6e5e70c9 100644 --- a/src/main/java/edu/harvard/iq/dataverse/DataverseHeaderFragment.java +++ b/src/main/java/edu/harvard/iq/dataverse/DataverseHeaderFragment.java @@ -249,6 +249,18 @@ public boolean isSignupAllowed() { return signupAllowed; } + public boolean isRootDataverseThemeDisabled(Dataverse dataverse) { + if (dataverse == null) { + return false; + } + if (dataverse.getOwner() == null) { + // We're operating on the root dataverse. + return settingsWrapper.isRootDataverseThemeDisabled(); + } else { + return false; + } + } + public String getSignupUrl(String loginRedirect) { String nonNullDefaultIfKeyNotFound = ""; String signUpUrl = settingsWrapper.getValueForKey(SettingsServiceBean.Key.SignUpUrl, nonNullDefaultIfKeyNotFound); diff --git a/src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java b/src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java index 411c72ac8b7..e36f7feaec3 100644 --- a/src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java +++ b/src/main/java/edu/harvard/iq/dataverse/EjbDataverseEngine.java @@ -182,7 +182,7 @@ public R submit(Command aCommand) throws CommandException { DataverseRequest dvReq = aCommand.getRequest(); Map affectedDvObjects = aCommand.getAffectedDvObjects(); - logRec.setInfo( describe(affectedDvObjects) ); + logRec.setInfo(aCommand.describe()); for (Map.Entry> pair : requiredMap.entrySet()) { String dvName = pair.getKey(); if (!affectedDvObjects.containsKey(dvName)) { @@ -442,16 +442,5 @@ public DataCaptureModuleServiceBean dataCaptureModule() { return ctxt; } - - - private String describe( Map dvObjMap ) { - StringBuilder sb = new StringBuilder(); - for ( Map.Entry ent : dvObjMap.entrySet() ) { - DvObject value = ent.getValue(); - sb.append(ent.getKey()).append(":"); - sb.append( (value!=null) ? value.accept(DvObject.NameIdPrinter) : ""); - sb.append(" "); - } - return sb.toString(); - } + } diff --git a/src/main/java/edu/harvard/iq/dataverse/SettingsWrapper.java b/src/main/java/edu/harvard/iq/dataverse/SettingsWrapper.java index 75ae1e00b96..0dfa2d67885 100644 --- a/src/main/java/edu/harvard/iq/dataverse/SettingsWrapper.java +++ b/src/main/java/edu/harvard/iq/dataverse/SettingsWrapper.java @@ -155,5 +155,9 @@ public String getSupportTeamName() { return BrandingUtil.getSupportTeamName(systemAddress, dataverseService.findRootDataverse().getName()); } + public boolean isRootDataverseThemeDisabled() { + return isTrueForKey(Key.DisableRootDataverseTheme, false); + } + } diff --git a/src/main/java/edu/harvard/iq/dataverse/ThemeWidgetFragment.java b/src/main/java/edu/harvard/iq/dataverse/ThemeWidgetFragment.java index 17b59eb27fd..d9cecd71343 100644 --- a/src/main/java/edu/harvard/iq/dataverse/ThemeWidgetFragment.java +++ b/src/main/java/edu/harvard/iq/dataverse/ThemeWidgetFragment.java @@ -44,8 +44,8 @@ @ViewScoped @Named public class ThemeWidgetFragment implements java.io.Serializable { - static final String DEFAULT_LOGO_BACKGROUND_COLOR = "F5F5F5"; - static final String DEFAULT_BACKGROUND_COLOR = "F5F5F5"; + static final String DEFAULT_LOGO_BACKGROUND_COLOR = "FFFFFF"; + static final String DEFAULT_BACKGROUND_COLOR = "FFFFFF"; static final String DEFAULT_LINK_COLOR = "428BCA"; static final String DEFAULT_TEXT_COLOR = "888888"; private static final Logger logger = Logger.getLogger(ThemeWidgetFragment.class.getCanonicalName()); diff --git a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java index 97f3925b5ec..b75f8ae4f19 100644 --- a/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java +++ b/src/main/java/edu/harvard/iq/dataverse/api/Dataverses.java @@ -97,10 +97,6 @@ public class Dataverses extends AbstractApiBean { @Deprecated private static final Logger LOGGER = Logger.getLogger(Dataverses.class.getName()); private static final Logger logger = Logger.getLogger(Dataverses.class.getCanonicalName()); -// static final String DEFAULT_LOGO_BACKGROUND_COLOR = "F5F5F5"; -// static final String DEFAULT_BACKGROUND_COLOR = "F5F5F5"; -// static final String DEFAULT_LINK_COLOR = "428BCA"; -// static final String DEFAULT_TEXT_COLOR = "888888"; @EJB ExplicitGroupServiceBean explicitGroupSvc; diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/DataportenOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/DataportenOAuth2AP.java index 8837d57410d..24f9853af47 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/DataportenOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/DataportenOAuth2AP.java @@ -1,5 +1,7 @@ package edu.harvard.iq.dataverse.authorization.providers.oauth2.impl; +// Dataporten is a part of ScribeJava in the future https://github.com/scribejava/scribejava/pull/805 +// import com.github.scribejava.apis.DataportenApi; //Uncomment and delete DataportenApi.java when ScribeJava is updated in Maven import com.github.scribejava.core.builder.api.BaseApi; import edu.emory.mathcs.backport.java.util.Collections; import edu.harvard.iq.dataverse.authorization.AuthenticatedUserDisplayInfo; @@ -78,7 +80,7 @@ protected ParsedUserResponse parseUserResponse( String responseBody ) { return new ParsedUserResponse( displayInfo, userObject.getString("userid"), //persistentUserId - userObject.getString("email"), //username + username, //username displayInfo.getEmailAddress().length()>0 ? Collections.singletonList(displayInfo.getEmailAddress()) : Collections.emptyList() ); diff --git a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java index 030680f29c4..dbc4b0ac4e6 100644 --- a/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java +++ b/src/main/java/edu/harvard/iq/dataverse/authorization/providers/oauth2/impl/OrcidOAuth2AP.java @@ -263,7 +263,7 @@ public String getPersistentIdDescription() { @Override public String getPersistentIdUrlPrefix() { - return "http://orcid.org/"; + return "https://orcid.org/"; } @Override diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/AbstractCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/AbstractCommand.java index e4d0593835b..1876d47fc07 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/AbstractCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/AbstractCommand.java @@ -16,7 +16,7 @@ */ public abstract class AbstractCommand implements Command { - private final Map affectedDataverses; + private final Map affectedDvObjects; private final DataverseRequest request; static protected class DvNamePair { @@ -47,21 +47,21 @@ public AbstractCommand(DataverseRequest aRequest, DvObject anAffectedDvObject) { public AbstractCommand(DataverseRequest aRequest, DvNamePair dvp, DvNamePair... more) { request = aRequest; - affectedDataverses = new HashMap<>(); - affectedDataverses.put(dvp.name, dvp.dvObject); + affectedDvObjects = new HashMap<>(); + affectedDvObjects.put(dvp.name, dvp.dvObject); for (DvNamePair p : more) { - affectedDataverses.put(p.name, p.dvObject); + affectedDvObjects.put(p.name, p.dvObject); } } public AbstractCommand(DataverseRequest aRequest, Map someAffectedDvObjects) { request = aRequest; - affectedDataverses = someAffectedDvObjects; + affectedDvObjects = someAffectedDvObjects; } @Override public Map getAffectedDvObjects() { - return affectedDataverses; + return affectedDvObjects; } @Override @@ -81,4 +81,17 @@ public Map> getRequiredPermissions() { protected User getUser() { return getRequest().getUser(); } + + @Override + public String describe() { + StringBuilder sb = new StringBuilder(); + for (Map.Entry ent : affectedDvObjects.entrySet()) { + DvObject value = ent.getValue(); + sb.append(ent.getKey()).append(":"); + sb.append((value != null) ? value.accept(DvObject.NameIdPrinter) : ""); + sb.append(" "); + } + return sb.toString(); + } + } diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/Command.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/Command.java index 32a8a3cb282..c6093432092 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/Command.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/Command.java @@ -41,5 +41,6 @@ public interface Command { * @return A map of the permissions required for this command */ Map> getRequiredPermissions(); - + + public String describe(); } diff --git a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/AssignRoleCommand.java b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/AssignRoleCommand.java index 767bee92619..34263599ff0 100644 --- a/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/AssignRoleCommand.java +++ b/src/main/java/edu/harvard/iq/dataverse/engine/command/impl/AssignRoleCommand.java @@ -62,4 +62,9 @@ public Map> getRequiredPermissions() { : Collections.singleton(Permission.ManageDatasetPermissions)); } + @Override + public String describe() { + return grantee + " has been given " + role + " on " + defPoint.accept(DvObject.NameIdPrinter); + } + } diff --git a/src/main/java/edu/harvard/iq/dataverse/export/SchemaDotOrgExporter.java b/src/main/java/edu/harvard/iq/dataverse/export/SchemaDotOrgExporter.java new file mode 100644 index 00000000000..e039407fcf2 --- /dev/null +++ b/src/main/java/edu/harvard/iq/dataverse/export/SchemaDotOrgExporter.java @@ -0,0 +1,86 @@ +package edu.harvard.iq.dataverse.export; + +import com.google.auto.service.AutoService; +import edu.harvard.iq.dataverse.DatasetVersion; +import edu.harvard.iq.dataverse.export.spi.Exporter; +import edu.harvard.iq.dataverse.util.BundleUtil; +import java.io.IOException; +import java.io.OutputStream; +import java.io.StringReader; +import java.util.logging.Logger; +import javax.json.Json; +import javax.json.JsonObject; +import javax.json.JsonReader; + +@AutoService(Exporter.class) +public class SchemaDotOrgExporter implements Exporter { + + private static final Logger logger = Logger.getLogger(SchemaDotOrgExporter.class.getCanonicalName()); + + public static final String NAME = "schema.org"; + + @Override + public void exportDataset(DatasetVersion version, JsonObject json, OutputStream outputStream) throws ExportException { + String jsonLdAsString = version.getJsonLd(); + StringReader stringReader = new StringReader(jsonLdAsString); + JsonReader jsonReader = Json.createReader(stringReader); + JsonObject jsonLdJsonObject = jsonReader.readObject(); + try { + outputStream.write(jsonLdJsonObject.toString().getBytes("UTF8")); + } catch (IOException ex) { + logger.info("IOException calling outputStream.write: " + ex); + } + try { + outputStream.flush(); + } catch (IOException ex) { + logger.info("IOException calling outputStream.flush: " + ex); + } + } + + @Override + public String getProviderName() { + return NAME; + } + + @Override + public String getDisplayName() { + return BundleUtil.getStringFromBundle("dataset.exportBtn.itemLabel.schemaDotOrg"); + } + + @Override + public Boolean isXMLFormat() { + return false; + } + + @Override + public Boolean isHarvestable() { + // Defer harvesting because the current effort was estimated as a "2": https://github.com/IQSS/dataverse/issues/3700 + return false; + } + + @Override + public Boolean isAvailableToUsers() { + return true; + } + + @Override + public String getXMLNameSpace() throws ExportException { + throw new ExportException(SchemaDotOrgExporter.class.getSimpleName() + ": not an XML format."); + } + + @Override + public String getXMLSchemaLocation() throws ExportException { + throw new ExportException(SchemaDotOrgExporter.class.getSimpleName() + ": not an XML format."); + } + + @Override + public String getXMLSchemaVersion() throws ExportException { + throw new ExportException(SchemaDotOrgExporter.class.getSimpleName() + ": not an XML format."); + } + + @Override + public void setParam(String name, Object value) { + // this exporter doesn't need/doesn't currently take any parameters + } + +} diff --git a/src/main/java/edu/harvard/iq/dataverse/settings/SettingsServiceBean.java b/src/main/java/edu/harvard/iq/dataverse/settings/SettingsServiceBean.java index 94024bf5949..f163bcaea0f 100644 --- a/src/main/java/edu/harvard/iq/dataverse/settings/SettingsServiceBean.java +++ b/src/main/java/edu/harvard/iq/dataverse/settings/SettingsServiceBean.java @@ -309,6 +309,12 @@ Whether Harvesting (OAI) service is enabled // Option to override multiple guides with a single url NavbarGuidesUrl, + /** + * The theme for the root dataverse can get in the way when you try make + * use of HeaderCustomizationFile and LogoCustomizationFile so this is a + * way to disable it. + */ + DisableRootDataverseTheme, // Limit on how many guestbook entries to display on the guestbook-responses page: GuestbookResponsesPageDisplayLimit, diff --git a/src/main/java/edu/harvard/iq/dataverse/util/SystemConfig.java b/src/main/java/edu/harvard/iq/dataverse/util/SystemConfig.java index c99d99a4b05..b69631eac12 100644 --- a/src/main/java/edu/harvard/iq/dataverse/util/SystemConfig.java +++ b/src/main/java/edu/harvard/iq/dataverse/util/SystemConfig.java @@ -277,6 +277,10 @@ public static int getMinutesUntilPasswordResetTokenExpires() { * by the Settings Service configuration. */ public String getDataverseSiteUrl() { + return getDataverseSiteUrlStatic(); + } + + public static String getDataverseSiteUrlStatic() { String hostUrl = System.getProperty(SITE_URL); if (hostUrl != null && !"".equals(hostUrl)) { return hostUrl; diff --git a/src/main/webapp/dataset.xhtml b/src/main/webapp/dataset.xhtml index b425fbc8de1..08f3987b51d 100755 --- a/src/main/webapp/dataset.xhtml +++ b/src/main/webapp/dataset.xhtml @@ -24,16 +24,21 @@ - + - + + + + diff --git a/src/main/webapp/dataverse_header.xhtml b/src/main/webapp/dataverse_header.xhtml index 8ec2ced172f..83662e867d0 100644 --- a/src/main/webapp/dataverse_header.xhtml +++ b/src/main/webapp/dataverse_header.xhtml @@ -48,7 +48,7 @@ - @@ -154,11 +154,13 @@ +
+ style="background:##{!empty dataverse.dataverseTheme.backgroundColor ? dataverse.dataverseTheme.backgroundColor : 'FFFFFF'};" + jsf:rendered="#{showDataverseHeader and !widgetWrapper.widgetView and !dataverseHeaderFragment.isRootDataverseThemeDisabled(dataverse)}"> -