From 83393d9a72cd53ee7e9aa0782de94818f0d353ff Mon Sep 17 00:00:00 2001 From: corrado lombardi Date: Thu, 27 Jul 2023 13:17:25 +0200 Subject: [PATCH 01/23] [CST-1072] Added support for additional custom filter to be injected in AccessItemMode --- .../dspace/content/edit/CorrectItemMode.java | 11 +++ .../org/dspace/content/edit/EditItemMode.java | 11 +++ .../content/security/AccessItemMode.java | 4 + .../dspace/content/security/CrisSecurity.java | 3 +- .../security/CrisSecurityServiceImpl.java | 50 +++++++---- .../security/CrisSecurityServiceIT.java | 89 +++++++++++++++++++ 6 files changed, 148 insertions(+), 20 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/edit/CorrectItemMode.java b/dspace-api/src/main/java/org/dspace/content/edit/CorrectItemMode.java index b374861db9a3..2945065db4ea 100644 --- a/dspace-api/src/main/java/org/dspace/content/edit/CorrectItemMode.java +++ b/dspace-api/src/main/java/org/dspace/content/edit/CorrectItemMode.java @@ -10,6 +10,7 @@ import java.util.ArrayList; import java.util.List; +import org.dspace.content.logic.Filter; import org.dspace.content.security.AccessItemMode; import org.dspace.content.security.CrisSecurity; @@ -42,6 +43,7 @@ public class CorrectItemMode implements AccessItemMode { * Contains the list of users metadata for CUSTOM security */ private List items = new ArrayList(); + private Filter additionalFilter; @Override public List getSecurities() { @@ -87,4 +89,13 @@ public void setItems(List items) { public List getGroups() { return groups; } + + public void setAdditionalFilter(Filter additionalFilter) { + this.additionalFilter = additionalFilter; + } + + @Override + public Filter getAdditionalFilter() { + return additionalFilter; + } } diff --git a/dspace-api/src/main/java/org/dspace/content/edit/EditItemMode.java b/dspace-api/src/main/java/org/dspace/content/edit/EditItemMode.java index 4d56ddafe731..6f6b33ecaa28 100644 --- a/dspace-api/src/main/java/org/dspace/content/edit/EditItemMode.java +++ b/dspace-api/src/main/java/org/dspace/content/edit/EditItemMode.java @@ -9,6 +9,7 @@ import java.util.List; +import org.dspace.content.logic.Filter; import org.dspace.content.security.AccessItemMode; import org.dspace.content.security.CrisSecurity; @@ -49,6 +50,7 @@ public class EditItemMode implements AccessItemMode { * Contains the list of items metadata for CUSTOM security */ private List items; + private Filter additionalFilter; @Override public List getSecurities() { @@ -100,6 +102,15 @@ public void setItems(List items) { this.items = items; } + public void setAdditionalFilter(Filter additionalFilter) { + this.additionalFilter = additionalFilter; + } + + @Override + public Filter getAdditionalFilter() { + return additionalFilter; + } + @Override public List getGroups() { return groups; diff --git a/dspace-api/src/main/java/org/dspace/content/security/AccessItemMode.java b/dspace-api/src/main/java/org/dspace/content/security/AccessItemMode.java index 2aee66fed1ff..e2954bf8f83c 100644 --- a/dspace-api/src/main/java/org/dspace/content/security/AccessItemMode.java +++ b/dspace-api/src/main/java/org/dspace/content/security/AccessItemMode.java @@ -9,6 +9,8 @@ import java.util.List; +import org.dspace.content.logic.Filter; + /** * Interface to be extended for the configuration related to access item modes. * @@ -50,4 +52,6 @@ public interface AccessItemMode { * @return the group list */ public List getGroups(); + + public Filter getAdditionalFilter(); } diff --git a/dspace-api/src/main/java/org/dspace/content/security/CrisSecurity.java b/dspace-api/src/main/java/org/dspace/content/security/CrisSecurity.java index 3fcd83864175..9a472b8a40c3 100644 --- a/dspace-api/src/main/java/org/dspace/content/security/CrisSecurity.java +++ b/dspace-api/src/main/java/org/dspace/content/security/CrisSecurity.java @@ -23,6 +23,7 @@ public enum CrisSecurity { ITEM_ADMIN, SUBMITTER, SUBMITTER_GROUP, - GROUP; + GROUP, + ALL; } diff --git a/dspace-api/src/main/java/org/dspace/content/security/CrisSecurityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/security/CrisSecurityServiceImpl.java index 4a8b2c313846..3b92ca5985f4 100644 --- a/dspace-api/src/main/java/org/dspace/content/security/CrisSecurityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/security/CrisSecurityServiceImpl.java @@ -18,6 +18,7 @@ import org.dspace.content.Collection; import org.dspace.content.Item; import org.dspace.content.MetadataValue; +import org.dspace.content.logic.Filter; import org.dspace.content.security.service.CrisSecurityService; import org.dspace.content.service.ItemService; import org.dspace.core.Context; @@ -60,25 +61,11 @@ private boolean hasAccess(Context context, Item item, EPerson user, AccessItemMo try { - switch (crisSecurity) { - case ADMIN: - return authorizeService.isAdmin(context, user); - case CUSTOM: - return hasAccessByCustomPolicy(context, item, user, accessMode); - case GROUP: - return hasAccessByGroup(context, user, accessMode.getGroups()); - case ITEM_ADMIN: - return authorizeService.isAdmin(context, user, item); - case OWNER: - return isOwner(user, item); - case SUBMITTER: - return user != null && user.equals(item.getSubmitter()); - case SUBMITTER_GROUP: - return isUserInSubmitterGroup(context, item, user); - case NONE: - default: - return false; - } + boolean checkSecurity = checkSecurity(context, item, user, accessMode, crisSecurity); + Filter additionalFilter = accessMode.getAdditionalFilter(); + + return additionalFilter == null ? checkSecurity + : checkSecurity && additionalFilter.getResult(context, item); } catch (SQLException e) { throw new RuntimeException(e); @@ -86,6 +73,31 @@ private boolean hasAccess(Context context, Item item, EPerson user, AccessItemMo } + private boolean checkSecurity(Context context, Item item, EPerson user, AccessItemMode accessMode, + CrisSecurity crisSecurity) throws SQLException { + switch (crisSecurity) { + case ADMIN: + return authorizeService.isAdmin(context, user); + case CUSTOM: + return hasAccessByCustomPolicy(context, item, user, accessMode); + case GROUP: + return hasAccessByGroup(context, user, accessMode.getGroups()); + case ITEM_ADMIN: + return authorizeService.isAdmin(context, user, item); + case OWNER: + return isOwner(user, item); + case SUBMITTER: + return user != null && user.equals(item.getSubmitter()); + case SUBMITTER_GROUP: + return isUserInSubmitterGroup(context, item, user); + case ALL: + return true; + case NONE: + default: + return false; + } + } + private boolean isOwner(EPerson eperson, Item item) { return ePersonService.isOwnerOfItem(eperson, item); } diff --git a/dspace-api/src/test/java/org/dspace/content/security/CrisSecurityServiceIT.java b/dspace-api/src/test/java/org/dspace/content/security/CrisSecurityServiceIT.java index 854ab0fa300f..2fc14dbf0346 100644 --- a/dspace-api/src/test/java/org/dspace/content/security/CrisSecurityServiceIT.java +++ b/dspace-api/src/test/java/org/dspace/content/security/CrisSecurityServiceIT.java @@ -24,7 +24,10 @@ import org.dspace.builder.ItemBuilder; import org.dspace.content.Collection; import org.dspace.content.Item; +import org.dspace.content.logic.Filter; +import org.dspace.content.logic.LogicalStatementException; import org.dspace.content.security.service.CrisSecurityService; +import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.dspace.eperson.Group; import org.dspace.utils.DSpace; @@ -368,6 +371,92 @@ public void testHasAccessWithGroupConfig() throws SQLException, AuthorizeExcepti assertThat(crisSecurityService.hasAccess(context, item, fourthUser, accessMode), is(true)); } + @Test + public void testHasAccessWithGroupConfigAndAdditionalFilter() throws SQLException, AuthorizeException { + + context.turnOffAuthorisationSystem(); + + Group firstGroup = GroupBuilder.createGroup(context) + .withName("Group 1") + .build(); + + Group secondGroup = GroupBuilder.createGroup(context) + .withName("Group 2") + .build(); + + Group thirdGroup = GroupBuilder.createGroup(context) + .withName("Group 3") + .build(); + + EPerson firstUser = EPersonBuilder.createEPerson(context) + .withEmail("user@mail.it") + .withGroupMembership(firstGroup) + .build(); + + EPerson secondUser = EPersonBuilder.createEPerson(context) + .withEmail("user2@mail.it") + .withGroupMembership(secondGroup) + .build(); + + EPerson thirdUser = EPersonBuilder.createEPerson(context) + .withEmail("user3@mail.it") + .withGroupMembership(thirdGroup) + .build(); + + EPerson fourthUser = EPersonBuilder.createEPerson(context) + .withEmail("user4@mail.it") + .withGroupMembership(thirdGroup) + .build(); + + Item item = ItemBuilder.createItem(context, collection) + .withTitle("Test item") + .withDspaceObjectOwner("Owner", owner.getID().toString()) + .build(); + + Item itemNotAccessible = ItemBuilder.createItem(context, collection) + .withTitle("Test item not accessible") + .withDspaceObjectOwner("Owner", owner.getID().toString()) + .build(); + + context.restoreAuthSystemState(); + + AccessItemMode accessMode = buildAccessItemMode(CrisSecurity.GROUP); + when(accessMode.getGroups()).thenReturn(List.of("Group 1", thirdGroup.getID().toString())); + // filter valid only on first item + when(accessMode.getAdditionalFilter()).thenReturn(new Filter() { + @Override + public Boolean getResult(Context context, Item item) throws LogicalStatementException { + return item.getName().equals("Test item"); + } + + @Override + public String getName() { + return null; + } + + @Override + public void setBeanName(String s) {} + }); + + assertThat(crisSecurityService.hasAccess(context, item, eperson, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, item, admin, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, item, owner, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, item, collectionAdmin, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, item, communityAdmin, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, item, submitter, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, item, anotherSubmitter, accessMode), is(false)); + + assertThat(crisSecurityService.hasAccess(context, item, firstUser, accessMode), is(true)); + assertThat(crisSecurityService.hasAccess(context, item, secondUser, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, item, thirdUser, accessMode), is(true)); + assertThat(crisSecurityService.hasAccess(context, item, fourthUser, accessMode), is(true)); + + assertThat(crisSecurityService.hasAccess(context, itemNotAccessible, firstUser, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, itemNotAccessible, secondUser, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, itemNotAccessible, thirdUser, accessMode), is(false)); + assertThat(crisSecurityService.hasAccess(context, itemNotAccessible, fourthUser, accessMode), is(false)); + } + private AccessItemMode buildAccessItemMode(CrisSecurity... securities) { AccessItemMode mode = mock(AccessItemMode.class); when(mode.getSecurities()).thenReturn(List.of(securities)); From b2262050a38d4dd943f541f6e93a3c2c96b8c496 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 30 Aug 2023 18:24:23 +0200 Subject: [PATCH 02/23] [DSC-1224] Export script that generates registry-file for schema --- .../DspaceExportMetadataSchemaException.java | 23 ++ .../export/MetadataSchemaExportCliScript.java | 51 +++++ ...ataSchemaExportCliScriptConfiguration.java | 34 +++ .../export/MetadataSchemaExportScript.java | 119 ++++++++++ ...tadataSchemaExportScriptConfiguration.java | 73 ++++++ .../export/model/AbstractJaxbBuilder.java | 54 +++++ .../app/metadata/export/model/DcSchema.java | 80 +++++++ .../export/model/DcSchemaBuilder.java | 39 ++++ .../app/metadata/export/model/DcType.java | 86 +++++++ .../metadata/export/model/DcTypeBuilder.java | 49 ++++ .../metadata/export/model/DspaceDcTypes.java | 82 +++++++ .../export/model/DspaceDcTypesBuilder.java | 59 +++++ .../metadata/export/model/DspaceHeader.java | 92 ++++++++ .../export/model/DspaceHeaderBuilder.java | 59 +++++ .../metadata/export/model/ObjectFactory.java | 212 ++++++++++++++++++ .../service/MetadataExportServiceFactory.java | 28 +++ .../MetadataExportServiceFactoryImpl.java | 31 +++ .../service/MetadataFieldExportService.java | 35 +++ .../MetadataFieldExportServiceImpl.java | 49 ++++ .../service/MetadataSchemaExportService.java | 68 ++++++ .../MetadataSchemaExportServiceImpl.java | 107 +++++++++ .../config/spring/api/scripts.xml | 5 + .../export/MetadataSchemaExportScriptIT.java | 145 ++++++++++++ dspace/config/registries/dspace-dc-types.xsd | 59 +++++ .../spring/api/metadata-schema-export.xml | 17 ++ dspace/config/spring/api/scripts.xml | 5 + dspace/config/spring/rest/scripts.xml | 5 + 27 files changed, 1666 insertions(+) create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/DspaceExportMetadataSchemaException.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportCliScript.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportCliScriptConfiguration.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScript.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptConfiguration.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/AbstractJaxbBuilder.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcSchema.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcSchemaBuilder.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcType.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcTypeBuilder.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceDcTypes.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceDcTypesBuilder.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceHeader.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceHeaderBuilder.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/model/ObjectFactory.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataExportServiceFactory.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataExportServiceFactoryImpl.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataFieldExportService.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataFieldExportServiceImpl.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataSchemaExportService.java create mode 100644 dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataSchemaExportServiceImpl.java create mode 100644 dspace-api/src/test/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptIT.java create mode 100644 dspace/config/registries/dspace-dc-types.xsd create mode 100644 dspace/config/spring/api/metadata-schema-export.xml diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/DspaceExportMetadataSchemaException.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/DspaceExportMetadataSchemaException.java new file mode 100644 index 000000000000..1f2cbd824a80 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/DspaceExportMetadataSchemaException.java @@ -0,0 +1,23 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class DspaceExportMetadataSchemaException extends Exception { + + public DspaceExportMetadataSchemaException(Exception e) { + super(e); + } + + public DspaceExportMetadataSchemaException(String message, Exception e) { + super(message, e); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportCliScript.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportCliScript.java new file mode 100644 index 000000000000..83b8e94330ba --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportCliScript.java @@ -0,0 +1,51 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export; + +import java.io.File; + +import org.apache.commons.cli.ParseException; +import org.dspace.core.Context; + +/** + * This script can be use to export a given {@code MetadataSchema} into its + * registry file, that respects the standard DTD / XSD DSpace xml registry. + *

+ * This script is supposed to work with the CLI (command-line-interface), + * it accepts only two parameters {@code -i -f } + * respectively representing: + *

    + *
  • {@code schema-id}: id of the schema to export
  • + *
  • {@code file-path}:full file path of the file that will contain the export
  • + *
      + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + * + */ +public class MetadataSchemaExportCliScript extends MetadataSchemaExportScript { + + protected String filename; + + @Override + public void setup() throws ParseException { + super.setup(); + filename = commandLine.getOptionValue('f'); + } + + @Override + protected File getExportedFile(Context context) throws DspaceExportMetadataSchemaException { + try { + File file = new File(filename); + return metadataSchemaExportService.exportMetadataSchemaToFile(context, metadataSchema, file); + } catch (DspaceExportMetadataSchemaException e) { + handler.logError("Problem occured while exporting the schema to file: " + filename, e); + throw e; + } + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportCliScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportCliScriptConfiguration.java new file mode 100644 index 000000000000..5adfa2a725fc --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportCliScriptConfiguration.java @@ -0,0 +1,34 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + * + */ +public class MetadataSchemaExportCliScriptConfiguration + extends MetadataSchemaExportScriptConfiguration { + + @Override + public Options getOptions() { + Options options = super.getOptions(); + + options.addOption( + Option.builder("f").longOpt("file") + .desc("The temporary file-name to use") + .hasArg() + .build() + ); + + return options; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScript.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScript.java new file mode 100644 index 000000000000..59b3ddfda1a1 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScript.java @@ -0,0 +1,119 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export; + +import java.io.File; +import java.io.FileInputStream; +import java.sql.SQLException; + +import org.apache.commons.cli.ParseException; +import org.dspace.app.metadata.export.service.MetadataExportServiceFactory; +import org.dspace.app.metadata.export.service.MetadataSchemaExportService; +import org.dspace.content.MetadataSchema; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.MetadataSchemaService; +import org.dspace.core.Context; +import org.dspace.scripts.DSpaceRunnable; +import org.dspace.services.factory.DSpaceServicesFactory; + +/** + * This script can be use to export a given {@code MetadataSchema} into its + * registry file, that respects the standard DTD / XSD DSpace xml registry. + *

      + * This script is supposed to work with the webapp, it accepts only one + * parameter {@code -i } representing the id of the schema that + * will be exported. + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class MetadataSchemaExportScript + extends DSpaceRunnable> { + + protected MetadataSchemaService metadataSchemaService = + ContentServiceFactory.getInstance().getMetadataSchemaService(); + + protected MetadataSchemaExportService metadataSchemaExportService = + MetadataExportServiceFactory.getInstance().getMetadataSchemaExportService(); + + protected boolean help; + protected int id; + + protected MetadataSchema metadataSchema; + + @Override + public MetadataSchemaExportScriptConfiguration getScriptConfiguration() { + return DSpaceServicesFactory + .getInstance().getServiceManager() + .getServiceByName("export-schema", MetadataSchemaExportScriptConfiguration.class); + } + + @Override + public void setup() throws ParseException { + help = commandLine.hasOption('h'); + try { + id = Integer.parseInt(commandLine.getOptionValue('i')); + } catch (Exception e) { + handler.logError("Cannot parse the id argument ( " + id + " )! You should provide an integer!"); + throw new ParseException("Cannot parse the id argument ( " + id + " )! You should provide an integer!"); + } + } + + @Override + public void internalRun() throws Exception { + if (help) { + printHelp(); + return; + } + + Context context = new Context(); + try { + validate(context); + exportMetadataSchema(context); + } catch (Exception e) { + context.abort(); + throw e; + } + } + + private void validate(Context context) throws SQLException, ParseException { + metadataSchema = this.metadataSchemaService.find(context, id); + if (metadataSchema == null) { + handler.logError("Cannot find the metadata-schema with id: " + id); + throw new ParseException("Cannot find the metadata-schema with id: " + id); + } + } + + private void exportMetadataSchema(Context context) throws Exception { + handler.logInfo( + "Exporting the metadata-schema file for the schema " + metadataSchema.getName() + ); + try { + File tempFile = getExportedFile(context); + + handler.logInfo( + "Exported to file: " + tempFile.getAbsolutePath() + ); + + try (FileInputStream fis = new FileInputStream(tempFile)) { + handler.logInfo("Summarizing export ..."); + context.turnOffAuthorisationSystem(); + handler.writeFilestream( + context, metadataSchema.getName(), fis, "application/xml", false + ); + context.restoreAuthSystemState(); + } + } catch (Exception e) { + handler.logError("Problem occured while exporting the schema!", e); + throw e; + } + } + + protected File getExportedFile(Context context) throws DspaceExportMetadataSchemaException { + return this.metadataSchemaExportService.exportMetadataSchemaToFile(context, metadataSchema); + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptConfiguration.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptConfiguration.java new file mode 100644 index 000000000000..665dbe15567c --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptConfiguration.java @@ -0,0 +1,73 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export; + +import java.sql.SQLException; + +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.core.Context; +import org.dspace.scripts.configuration.ScriptConfiguration; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Configuration of the Script {@code MetadataSchemaExportScript} + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class MetadataSchemaExportScriptConfiguration + extends ScriptConfiguration { + + @Autowired + private AuthorizeService authorizeService; + + private Class dspaceRunnableClass; + + @Override + public Class getDspaceRunnableClass() { + return this.dspaceRunnableClass; + } + + @Override + public void setDspaceRunnableClass(Class dspaceRunnableClass) { + this.dspaceRunnableClass = dspaceRunnableClass; + } + + @Override + public boolean isAllowedToExecute(Context context) { + try { + return authorizeService.isAdmin(context); + } catch (SQLException e) { + throw new RuntimeException("SQLException occurred when checking if the current user is an admin", e); + } + } + + @Override + public Options getOptions() { + Options options = new Options(); + + options.addOption( + Option.builder("i").longOpt("id") + .desc("Metadata schema id") + .hasArg() + .required() + .build() + ); + + options.addOption( + Option.builder("h").longOpt("help") + .desc("help") + .hasArg(false) + .required(false) + .build() + ); + + return options; + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/AbstractJaxbBuilder.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/AbstractJaxbBuilder.java new file mode 100644 index 000000000000..038d8552e1e2 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/AbstractJaxbBuilder.java @@ -0,0 +1,54 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import java.lang.reflect.InvocationTargetException; +import java.util.function.Function; +import javax.xml.bind.JAXBElement; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public abstract class AbstractJaxbBuilder { + + T object; + Class clazz; + + protected final ObjectFactory objectFactory = new ObjectFactory(); + + protected AbstractJaxbBuilder(Class clazz) { + this.clazz = clazz; + } + + protected T getObejct() { + if (object == null) { + try { + object = clazz.getDeclaredConstructor().newInstance(); + } catch (InstantiationException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + return object; + } + + public T build() { + return object; + } + + protected void addChildElement(C value, Function> mapper) { + addChildElement(mapper.apply(value)); + } + + protected abstract void addChildElement(JAXBElement v); +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcSchema.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcSchema.java new file mode 100644 index 000000000000..e0ad541bdb84 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcSchema.java @@ -0,0 +1,80 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElementRef; +import javax.xml.bind.annotation.XmlElementRefs; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

      Classe Java per anonymous complex type. + * + *

      Il seguente frammento di schema specifica il contenuto previsto contenuto in questa classe. + * + *

      + * <complexType>
      + *   <complexContent>
      + *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
      + *       <choice maxOccurs="unbounded" minOccurs="0">
      + *         <element ref="{}name"/>
      + *         <element ref="{}namespace"/>
      + *       </choice>
      + *     </restriction>
      + *   </complexContent>
      + * </complexType>
      + * 
      + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "nameOrNamespace" +}) +@XmlRootElement(name = "dc-schema") +public class DcSchema { + + @XmlElementRefs({ + @XmlElementRef(name = "name", type = JAXBElement.class, required = false), + @XmlElementRef(name = "namespace", type = JAXBElement.class, required = false) + }) + protected List> nameOrNamespace; + + /** + * Gets the value of the nameOrNamespace property. + * + *

      + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the nameOrNamespace property. + * + *

      + * For example, to add a new item, do as follows: + *

      +     *    getNameOrNamespace().add(newItem);
      +     * 
      + * + * + *

      + * Objects of the following type(s) are allowed in the list + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + */ + public List> getNameOrNamespace() { + if (nameOrNamespace == null) { + nameOrNamespace = new ArrayList>(); + } + return this.nameOrNamespace; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcSchemaBuilder.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcSchemaBuilder.java new file mode 100644 index 000000000000..fe7144bda854 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcSchemaBuilder.java @@ -0,0 +1,39 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import javax.xml.bind.JAXBElement; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class DcSchemaBuilder extends AbstractJaxbBuilder { + + protected DcSchemaBuilder() { + super(DcSchema.class); + } + + public static DcSchemaBuilder createBuilder() { + return new DcSchemaBuilder(); + } + + public DcSchemaBuilder withName(String name) { + this.addChildElement(name, objectFactory::createName); + return this; + } + + public DcSchemaBuilder withNamespace(String namespace) { + this.addChildElement(namespace, objectFactory::createNamespace); + return this; + } + + @Override + protected void addChildElement(JAXBElement v) { + getObejct().getNameOrNamespace().add(v); + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcType.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcType.java new file mode 100644 index 000000000000..dcf4d9821cd0 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcType.java @@ -0,0 +1,86 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElementRef; +import javax.xml.bind.annotation.XmlElementRefs; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

      Classe Java per anonymous complex type. + * + *

      Il seguente frammento di schema specifica il contenuto previsto contenuto in questa classe. + * + *

      + * <complexType>
      + *   <complexContent>
      + *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
      + *       <choice maxOccurs="unbounded" minOccurs="0">
      + *         <element ref="{}schema"/>
      + *         <element ref="{}element"/>
      + *         <element ref="{}qualifier"/>
      + *         <element ref="{}scope_note"/>
      + *       </choice>
      + *     </restriction>
      + *   </complexContent>
      + * </complexType>
      + * 
      + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "schemaOrElementOrQualifier" +}) +@XmlRootElement(name = "dc-type") +public class DcType { + + @XmlElementRefs({ + @XmlElementRef(name = "schema", type = JAXBElement.class), + @XmlElementRef(name = "element", type = JAXBElement.class), + @XmlElementRef(name = "qualifier", type = JAXBElement.class), + @XmlElementRef(name = "scope_note", type = JAXBElement.class) + }) + protected List> schemaOrElementOrQualifier; + + /** + * Gets the value of the schemaOrElementOrQualifier property. + * + *

      + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the schemaOrElementOrQualifier property. + * + *

      + * For example, to add a new item, do as follows: + *

      +     *    getSchemaOrElementOrQualifier().add(newItem);
      +     * 
      + * + * + *

      + * Objects of the following type(s) are allowed in the list + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + */ + public List> getSchemaOrElementOrQualifier() { + if (schemaOrElementOrQualifier == null) { + schemaOrElementOrQualifier = new ArrayList>(); + } + return this.schemaOrElementOrQualifier; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcTypeBuilder.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcTypeBuilder.java new file mode 100644 index 000000000000..47fd64763ead --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcTypeBuilder.java @@ -0,0 +1,49 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import javax.xml.bind.JAXBElement; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class DcTypeBuilder extends AbstractJaxbBuilder { + + protected DcTypeBuilder() { + super(DcType.class); + } + + public static DcTypeBuilder createBuilder() { + return new DcTypeBuilder(); + } + + public DcTypeBuilder withSchema(String schema) { + addChildElement(schema, objectFactory::createSchema); + return this; + } + + public DcTypeBuilder withElement(String element) { + addChildElement(element, objectFactory::createElement); + return this; + } + + public DcTypeBuilder withQualifier(String qualifier) { + addChildElement(qualifier, objectFactory::createQualifier); + return this; + } + + public DcTypeBuilder withScopeNote(String scopeNote) { + addChildElement(scopeNote, objectFactory::createScopeNote); + return this; + } + + @Override + protected void addChildElement(JAXBElement v) { + getObejct().getSchemaOrElementOrQualifier().add(v); + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceDcTypes.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceDcTypes.java new file mode 100644 index 000000000000..4cba081a8a30 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceDcTypes.java @@ -0,0 +1,82 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlElements; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

      Classe Java per anonymous complex type. + * + *

      Il seguente frammento di schema specifica il contenuto previsto contenuto in questa classe. + * + *

      + * <complexType>
      + *   <complexContent>
      + *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
      + *       <choice maxOccurs="unbounded" minOccurs="0">
      + *         <element ref="{}dspace-header"/>
      + *         <element ref="{}dc-schema"/>
      + *         <element ref="{}dc-type"/>
      + *       </choice>
      + *     </restriction>
      + *   </complexContent>
      + * </complexType>
      + * 
      + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "dspaceHeaderOrDcSchemaOrDcType" +}) +@XmlRootElement(name = "dspace-dc-types") +public class DspaceDcTypes { + + @XmlElements({ + @XmlElement(name = "dspace-header", type = DspaceHeader.class), + @XmlElement(name = "dc-schema", type = DcSchema.class), + @XmlElement(name = "dc-type", type = DcType.class) + }) + protected List dspaceHeaderOrDcSchemaOrDcType; + + /** + * Gets the value of the dspaceHeaderOrDcSchemaOrDcType property. + * + *

      + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the dspaceHeaderOrDcSchemaOrDcType property. + * + *

      + * For example, to add a new item, do as follows: + *

      +     *    getDspaceHeaderOrDcSchemaOrDcType().add(newItem);
      +     * 
      + * + * + *

      + * Objects of the following type(s) are allowed in the list + * {@link DspaceHeader } + * {@link DcSchema } + * {@link DcType } + */ + public List getDspaceHeaderOrDcSchemaOrDcType() { + if (dspaceHeaderOrDcSchemaOrDcType == null) { + dspaceHeaderOrDcSchemaOrDcType = new ArrayList(); + } + return this.dspaceHeaderOrDcSchemaOrDcType; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceDcTypesBuilder.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceDcTypesBuilder.java new file mode 100644 index 000000000000..1e4cdb83393c --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceDcTypesBuilder.java @@ -0,0 +1,59 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import java.util.Collection; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class DspaceDcTypesBuilder { + + private DspaceDcTypes dcTypes; + + private final ObjectFactory objectFactory = new ObjectFactory(); + + private DspaceDcTypes getDcTypes() { + if (dcTypes == null) { + dcTypes = new DspaceDcTypes(); + } + return dcTypes; + } + + private DspaceDcTypesBuilder() { + } + + public static DspaceDcTypesBuilder createBuilder() { + return new DspaceDcTypesBuilder(); + } + + public DspaceDcTypesBuilder witheader(DspaceHeader header) { + this.getDcTypes().getDspaceHeaderOrDcSchemaOrDcType().add(header); + return this; + } + + public DspaceDcTypesBuilder withSchema(DcSchema schema) { + this.getDcTypes().getDspaceHeaderOrDcSchemaOrDcType().add(schema); + return this; + } + + public DspaceDcTypesBuilder withDcType(DcType dcType) { + this.getDcTypes().getDspaceHeaderOrDcSchemaOrDcType().add(dcType); + return this; + } + + public DspaceDcTypesBuilder withDcTypes(Collection dcTypes) { + this.getDcTypes().getDspaceHeaderOrDcSchemaOrDcType().addAll(dcTypes); + return this; + } + + public DspaceDcTypes build() { + return dcTypes; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceHeader.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceHeader.java new file mode 100644 index 000000000000..151c8b28292d --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceHeader.java @@ -0,0 +1,92 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import java.util.ArrayList; +import java.util.List; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlAccessType; +import javax.xml.bind.annotation.XmlAccessorType; +import javax.xml.bind.annotation.XmlElementRef; +import javax.xml.bind.annotation.XmlElementRefs; +import javax.xml.bind.annotation.XmlRootElement; +import javax.xml.bind.annotation.XmlType; + + +/** + *

      Classe Java per anonymous complex type. + * + *

      Il seguente frammento di schema specifica il contenuto previsto contenuto in questa classe. + * + *

      + * <complexType>
      + *   <complexContent>
      + *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
      + *       <choice maxOccurs="unbounded" minOccurs="0">
      + *         <element ref="{}title"/>
      + *         <element ref="{}contributor.author"/>
      + *         <element ref="{}contributor.editor"/>
      + *         <element ref="{}date.created"/>
      + *         <element ref="{}description"/>
      + *         <element ref="{}description.version"/>
      + *       </choice>
      + *     </restriction>
      + *   </complexContent>
      + * </complexType>
      + * 
      + */ +@XmlAccessorType(XmlAccessType.FIELD) +@XmlType(name = "", propOrder = { + "titleOrContributorAuthorOrContributorEditor" +}) +@XmlRootElement(name = "dspace-header") +public class DspaceHeader { + + @XmlElementRefs({ + @XmlElementRef(name = "title", type = JAXBElement.class, required = false), + @XmlElementRef(name = "contributor.author", type = JAXBElement.class, required = false), + @XmlElementRef(name = "contributor.editor", type = JAXBElement.class, required = false), + @XmlElementRef(name = "date.created", type = JAXBElement.class, required = false), + @XmlElementRef(name = "description", type = JAXBElement.class, required = false), + @XmlElementRef(name = "description.version", type = JAXBElement.class, required = false) + }) + protected List> titleOrContributorAuthorOrContributorEditor; + + /** + * Gets the value of the titleOrContributorAuthorOrContributorEditor property. + * + *

      + * This accessor method returns a reference to the live list, + * not a snapshot. Therefore any modification you make to the + * returned list will be present inside the JAXB object. + * This is why there is not a set method for the titleOrContributorAuthorOrContributorEditor property. + * + *

      + * For example, to add a new item, do as follows: + *

      +     *    getTitleOrContributorAuthorOrContributorEditor().add(newItem);
      +     * 
      + * + * + *

      + * Objects of the following type(s) are allowed in the list + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + * {@link JAXBElement }{@code <}{@link String }{@code >} + */ + public List> getTitleOrContributorAuthorOrContributorEditor() { + if (titleOrContributorAuthorOrContributorEditor == null) { + titleOrContributorAuthorOrContributorEditor = new ArrayList>(); + } + return this.titleOrContributorAuthorOrContributorEditor; + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceHeaderBuilder.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceHeaderBuilder.java new file mode 100644 index 000000000000..fb4028a2057b --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DspaceHeaderBuilder.java @@ -0,0 +1,59 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import javax.xml.bind.JAXBElement; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class DspaceHeaderBuilder extends AbstractJaxbBuilder { + + protected DspaceHeaderBuilder() { + super(DspaceHeader.class); + } + + public static DspaceHeaderBuilder createBuilder() { + return new DspaceHeaderBuilder(); + } + + public DspaceHeaderBuilder withTitle(String title) { + addChildElement(title, objectFactory::createTitle); + return this; + } + + public DspaceHeaderBuilder withContributorAuthor(String contributorAuthor) { + addChildElement(contributorAuthor, objectFactory::createContributorAuthor); + return this; + } + + public DspaceHeaderBuilder withContributorEditor(String contributorEditor) { + addChildElement(contributorEditor, objectFactory::createContributorEditor); + return this; + } + + public DspaceHeaderBuilder withDateCreated(String dateCreated) { + addChildElement(dateCreated, objectFactory::createDateCreated); + return this; + } + + public DspaceHeaderBuilder withDescription(String description) { + addChildElement(description, objectFactory::createDescription); + return this; + } + + public DspaceHeaderBuilder withDescriptionVersion(String descriptionVersion) { + addChildElement(descriptionVersion, objectFactory::createDescriptionVersion); + return this; + } + + @Override + protected void addChildElement(JAXBElement v) { + getObejct().getTitleOrContributorAuthorOrContributorEditor().add(v); + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/ObjectFactory.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/ObjectFactory.java new file mode 100644 index 000000000000..085e8af5f81b --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/ObjectFactory.java @@ -0,0 +1,212 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.model; + +import javax.xml.bind.JAXBElement; +import javax.xml.bind.annotation.XmlElementDecl; +import javax.xml.bind.annotation.XmlRegistry; +import javax.xml.namespace.QName; + + +/** + * This object contains factory methods for each + * Java content interface and Java element interface + * generated in the org.dspace.app.metadata.export.model package. + *

      An ObjectFactory allows you to programatically + * construct new instances of the Java representation + * for XML content. The Java representation of XML + * content can consist of schema derived interfaces + * and classes representing the binding of schema + * type definitions, element declarations and model + * groups. Factory methods for each of these are + * provided in this class. + */ +@XmlRegistry +public class ObjectFactory { + + private final static QName _Title_QNAME = new QName("", "title"); + private final static QName _ContributorAuthor_QNAME = new QName("", "contributor.author"); + private final static QName _ContributorEditor_QNAME = new QName("", "contributor.editor"); + private final static QName _DateCreated_QNAME = new QName("", "date.created"); + private final static QName _Description_QNAME = new QName("", "description"); + private final static QName _DescriptionVersion_QNAME = new QName("", "description.version"); + private final static QName _Name_QNAME = new QName("", "name"); + private final static QName _Namespace_QNAME = new QName("", "namespace"); + private final static QName _Schema_QNAME = new QName("", "schema"); + private final static QName _Element_QNAME = new QName("", "element"); + private final static QName _Qualifier_QNAME = new QName("", "qualifier"); + private final static QName _ScopeNote_QNAME = new QName("", "scope_note"); + + /** + * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org + * .dspace.app.metadata.export.model + */ + public ObjectFactory() { + } + + /** + * Create an instance of {@link DspaceDcTypes } + */ + public DspaceDcTypes createDspaceDcTypes() { + return new DspaceDcTypes(); + } + + /** + * Create an instance of {@link DspaceHeader } + */ + public DspaceHeader createDspaceHeader() { + return new DspaceHeader(); + } + + /** + * Create an instance of {@link DcSchema } + */ + public DcSchema createDcSchema() { + return new DcSchema(); + } + + /** + * Create an instance of {@link DcType } + */ + public DcType createDcType() { + return new DcType(); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "title") + public JAXBElement createTitle(String value) { + return new JAXBElement(_Title_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "contributor.author") + public JAXBElement createContributorAuthor(String value) { + return new JAXBElement(_ContributorAuthor_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "contributor.editor") + public JAXBElement createContributorEditor(String value) { + return new JAXBElement(_ContributorEditor_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "date.created") + public JAXBElement createDateCreated(String value) { + return new JAXBElement(_DateCreated_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "description") + public JAXBElement createDescription(String value) { + return new JAXBElement(_Description_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "description.version") + public JAXBElement createDescriptionVersion(String value) { + return new JAXBElement(_DescriptionVersion_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "name") + public JAXBElement createName(String value) { + return new JAXBElement(_Name_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "namespace") + public JAXBElement createNamespace(String value) { + return new JAXBElement(_Namespace_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "schema") + public JAXBElement createSchema(String value) { + return new JAXBElement(_Schema_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "element") + public JAXBElement createElement(String value) { + return new JAXBElement(_Element_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "qualifier") + public JAXBElement createQualifier(String value) { + return new JAXBElement(_Qualifier_QNAME, String.class, null, value); + } + + /** + * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >} + * + * @param value Java instance representing xml element's value. + * @return the new instance of {@link JAXBElement }{@code <}{@link String }{@code >} + */ + @XmlElementDecl(namespace = "", name = "scope_note") + public JAXBElement createScopeNote(String value) { + return new JAXBElement(_ScopeNote_QNAME, String.class, null, value); + } + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataExportServiceFactory.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataExportServiceFactory.java new file mode 100644 index 000000000000..3553cbcba2fd --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataExportServiceFactory.java @@ -0,0 +1,28 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.service; + +import org.dspace.services.factory.DSpaceServicesFactory; + +/** + * Factory for the export services related to metadata-schema and metadata-fields. + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public abstract class MetadataExportServiceFactory { + + public static MetadataExportServiceFactory getInstance() { + return DSpaceServicesFactory + .getInstance().getServiceManager() + .getServiceByName("metadataExportServiceFactory", MetadataExportServiceFactory.class); + } + + public abstract MetadataSchemaExportService getMetadataSchemaExportService(); + public abstract MetadataFieldExportService getMetadataFieldExportService(); + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataExportServiceFactoryImpl.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataExportServiceFactoryImpl.java new file mode 100644 index 000000000000..a69d5dfd0fde --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataExportServiceFactoryImpl.java @@ -0,0 +1,31 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.service; + +import org.springframework.beans.factory.annotation.Autowired; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class MetadataExportServiceFactoryImpl extends MetadataExportServiceFactory { + + @Autowired + private MetadataSchemaExportService metadataSchemaExportService; + @Autowired + private MetadataFieldExportService metadataFieldExportService; + + @Override + public MetadataSchemaExportService getMetadataSchemaExportService() { + return metadataSchemaExportService; + } + + @Override + public MetadataFieldExportService getMetadataFieldExportService() { + return metadataFieldExportService; + } +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataFieldExportService.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataFieldExportService.java new file mode 100644 index 000000000000..ace312885230 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataFieldExportService.java @@ -0,0 +1,35 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.service; + +import java.sql.SQLException; +import java.util.List; + +import org.dspace.app.metadata.export.model.DcType; +import org.dspace.content.MetadataField; +import org.dspace.content.MetadataSchema; +import org.dspace.core.Context; + +/** + * Exports {@code MetadataField} into {@code DcType} + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public interface MetadataFieldExportService { + + /** + * Creates a one {@link DCType} for each {@link MetadataField} + * in the given {@link MetadataSchema}, and returns them in a list + * + * @param context + * @param metadataSchema + * @return + * @throws SQLException + */ + List exportMetadataFieldsBy(Context context, MetadataSchema metadataSchema) throws SQLException; +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataFieldExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataFieldExportServiceImpl.java new file mode 100644 index 000000000000..1ace35f4e45d --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataFieldExportServiceImpl.java @@ -0,0 +1,49 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.service; + +import java.sql.SQLException; +import java.util.List; +import java.util.stream.Collectors; + +import org.dspace.app.metadata.export.model.DcType; +import org.dspace.app.metadata.export.model.DcTypeBuilder; +import org.dspace.content.MetadataField; +import org.dspace.content.MetadataSchema; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.MetadataFieldService; +import org.dspace.core.Context; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class MetadataFieldExportServiceImpl implements MetadataFieldExportService { + + private MetadataFieldService metadataFieldService = + ContentServiceFactory.getInstance().getMetadataFieldService(); + + public List exportMetadataFieldsBy(Context context, MetadataSchema metadataSchema) throws SQLException { + return metadataFieldService + .findAllInSchema(context, metadataSchema) + .stream() + .map(this::toDcType) + .collect(Collectors.toList()); + } + + private DcType toDcType(MetadataField metadataField) { + return DcTypeBuilder + .createBuilder() + .withSchema(metadataField.getMetadataSchema().getName()) + .withElement(metadataField.getElement()) + .withQualifier(metadataField.getQualifier()) + .withScopeNote(metadataField.getScopeNote()) + .build(); + } + +} + diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataSchemaExportService.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataSchemaExportService.java new file mode 100644 index 000000000000..cd1f35e2ef9b --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataSchemaExportService.java @@ -0,0 +1,68 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.service; + +import java.io.File; +import java.sql.SQLException; + +import org.dspace.app.metadata.export.DspaceExportMetadataSchemaException; +import org.dspace.app.metadata.export.model.DspaceDcTypes; +import org.dspace.content.MetadataSchema; +import org.dspace.core.Context; + +/** + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public interface MetadataSchemaExportService { + + /** + * Exports the given {@code schemaId} into a {@link DspaceDcTypes} entity + * + * @param context + * @param schemaId + * @return + * @throws SQLException + */ + DspaceDcTypes exportMetadataSchema(Context context, int schemaId) throws SQLException; + + /** + * Exports the given {@code metadataSchema} into a {@link DspaceDcTypes} entity + * + * @param context + * @param metadataSchema + * @return + * @throws SQLException + */ + DspaceDcTypes exportMetadataSchema(Context context, MetadataSchema metadataSchema) throws SQLException; + + /** + * Exports the given {@code metadataSchema} to a temporary {@code File}, + * that will respect the {@code registry} xml format of dspace + * + * @param context + * @param metadataSchema + * @return + * @throws DspaceExportMetadataSchemaException + */ + File exportMetadataSchemaToFile(Context context, MetadataSchema metadataSchema) + throws DspaceExportMetadataSchemaException; + + /** + * Exports the given {@code metadataSchema} to a target {@code File}, + * that will respect the {@code registry} xml format of dspace + * + * @param context + * @param metadataSchema + * @param file + * @return + * @throws DspaceExportMetadataSchemaException + */ + File exportMetadataSchemaToFile(Context context, MetadataSchema metadataSchema, File file) + throws DspaceExportMetadataSchemaException; + +} diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataSchemaExportServiceImpl.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataSchemaExportServiceImpl.java new file mode 100644 index 000000000000..eea9a09f7970 --- /dev/null +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/service/MetadataSchemaExportServiceImpl.java @@ -0,0 +1,107 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export.service; + +import java.io.File; +import java.io.IOException; +import java.sql.SQLException; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; + +import org.dspace.app.metadata.export.DspaceExportMetadataSchemaException; +import org.dspace.app.metadata.export.model.DcSchema; +import org.dspace.app.metadata.export.model.DcSchemaBuilder; +import org.dspace.app.metadata.export.model.DspaceDcTypes; +import org.dspace.app.metadata.export.model.DspaceDcTypesBuilder; +import org.dspace.content.MetadataSchema; +import org.dspace.content.factory.ContentServiceFactory; +import org.dspace.content.service.MetadataSchemaService; +import org.dspace.core.Context; + +/** + * This service can be used to export a target schema into a registry-file + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + **/ +public class MetadataSchemaExportServiceImpl implements MetadataSchemaExportService { + + private MetadataSchemaService metadataSchemaService = + ContentServiceFactory.getInstance().getMetadataSchemaService(); + + @Override + public DspaceDcTypes exportMetadataSchema(Context context, int schemaId) throws SQLException { + return this.exportMetadataSchema(context, metadataSchemaService.find(context, schemaId)); + } + + @Override + public DspaceDcTypes exportMetadataSchema(Context context, MetadataSchema metadataSchema) throws SQLException { + return DspaceDcTypesBuilder + .createBuilder() + .withSchema(this.mapToDcSchema(metadataSchema)) + .withDcTypes( + MetadataExportServiceFactory.getInstance() + .getMetadataFieldExportService() + .exportMetadataFieldsBy(context, metadataSchema) + ) + .build(); + } + + @Override + public File exportMetadataSchemaToFile(Context context, MetadataSchema metadataSchema) + throws DspaceExportMetadataSchemaException { + File tempFile; + try { + tempFile = + File.createTempFile( + metadataSchema.getName() + "-" + metadataSchema.getID(), + ".xml" + ); + tempFile.deleteOnExit(); + return this.exportMetadataSchemaToFile(context, metadataSchema, tempFile); + } catch (IOException e) { + throw new DspaceExportMetadataSchemaException( + "Probelm occured during while exporting to temporary file!", + e + ); + } + } + + @Override + public File exportMetadataSchemaToFile(Context context, MetadataSchema metadataSchema, File file) + throws DspaceExportMetadataSchemaException { + try { + DspaceDcTypes dspaceDcTypes = this.exportMetadataSchema(context, metadataSchema); + + JAXBContext jaxb = JAXBContext.newInstance(DspaceDcTypes.class); + Marshaller jaxbMarshaller = jaxb.createMarshaller(); + jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE); + jaxbMarshaller.marshal(dspaceDcTypes, file); + } catch (SQLException e) { + throw new DspaceExportMetadataSchemaException( + "Problem occured while retrieving data from DB!", + e + ); + } catch (JAXBException e) { + throw new DspaceExportMetadataSchemaException( + "Problem occured during the export to XML file!", + e + ); + } + return file; + } + + private DcSchema mapToDcSchema(MetadataSchema metadataSchema) { + return DcSchemaBuilder + .createBuilder() + .withName(metadataSchema.getName()) + .withNamespace(metadataSchema.getNamespace()) + .build(); + } + +} diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml index 0697423578bc..fdd7886c477b 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/scripts.xml @@ -155,5 +155,10 @@ + + + + + diff --git a/dspace-api/src/test/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptIT.java b/dspace-api/src/test/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptIT.java new file mode 100644 index 000000000000..bd53c48602b5 --- /dev/null +++ b/dspace-api/src/test/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptIT.java @@ -0,0 +1,145 @@ +/** + * The contents of this file are subject to the license and copyright + * detailed in the LICENSE and NOTICE files at the root of the source + * tree and available online at + * + * http://www.dspace.org/license/ + */ +package org.dspace.app.metadata.export; + +import static org.dspace.app.launcher.ScriptLauncher.handleScript; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.is; + +import java.io.File; +import java.io.FileInputStream; +import java.nio.charset.Charset; +import java.sql.SQLException; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.dspace.AbstractIntegrationTestWithDatabase; +import org.dspace.app.launcher.ScriptLauncher; +import org.dspace.app.scripts.handler.impl.TestDSpaceRunnableHandler; +import org.dspace.authorize.AuthorizeException; +import org.dspace.builder.MetadataFieldBuilder; +import org.dspace.builder.MetadataSchemaBuilder; +import org.dspace.content.MetadataSchema; +import org.dspace.services.ConfigurationService; +import org.dspace.services.factory.DSpaceServicesFactory; +import org.junit.Before; +import org.junit.Test; + + +/** + * Integration tests for {@link MetadataSchemaExportScript} + * + * @author Vincenzo Mecca (vins01-4science - vincenzo.mecca at 4science.com) + * + */ +public class MetadataSchemaExportScriptIT extends AbstractIntegrationTestWithDatabase { + + private final ConfigurationService configurationService = + DSpaceServicesFactory.getInstance().getConfigurationService(); + + private MetadataSchema schema; + private List fields; + private String fileLocation; + + @Before + @SuppressWarnings("deprecation") + public void beforeTests() throws SQLException, AuthorizeException { + context.turnOffAuthorisationSystem(); + schema = createMetadataSchema(); + fields = createFields(); + fileLocation = configurationService.getProperty("dspace.dir"); + context.restoreAuthSystemState(); + } + + private List createFields() throws SQLException, AuthorizeException { + return List.of( + MetadataFieldBuilder.createMetadataField(context, schema, "first", "metadata", "notes first"), + MetadataFieldBuilder.createMetadataField(context, schema, "second", "metadata", "notes second"), + MetadataFieldBuilder.createMetadataField(context, schema, "third", "metadata", "notes third"), + MetadataFieldBuilder.createMetadataField(context, schema, "element", null, null) + ); + } + + private MetadataSchema createMetadataSchema() throws SQLException, AuthorizeException { + return MetadataSchemaBuilder.createMetadataSchema(context, "test", "http://dspace.org/test").build(); + } + + @Test + public void testMetadataSchemaExport() throws Exception { + + File xml = new File(fileLocation + "/test-types.xml"); + xml.deleteOnExit(); + + String[] args = + new String[] { + "export-schema", + "-i", schema.getID().toString(), + "-f", xml.getAbsolutePath() + }; + TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler(); + + handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl, eperson); + + assertThat(handler.getErrorMessages(), empty()); + assertThat( + handler.getInfoMessages(), + hasItem("Exporting the metadata-schema file for the schema " + schema.getName()) + ); + assertThat("The xml file should be created", xml.exists(), is(true)); + + + try (FileInputStream fis = new FileInputStream(xml)) { + String content = IOUtils.toString(fis, Charset.defaultCharset()); + assertThat(content, containsString("")); + assertThat(content, containsString("test")); + assertThat(content, containsString("http://dspace.org/test")); + assertThat(content, containsString("")); + assertThat(content, containsString("test")); + assertThat(content, containsString("first")); + assertThat(content, containsString("metadata")); + assertThat(content, containsString("notes first")); + assertThat(content, containsString("")); + assertThat(content, containsString("")); + assertThat(content, containsString("test")); + assertThat(content, containsString("third")); + assertThat(content, containsString("metadata")); + assertThat(content, containsString("notes third")); + assertThat(content, containsString("")); + assertThat(content, containsString("")); + assertThat(content, containsString("test")); + assertThat(content, containsString("element")); + assertThat(content, containsString("")); + assertThat(content, containsString("")); + assertThat(content, containsString("")); + } + } + + @Test + public void testMetadataNotExistingSchemaExport() throws Exception { + + File xml = new File(fileLocation + "/test-types.xml"); + xml.deleteOnExit(); + + String[] args = + new String[] { + "export-schema", + "-i", "-1", + "-f", xml.getAbsolutePath() + }; + TestDSpaceRunnableHandler handler = new TestDSpaceRunnableHandler(); + + handleScript(args, ScriptLauncher.getConfig(kernelImpl), handler, kernelImpl, eperson); + + assertThat(handler.getErrorMessages(), hasItem("Cannot find the metadata-schema with id: -1")); + assertThat("The xml file should not be created", xml.exists(), is(false)); + } + +} diff --git a/dspace/config/registries/dspace-dc-types.xsd b/dspace/config/registries/dspace-dc-types.xsd new file mode 100644 index 000000000000..1722d3c505c9 --- /dev/null +++ b/dspace/config/registries/dspace-dc-types.xsd @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dspace/config/spring/api/metadata-schema-export.xml b/dspace/config/spring/api/metadata-schema-export.xml new file mode 100644 index 000000000000..26b9daf76045 --- /dev/null +++ b/dspace/config/spring/api/metadata-schema-export.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + diff --git a/dspace/config/spring/api/scripts.xml b/dspace/config/spring/api/scripts.xml index a99d580dd901..f4f7c97c0011 100644 --- a/dspace/config/spring/api/scripts.xml +++ b/dspace/config/spring/api/scripts.xml @@ -160,4 +160,9 @@ + + + + + diff --git a/dspace/config/spring/rest/scripts.xml b/dspace/config/spring/rest/scripts.xml index 6853d7499276..20ca17f2dabc 100644 --- a/dspace/config/spring/rest/scripts.xml +++ b/dspace/config/spring/rest/scripts.xml @@ -108,4 +108,9 @@ + + + + + From 404c825e559a69fabb8f596e1a4a65ec1d132f8c Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 31 Aug 2023 09:27:18 +0200 Subject: [PATCH 03/23] [DSC-1117] Fixes wrong property name in ApplicationConfig --- .../main/java/org/dspace/app/rest/utils/ApplicationConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java index 67983ba8f007..91c832c0533e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java @@ -38,7 +38,7 @@ public class ApplicationConfig { // Allowed IIIF CORS origins ("Access-Control-Allow-Origin" header) // Can be overridden in DSpace configuration - @Value("${rest.cors.bitstream-allow-origins}") + @Value("${rest.cors.bitstream-allowed-origins}") private String[] bitstreamCorsAllowedOrigins; // Whether to allow credentials (cookies) in CORS requests ("Access-Control-Allow-Credentials" header) From cfaafed8138ca6ec0e2d43de776a5600313d46b6 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 31 Aug 2023 11:54:42 +0200 Subject: [PATCH 04/23] [DSC-1117] Fixes property key in rest.cfg --- dspace/config/modules/rest.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index 9d2eb77be2cc..cfe954d410c7 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -134,7 +134,7 @@ rest.regex-clause = text_value ~ ? ##### Customize the REST origins allowed to retrieve the bitstreams ##### ##### default is set to pattern * - use this configuration to restrict/modify this behavior ##### This configuration doens't support the wildcard -bitstream.cors.allowed-origins = +rest.cors.bitstream-allowed-origins = ##### Configure REST Report Filters ##### @@ -233,4 +233,4 @@ rest.search.max.results = 100 # Patterns associated with uri for which do not set the context in read-only mode for GET calls rest.get-in-read-only-mode.exception-patterns = /api/authn/** -rest.get-in-read-only-mode.exception-patterns = /api/cris/orcid/** +rest.get-in-read-only-mode.exception-patterns = /api/cris/orcid/** \ No newline at end of file From f0356d586abfca7bc793d71986e8f79a727e1e83 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 31 Aug 2023 12:02:31 +0200 Subject: [PATCH 05/23] [DSC-1224] Refactoring of filename generation in script --- .../app/metadata/export/MetadataSchemaExportScript.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScript.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScript.java index 59b3ddfda1a1..3b07722a4b13 100644 --- a/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScript.java +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/MetadataSchemaExportScript.java @@ -10,6 +10,7 @@ import java.io.File; import java.io.FileInputStream; import java.sql.SQLException; +import java.text.MessageFormat; import org.apache.commons.cli.ParseException; import org.dspace.app.metadata.export.service.MetadataExportServiceFactory; @@ -34,6 +35,8 @@ public class MetadataSchemaExportScript extends DSpaceRunnable> { + protected static String REGISTRY_FILENAME_TEMPLATE = "{0}-types.xml"; + protected MetadataSchemaService metadataSchemaService = ContentServiceFactory.getInstance().getMetadataSchemaService(); @@ -103,7 +106,7 @@ private void exportMetadataSchema(Context context) throws Exception { handler.logInfo("Summarizing export ..."); context.turnOffAuthorisationSystem(); handler.writeFilestream( - context, metadataSchema.getName(), fis, "application/xml", false + context, getFilename(metadataSchema), fis, "application/xml", false ); context.restoreAuthSystemState(); } @@ -113,6 +116,10 @@ private void exportMetadataSchema(Context context) throws Exception { } } + protected String getFilename(MetadataSchema ms) { + return MessageFormat.format(REGISTRY_FILENAME_TEMPLATE, ms.getName()); + } + protected File getExportedFile(Context context) throws DspaceExportMetadataSchemaException { return this.metadataSchemaExportService.exportMetadataSchemaToFile(context, metadataSchema); } From 1df9cb8d53568c1ce8f1008625969051e3600f0e Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Tue, 5 Sep 2023 12:59:08 +0200 Subject: [PATCH 06/23] [DSC-1233] fixed issue with type of Pubmed during import --- .../contributor/SimpleXpathMetadatumContributor.java | 3 +-- dspace/config/spring/api/pubmed-integration.xml | 10 ++++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java index 598b827011b8..76c881817967 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java @@ -9,7 +9,6 @@ import java.util.ArrayList; import java.util.Collection; -import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.annotation.Resource; @@ -149,7 +148,7 @@ public void setQuery(String query) { */ @Override public Collection contributeMetadata(Element t) { - List values = new LinkedList<>(); + List values = new ArrayList<>(); List namespaces = new ArrayList<>(); for (String ns : prefixToNamespaceMapping.keySet()) { diff --git a/dspace/config/spring/api/pubmed-integration.xml b/dspace/config/spring/api/pubmed-integration.xml index 084d63579745..f316a1c0d2dd 100644 --- a/dspace/config/spring/api/pubmed-integration.xml +++ b/dspace/config/spring/api/pubmed-integration.xml @@ -29,7 +29,7 @@ - + @@ -151,16 +151,15 @@ - - + - - + + @@ -231,6 +230,5 @@ these must be present. If multiple are present the result is undefined. - From f813ff26c6868c13393b34ab3f568e006187e0d4 Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Tue, 5 Sep 2023 13:28:00 +0200 Subject: [PATCH 07/23] [DSC-1233] minor fix --- .../contributor/SimpleXpathMetadatumContributor.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java index 76c881817967..598b827011b8 100644 --- a/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java +++ b/dspace-api/src/main/java/org/dspace/importer/external/metadatamapping/contributor/SimpleXpathMetadatumContributor.java @@ -9,6 +9,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.annotation.Resource; @@ -148,7 +149,7 @@ public void setQuery(String query) { */ @Override public Collection contributeMetadata(Element t) { - List values = new ArrayList<>(); + List values = new LinkedList<>(); List namespaces = new ArrayList<>(); for (String ns : prefixToNamespaceMapping.keySet()) { From 62ca6eb5aa496dbcd69d991faeb4a97a5574b2df Mon Sep 17 00:00:00 2001 From: Mykhaylo Boychuk Date: Tue, 5 Sep 2023 16:04:45 +0200 Subject: [PATCH 08/23] [DSC-1233] return null instead of launch the exception --- .../external/provider/impl/LiveImportDataProvider.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/external/provider/impl/LiveImportDataProvider.java b/dspace-api/src/main/java/org/dspace/external/provider/impl/LiveImportDataProvider.java index 21c14813f93d..9897639f04a6 100644 --- a/dspace-api/src/main/java/org/dspace/external/provider/impl/LiveImportDataProvider.java +++ b/dspace-api/src/main/java/org/dspace/external/provider/impl/LiveImportDataProvider.java @@ -9,6 +9,7 @@ import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -135,9 +136,8 @@ public int getNumberOfResults(String query) { * @return */ private ExternalDataObject getExternalDataObject(ImportRecord record) { - //return 400 if no record were found - if (record == null) { - throw new IllegalArgumentException("No record found for query or id"); + if (Objects.isNull(record)) { + return null; } ExternalDataObject externalDataObject = new ExternalDataObject(sourceIdentifier); String id = getFirstValue(record, recordIdMetadata); From 4dfcf692fa944e9d7c7b3eefbadc97e8d2958a23 Mon Sep 17 00:00:00 2001 From: eskander Date: Thu, 7 Sep 2023 17:38:06 +0300 Subject: [PATCH 09/23] [DSC-897] changed the implementation of Categories in statistics section depending --- .../statistics/TopCategoriesGenerator.java | 50 ++++++++++- dspace/config/spring/rest/statistics.xml | 87 ++++++++++++------- 2 files changed, 103 insertions(+), 34 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/TopCategoriesGenerator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/TopCategoriesGenerator.java index 39d8a1730c06..e0ced24e0eb2 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/TopCategoriesGenerator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/TopCategoriesGenerator.java @@ -10,6 +10,7 @@ import static org.dspace.core.Constants.ITEM; import java.io.IOException; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -21,9 +22,11 @@ import org.dspace.app.rest.model.UsageReportRest; import org.dspace.app.rest.utils.UsageReportUtils; import org.dspace.content.DSpaceObject; +import org.dspace.content.MetadataValue; import org.dspace.core.Context; import org.dspace.discovery.configuration.DiscoveryConfiguration; import org.dspace.discovery.configuration.DiscoveryConfigurationService; +import org.dspace.services.ConfigurationService; import org.dspace.statistics.content.StatisticsDatasetDisplay; import org.dspace.statistics.service.SolrLoggerService; import org.springframework.beans.factory.annotation.Autowired; @@ -40,13 +43,19 @@ public class TopCategoriesGenerator extends AbstractUsageReportGenerator { @Autowired private SolrLoggerService solrLoggerService; + @Autowired + private ConfigurationService configurationService; + @Autowired private DiscoveryConfigurationService discoveryConfigurationService; private Map categoryQueries; + private Map> entityCategoryQueries; + public UsageReportRest createUsageReport(Context context, DSpaceObject dso, String startDate, String endDate) { + setCategoryQueries(getEntityType(dso)); Map categoriesCount = getCategoriesCount(dso, startDate, endDate); UsageReportRest usageReportRest = new UsageReportRest(); @@ -129,11 +138,44 @@ public String getReportType() { return UsageReportUtils.TOP_CATEGORIES_REPORT_ID; } - public Map getCategoryQueries() { - return categoryQueries; + private String getEntityType(DSpaceObject dso) { + return dso.getMetadata() + .stream() + .filter(metadataValue -> + "dspace.entity.type".equals(metadataValue.getMetadataField().toString('.'))) + .map(MetadataValue::getValue) + .findFirst() + .orElse(""); } - public void setCategoryQueries(Map categoryQueries) { - this.categoryQueries = categoryQueries; + private void setCategoryQueries(String entityType) { + if (entityCategoryQueries != null && + entityCategoryQueries.containsKey(entityType)) { + categoryQueries = entityCategoryQueries.get(entityType); + } else { + categoryQueries = getDefaultCategoryQueries(); + } } + + private Map getDefaultCategoryQueries() { + return Arrays.stream(getDefaultEntityTypes()) + .collect(Collectors.toMap( + type -> type.toLowerCase(), + type -> "entityType_keyword: '" + type + "'" + )); + } + + private String[] getDefaultEntityTypes() { + return configurationService.getArrayProperty("cris.entity-type"); + } + + public Map> getEntityCategoryQueries() { + return entityCategoryQueries; + } + + public void setEntityCategoryQueries( + Map> entityCategoryQueries) { + this.entityCategoryQueries = entityCategoryQueries; + } + } diff --git a/dspace/config/spring/rest/statistics.xml b/dspace/config/spring/rest/statistics.xml index 23b528eddcb5..76b4944e6e8c 100644 --- a/dspace/config/spring/rest/statistics.xml +++ b/dspace/config/spring/rest/statistics.xml @@ -2,10 +2,13 @@ + http://www.springframework.org/schema/context/spring-context-2.5.xsd + http://www.springframework.org/schema/util + http://www.springframework.org/schema/util/spring-util.xsd"> @@ -405,16 +408,6 @@ - - - - - - - - - - @@ -449,18 +442,62 @@ - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -501,16 +538,6 @@ - - - - - - - - - - From 15f603401975f9780198d81d1014612370e86f7c Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 8 Sep 2023 15:21:49 +0200 Subject: [PATCH 10/23] [DSC-1224] Fixes nil element generation --- .../java/org/dspace/app/metadata/export/model/DcType.java | 8 ++++---- .../app/metadata/export/MetadataSchemaExportScriptIT.java | 2 -- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcType.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcType.java index dcf4d9821cd0..bff2fc77978a 100644 --- a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcType.java +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/DcType.java @@ -46,10 +46,10 @@ public class DcType { @XmlElementRefs({ - @XmlElementRef(name = "schema", type = JAXBElement.class), - @XmlElementRef(name = "element", type = JAXBElement.class), - @XmlElementRef(name = "qualifier", type = JAXBElement.class), - @XmlElementRef(name = "scope_note", type = JAXBElement.class) + @XmlElementRef(name = "schema", type = JAXBElement.class, required = false), + @XmlElementRef(name = "element", type = JAXBElement.class, required = false), + @XmlElementRef(name = "qualifier", type = JAXBElement.class, required = false), + @XmlElementRef(name = "scope_note", type = JAXBElement.class, required = false) }) protected List> schemaOrElementOrQualifier; diff --git a/dspace-api/src/test/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptIT.java b/dspace-api/src/test/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptIT.java index bd53c48602b5..6ed2279bb1fa 100644 --- a/dspace-api/src/test/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptIT.java +++ b/dspace-api/src/test/java/org/dspace/app/metadata/export/MetadataSchemaExportScriptIT.java @@ -116,8 +116,6 @@ public void testMetadataSchemaExport() throws Exception { assertThat(content, containsString("")); assertThat(content, containsString("test")); assertThat(content, containsString("element")); - assertThat(content, containsString("")); - assertThat(content, containsString("")); assertThat(content, containsString("")); } } From 2d6a75cb3911ad54e99277e26c9ac63f4db9b878 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Fri, 8 Sep 2023 15:23:40 +0200 Subject: [PATCH 11/23] [DSC-1224] Fixes nil element generation --- .../dspace/app/metadata/export/model/AbstractJaxbBuilder.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/AbstractJaxbBuilder.java b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/AbstractJaxbBuilder.java index 038d8552e1e2..925020a52631 100644 --- a/dspace-api/src/main/java/org/dspace/app/metadata/export/model/AbstractJaxbBuilder.java +++ b/dspace-api/src/main/java/org/dspace/app/metadata/export/model/AbstractJaxbBuilder.java @@ -47,6 +47,9 @@ public T build() { } protected void addChildElement(C value, Function> mapper) { + if (value == null) { + return; + } addChildElement(mapper.apply(value)); } From 1e1cccd69e772da26eaacb18fd8e51078a986f60 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 31 Aug 2023 09:27:18 +0200 Subject: [PATCH 12/23] [DSC-1117] Fixes wrong property name in ApplicationConfig --- .../main/java/org/dspace/app/rest/utils/ApplicationConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java index 67983ba8f007..91c832c0533e 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/ApplicationConfig.java @@ -38,7 +38,7 @@ public class ApplicationConfig { // Allowed IIIF CORS origins ("Access-Control-Allow-Origin" header) // Can be overridden in DSpace configuration - @Value("${rest.cors.bitstream-allow-origins}") + @Value("${rest.cors.bitstream-allowed-origins}") private String[] bitstreamCorsAllowedOrigins; // Whether to allow credentials (cookies) in CORS requests ("Access-Control-Allow-Credentials" header) From a068fa4ec80ae499e5f95e347632f176035f4358 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Thu, 31 Aug 2023 11:54:42 +0200 Subject: [PATCH 13/23] [DSC-1117] Fixes property key in rest.cfg --- dspace/config/modules/rest.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dspace/config/modules/rest.cfg b/dspace/config/modules/rest.cfg index 9d2eb77be2cc..cfe954d410c7 100644 --- a/dspace/config/modules/rest.cfg +++ b/dspace/config/modules/rest.cfg @@ -134,7 +134,7 @@ rest.regex-clause = text_value ~ ? ##### Customize the REST origins allowed to retrieve the bitstreams ##### ##### default is set to pattern * - use this configuration to restrict/modify this behavior ##### This configuration doens't support the wildcard -bitstream.cors.allowed-origins = +rest.cors.bitstream-allowed-origins = ##### Configure REST Report Filters ##### @@ -233,4 +233,4 @@ rest.search.max.results = 100 # Patterns associated with uri for which do not set the context in read-only mode for GET calls rest.get-in-read-only-mode.exception-patterns = /api/authn/** -rest.get-in-read-only-mode.exception-patterns = /api/cris/orcid/** +rest.get-in-read-only-mode.exception-patterns = /api/cris/orcid/** \ No newline at end of file From 191ac97dbc2fa4e1dc15e5f17887c3334d450757 Mon Sep 17 00:00:00 2001 From: corrado lombardi Date: Mon, 11 Sep 2023 12:57:11 +0200 Subject: [PATCH 14/23] [DSC-1238][CST-11734] added type option to subscription-send process --- .../SubscriptionEmailNotification.java | 13 ++++++++++--- .../SubscriptionEmailNotificationConfiguration.java | 3 +++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotification.java b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotification.java index b429ecbd46e7..cc5cac24eabb 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotification.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotification.java @@ -48,16 +48,23 @@ public void setup() throws ParseException { public void internalRun() throws Exception { assignCurrentUserInContext(); assignSpecialGroupsInContext(); + String typeOption = commandLine.getOptionValue("t"); String frequencyOption = commandLine.getOptionValue("f"); - if (StringUtils.isBlank(frequencyOption)) { - throw new IllegalArgumentException("Option --frequency (-f) must be set"); + if (StringUtils.isBlank(frequencyOption) || StringUtils.isBlank(typeOption)) { + throw new IllegalArgumentException("Options --frequency (-f) and --type (-t) must be set"); } if (!FrequencyType.isSupportedFrequencyType(frequencyOption)) { throw new IllegalArgumentException( "Option f must be one of following values D(Day), W(Week) or M(Month)"); } - subscriptionEmailNotificationService.perform(getContext(), handler, "content", frequencyOption); + + if (!StringUtils.equalsAny(typeOption, "content", "statistics")) { + throw new IllegalArgumentException( + "Option t (type) must be one of \"content\" or \"statistics\""); + } + + subscriptionEmailNotificationService.perform(getContext(), handler, typeOption, frequencyOption); } private void assignCurrentUserInContext() throws SQLException { diff --git a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationConfiguration.java b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationConfiguration.java index 52685b563d9b..d4f76a555936 100644 --- a/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationConfiguration.java +++ b/dspace-api/src/main/java/org/dspace/subscriptions/SubscriptionEmailNotificationConfiguration.java @@ -42,6 +42,9 @@ public boolean isAllowedToExecute(Context context) { public Options getOptions() { if (Objects.isNull(options)) { Options options = new Options(); + options.addOption("t", "type", true, + "Subscription type, Valid values are \"content\" or \"statistics\""); + options.getOption("t").setRequired(true); options.addOption("f", "frequency", true, "Subscription frequency. Valid values include: D (Day), W (Week) and M (Month)"); options.getOption("f").setRequired(true); From c15db0d530aca60f632a2c167f96b3e8db5e865f Mon Sep 17 00:00:00 2001 From: eskander Date: Tue, 12 Sep 2023 17:59:45 +0300 Subject: [PATCH 15/23] [DSC-897] excluded top categories report from Person Collection statistics --- .../AbstractUsageReportGenerator.java | 27 +++++++++++++++++++ .../rest/statistics/UsageReportGenerator.java | 6 +++++ .../app/rest/utils/UsageReportUtils.java | 14 +++++++++- dspace/config/spring/rest/statistics.xml | 5 ++++ 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/AbstractUsageReportGenerator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/AbstractUsageReportGenerator.java index 940773547da4..368733f2ad0a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/AbstractUsageReportGenerator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/AbstractUsageReportGenerator.java @@ -7,6 +7,11 @@ */ package org.dspace.app.rest.statistics; +import java.util.List; + +import org.apache.commons.collections4.CollectionUtils; +import org.apache.commons.lang3.StringUtils; + /** * This is an abstract class that adds common configurable options for the generator * @@ -16,6 +21,7 @@ public abstract class AbstractUsageReportGenerator implements UsageReportGenerat private String viewMode = "table"; private int maxResults = 100; private String relation; + private List excludedEntityTypes; public void setViewMode(String viewMode) { this.viewMode = viewMode; @@ -40,5 +46,26 @@ public String getRelation() { public void setRelation(String relation) { this.relation = relation; } + + public boolean isApplicable(String entityType) { + + if (StringUtils.isEmpty(entityType)) { + return true; + } + + if (CollectionUtils.isNotEmpty(excludedEntityTypes)) { + return !excludedEntityTypes.contains(entityType); + } + + return true; + } + + public List getExcludedEntityTypes() { + return excludedEntityTypes; + } + + public void setExcludedEntityTypes(List excludedEntityTypes) { + this.excludedEntityTypes = excludedEntityTypes; + } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/UsageReportGenerator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/UsageReportGenerator.java index ee542785c8cb..70e0bd499cae 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/UsageReportGenerator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/UsageReportGenerator.java @@ -45,4 +45,10 @@ public UsageReportRest createUsageReport(Context context, */ public String getViewMode(); + /** + * + * @return true if the report applicable for the provided entity type + */ + public boolean isApplicable(String entityType); + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java index 47dbcef1749d..199f57b33405 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java @@ -27,6 +27,7 @@ import org.dspace.content.Bitstream; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; +import org.dspace.content.MetadataValue; import org.dspace.content.Site; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -126,7 +127,8 @@ public List getUsageReportsOfDSO(Context context, for (UsageReportCategoryRest cat : categories) { if (category == null || StringUtils.equals(cat.getId(), category)) { for (Entry entry : cat.getReports().entrySet()) { - if (!reportIds.contains(entry.getKey())) { + if (!reportIds.contains(entry.getKey()) && + entry.getValue().isApplicable(getEntityType(dso))) { reportIds.add(entry.getKey()); reports.add(createUsageReport(context, dso, entry.getKey(), startDate, endDate)); } @@ -136,6 +138,16 @@ public List getUsageReportsOfDSO(Context context, return reports; } + private String getEntityType(DSpaceObject dso) { + return dso.getMetadata() + .stream() + .filter(metadataValue -> + "dspace.entity.type".equals(metadataValue.getMetadataField().toString('.'))) + .map(MetadataValue::getValue) + .findFirst() + .orElse(""); + } + private List getReports(Context context, DSpaceObject dso, String category) { List reports = new ArrayList(); if (dso instanceof Site) { diff --git a/dspace/config/spring/rest/statistics.xml b/dspace/config/spring/rest/statistics.xml index 76b4944e6e8c..f5028b6e3e6d 100644 --- a/dspace/config/spring/rest/statistics.xml +++ b/dspace/config/spring/rest/statistics.xml @@ -441,6 +441,11 @@ + + + Person + + From 883262c908b7b7c35ced2bec04e23c95f27a957f Mon Sep 17 00:00:00 2001 From: eskander Date: Tue, 12 Sep 2023 19:23:02 +0300 Subject: [PATCH 16/23] [DSC-897] fixed broken ITs methods --- .../app/rest/StatisticsRestRepositoryIT.java | 136 +++++++++++------- 1 file changed, 86 insertions(+), 50 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java index 68dee1555f72..c54487620c9f 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java @@ -1296,21 +1296,20 @@ public void usageReportsSearch_Site_mainReports() throws Exception { context.turnOffAuthorisationSystem(); Site site = SiteBuilder.createSite(context).build(); Item item = ItemBuilder.createItem(context, collectionNotVisited) + .withEntityType("Publication") .withTitle("My item") - .withType("Controlled Vocabulary for Resource Type Genres::image") .build(); Item item2 = ItemBuilder.createItem(context, collectionNotVisited) + .withEntityType("Patent") .withTitle("My item 2") - .withType("Controlled Vocabulary for Resource Type Genres::thesis") .build(); Item item3 = ItemBuilder.createItem(context, collectionNotVisited) + .withEntityType("Funding") .withTitle("My item 3") - .withType("Controlled Vocabulary for Resource Type Genres::thesis::bachelor thesis") .build(); Item item4 = ItemBuilder.createItem(context, collectionNotVisited) + .withEntityType("Project") .withTitle("My item 4") - .withType("Controlled Vocabulary for Resource Type Genres::text::periodical::" - + "journal::contribution to journal::journal article") .build(); context.restoreAuthSystemState(); @@ -1395,32 +1394,49 @@ public void usageReportsSearch_Site_mainReports() throws Exception { pointCountry.addValue("views", 5); pointCountry.setIdAndLabel(Locale.US.getCountry(), Locale.US.getDisplayCountry(context.getCurrentLocale())); - UsageReportPointCategoryRest articleCategory = new UsageReportPointCategoryRest(); - articleCategory.addValue("views", 1); - articleCategory.setId("article"); + UsageReportPointCategoryRest publicationCategory = new UsageReportPointCategoryRest(); + publicationCategory.addValue("views", 1); + publicationCategory.setId("publication"); - UsageReportPointCategoryRest thesisCategory = new UsageReportPointCategoryRest(); - thesisCategory.addValue("views", 3); - thesisCategory.setId("thesis"); + UsageReportPointCategoryRest patentCategory = new UsageReportPointCategoryRest(); + patentCategory.addValue("views", 2); + patentCategory.setId("patent"); - UsageReportPointCategoryRest otherCategory = new UsageReportPointCategoryRest(); - otherCategory.addValue("views", 1); - otherCategory.setId("other"); + UsageReportPointCategoryRest fundingCategory = new UsageReportPointCategoryRest(); + fundingCategory.addValue("views", 1); + fundingCategory.setId("funding"); - UsageReportPointCategoryRest bookCategory = new UsageReportPointCategoryRest(); - bookCategory.addValue("views", 0); - bookCategory.setId("book"); + UsageReportPointCategoryRest projectCategory = new UsageReportPointCategoryRest(); + projectCategory.addValue("views", 1); + projectCategory.setId("project"); - UsageReportPointCategoryRest bookChapterCategory = new UsageReportPointCategoryRest(); - bookChapterCategory.addValue("views", 0); - bookChapterCategory.setId("bookChapter"); + UsageReportPointCategoryRest productCategory = new UsageReportPointCategoryRest(); + productCategory.addValue("views", 0); + productCategory.setId("product"); - UsageReportPointCategoryRest datasetCategory = new UsageReportPointCategoryRest(); - datasetCategory.addValue("views", 0); - datasetCategory.setId("dataset"); + UsageReportPointCategoryRest journalCategory = new UsageReportPointCategoryRest(); + journalCategory.addValue("views", 0); + journalCategory.setId("journal"); - List categories = List.of(articleCategory, thesisCategory, otherCategory, bookCategory, - bookChapterCategory, datasetCategory); + UsageReportPointCategoryRest personCategory = new UsageReportPointCategoryRest(); + personCategory.addValue("views", 0); + personCategory.setId("person"); + + UsageReportPointCategoryRest orgUnitCategory = new UsageReportPointCategoryRest(); + orgUnitCategory.addValue("views", 0); + orgUnitCategory.setId("orgunit"); + + UsageReportPointCategoryRest equipmentCategory = new UsageReportPointCategoryRest(); + equipmentCategory.addValue("views", 0); + equipmentCategory.setId("equipment"); + + UsageReportPointCategoryRest eventCategory = new UsageReportPointCategoryRest(); + eventCategory.addValue("views", 0); + eventCategory.setId("event"); + + List categories = List.of(publicationCategory, patentCategory, fundingCategory, + projectCategory, productCategory, journalCategory, personCategory, orgUnitCategory, + equipmentCategory, eventCategory); // And request the sites global usage report (show top most popular items) getClient(adminToken) @@ -2445,6 +2461,11 @@ public void usageReportsSearch_OrgUnitWithPublicationVisited() throws Exception public void usageReportsSearch_Collection_ItemReports() throws Exception { context.turnOffAuthorisationSystem(); + Community community = CommunityBuilder.createCommunity(context).build(); + collectionNotVisited = CollectionBuilder.createCollection(context, community) + .withEntityType("Publication") + .build(); + Item item = ItemBuilder.createItem(context, collectionNotVisited) .withTitle("My item") .withType("Controlled Vocabulary for Resource Type Genres::image") @@ -2701,21 +2722,20 @@ public void usageReportsSearch_Community_ItemReports() throws Exception { collectionNotVisited = CollectionBuilder.createCollection(context, community).build(); Item item = ItemBuilder.createItem(context, collectionNotVisited) + .withEntityType("Publication") .withTitle("My item") - .withType("Controlled Vocabulary for Resource Type Genres::image") .build(); Item item2 = ItemBuilder.createItem(context, collectionNotVisited) + .withEntityType("Patent") .withTitle("My item 2") - .withType("Controlled Vocabulary for Resource Type Genres::thesis") .build(); Item item3 = ItemBuilder.createItem(context, collectionNotVisited) + .withEntityType("Funding") .withTitle("My item 3") - .withType("Controlled Vocabulary for Resource Type Genres::thesis::bachelor thesis") .build(); Item item4 = ItemBuilder.createItem(context, collectionNotVisited) + .withEntityType("Project") .withTitle("My item 4") - .withType("Controlled Vocabulary for Resource Type Genres::text::periodical::" - + "journal::contribution to journal::journal article") .build(); context.restoreAuthSystemState(); @@ -2800,33 +2820,49 @@ public void usageReportsSearch_Community_ItemReports() throws Exception { pointCountry.addValue("views", 5); pointCountry.setIdAndLabel(Locale.US.getCountry(), Locale.US.getDisplayCountry(context.getCurrentLocale())); - UsageReportPointCategoryRest articleCategory = new UsageReportPointCategoryRest(); - articleCategory.addValue("views", 1); - articleCategory.setId("article"); + UsageReportPointCategoryRest publicationCategory = new UsageReportPointCategoryRest(); + publicationCategory.addValue("views", 1); + publicationCategory.setId("publication"); - UsageReportPointCategoryRest thesisCategory = new UsageReportPointCategoryRest(); - thesisCategory.addValue("views", 3); - thesisCategory.setId("thesis"); + UsageReportPointCategoryRest patentCategory = new UsageReportPointCategoryRest(); + patentCategory.addValue("views", 2); + patentCategory.setId("patent"); - UsageReportPointCategoryRest otherCategory = new UsageReportPointCategoryRest(); - otherCategory.addValue("views", 1); - otherCategory.setId("other"); + UsageReportPointCategoryRest fundingCategory = new UsageReportPointCategoryRest(); + fundingCategory.addValue("views", 1); + fundingCategory.setId("funding"); - UsageReportPointCategoryRest bookCategory = new UsageReportPointCategoryRest(); - bookCategory.addValue("views", 0); - bookCategory.setId("book"); + UsageReportPointCategoryRest projectCategory = new UsageReportPointCategoryRest(); + projectCategory.addValue("views", 1); + projectCategory.setId("project"); - UsageReportPointCategoryRest bookChapterCategory = new UsageReportPointCategoryRest(); - bookChapterCategory.addValue("views", 0); - bookChapterCategory.setId("bookChapter"); + UsageReportPointCategoryRest productCategory = new UsageReportPointCategoryRest(); + productCategory.addValue("views", 0); + productCategory.setId("product"); - UsageReportPointCategoryRest datasetCategory = new UsageReportPointCategoryRest(); - datasetCategory.addValue("views", 0); - datasetCategory.setId("dataset"); + UsageReportPointCategoryRest journalCategory = new UsageReportPointCategoryRest(); + journalCategory.addValue("views", 0); + journalCategory.setId("journal"); - List categories = List.of(articleCategory, thesisCategory, otherCategory, bookCategory, - bookChapterCategory, datasetCategory); + UsageReportPointCategoryRest personCategory = new UsageReportPointCategoryRest(); + personCategory.addValue("views", 0); + personCategory.setId("person"); + + UsageReportPointCategoryRest orgUnitCategory = new UsageReportPointCategoryRest(); + orgUnitCategory.addValue("views", 0); + orgUnitCategory.setId("orgunit"); + + UsageReportPointCategoryRest equipmentCategory = new UsageReportPointCategoryRest(); + equipmentCategory.addValue("views", 0); + equipmentCategory.setId("equipment"); + + UsageReportPointCategoryRest eventCategory = new UsageReportPointCategoryRest(); + eventCategory.addValue("views", 0); + eventCategory.setId("event"); + List categories = List.of(publicationCategory, patentCategory, fundingCategory, + projectCategory, productCategory, journalCategory, personCategory, orgUnitCategory, + equipmentCategory, eventCategory); // And request the collections global usage report (show top most popular items) getClient(adminToken) .perform(get("/api/statistics/usagereports/search/object") From 92fb4d3c1265a4370b6d408fe9fb8f966f1643ee Mon Sep 17 00:00:00 2001 From: eskander Date: Tue, 12 Sep 2023 20:14:28 +0300 Subject: [PATCH 17/23] [DSC-897] fixed broken IT method --- .../app/rest/StatisticsRestRepositoryIT.java | 58 ++++++++++++------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java index c54487620c9f..ac28124b4ad4 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java @@ -1972,32 +1972,50 @@ public void usageReportsSearch_ItemNotVisited_AtTime() throws Exception { expectedPoint1.setType("item"); points.add(expectedPoint1); - UsageReportPointCategoryRest articleCategory = new UsageReportPointCategoryRest(); - articleCategory.addValue("views", 0); - articleCategory.setId("article"); - UsageReportPointCategoryRest thesisCategory = new UsageReportPointCategoryRest(); - thesisCategory.addValue("views", 0); - thesisCategory.setId("thesis"); + UsageReportPointCategoryRest publicationCategory = new UsageReportPointCategoryRest(); + publicationCategory.addValue("views", 0); + publicationCategory.setId("publication"); - UsageReportPointCategoryRest otherCategory = new UsageReportPointCategoryRest(); - otherCategory.addValue("views", 0); - otherCategory.setId("other"); + UsageReportPointCategoryRest patentCategory = new UsageReportPointCategoryRest(); + patentCategory.addValue("views", 0); + patentCategory.setId("patent"); - UsageReportPointCategoryRest bookCategory = new UsageReportPointCategoryRest(); - bookCategory.addValue("views", 0); - bookCategory.setId("book"); + UsageReportPointCategoryRest fundingCategory = new UsageReportPointCategoryRest(); + fundingCategory.addValue("views", 0); + fundingCategory.setId("funding"); - UsageReportPointCategoryRest bookChapterCategory = new UsageReportPointCategoryRest(); - bookChapterCategory.addValue("views", 0); - bookChapterCategory.setId("bookChapter"); + UsageReportPointCategoryRest projectCategory = new UsageReportPointCategoryRest(); + projectCategory.addValue("views", 0); + projectCategory.setId("project"); - UsageReportPointCategoryRest datasetCategory = new UsageReportPointCategoryRest(); - datasetCategory.addValue("views", 0); - datasetCategory.setId("dataset"); + UsageReportPointCategoryRest productCategory = new UsageReportPointCategoryRest(); + productCategory.addValue("views", 0); + productCategory.setId("product"); - List categories = List.of(articleCategory, thesisCategory, otherCategory, bookCategory, - bookChapterCategory, datasetCategory); + UsageReportPointCategoryRest journalCategory = new UsageReportPointCategoryRest(); + journalCategory.addValue("views", 0); + journalCategory.setId("journal"); + + UsageReportPointCategoryRest personCategory = new UsageReportPointCategoryRest(); + personCategory.addValue("views", 0); + personCategory.setId("person"); + + UsageReportPointCategoryRest orgUnitCategory = new UsageReportPointCategoryRest(); + orgUnitCategory.addValue("views", 0); + orgUnitCategory.setId("orgunit"); + + UsageReportPointCategoryRest equipmentCategory = new UsageReportPointCategoryRest(); + equipmentCategory.addValue("views", 0); + equipmentCategory.setId("equipment"); + + UsageReportPointCategoryRest eventCategory = new UsageReportPointCategoryRest(); + eventCategory.addValue("views", 0); + eventCategory.setId("event"); + + List categories = List.of(publicationCategory, patentCategory, fundingCategory, + projectCategory, productCategory, journalCategory, personCategory, orgUnitCategory, + equipmentCategory, eventCategory); UsageReportPointRest pointPerMonth = new UsageReportPointDateRest(); pointPerMonth.setId("June 2019"); From a11f60bbaa5eb18b839aa4fed0afb509206f43d5 Mon Sep 17 00:00:00 2001 From: eskander Date: Wed, 13 Sep 2023 14:44:15 +0300 Subject: [PATCH 18/23] [DSC-897] separated the configuration of statistics of all entity types of collection. --- .../AbstractUsageReportGenerator.java | 26 -- .../StatisticsReportsConfiguration.java | 18 ++ .../statistics/TopCategoriesGenerator.java | 41 +-- .../rest/statistics/UsageReportGenerator.java | 6 - .../app/rest/utils/UsageReportUtils.java | 14 +- .../app/rest/StatisticsRestRepositoryIT.java | 2 +- dspace/config/spring/rest/statistics.xml | 249 ++++++++++++++++-- 7 files changed, 263 insertions(+), 93 deletions(-) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/AbstractUsageReportGenerator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/AbstractUsageReportGenerator.java index 368733f2ad0a..23c8f99e5857 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/AbstractUsageReportGenerator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/AbstractUsageReportGenerator.java @@ -7,11 +7,6 @@ */ package org.dspace.app.rest.statistics; -import java.util.List; - -import org.apache.commons.collections4.CollectionUtils; -import org.apache.commons.lang3.StringUtils; - /** * This is an abstract class that adds common configurable options for the generator * @@ -21,7 +16,6 @@ public abstract class AbstractUsageReportGenerator implements UsageReportGenerat private String viewMode = "table"; private int maxResults = 100; private String relation; - private List excludedEntityTypes; public void setViewMode(String viewMode) { this.viewMode = viewMode; @@ -47,25 +41,5 @@ public void setRelation(String relation) { this.relation = relation; } - public boolean isApplicable(String entityType) { - - if (StringUtils.isEmpty(entityType)) { - return true; - } - - if (CollectionUtils.isNotEmpty(excludedEntityTypes)) { - return !excludedEntityTypes.contains(entityType); - } - - return true; - } - - public List getExcludedEntityTypes() { - return excludedEntityTypes; - } - - public void setExcludedEntityTypes(List excludedEntityTypes) { - this.excludedEntityTypes = excludedEntityTypes; - } } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/StatisticsReportsConfiguration.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/StatisticsReportsConfiguration.java index 3e366f7cc9de..8fef2b35853a 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/StatisticsReportsConfiguration.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/StatisticsReportsConfiguration.java @@ -11,6 +11,7 @@ import java.util.Map; import java.util.Optional; +import org.apache.commons.lang3.StringUtils; import org.dspace.app.rest.model.UsageReportCategoryRest; import org.dspace.content.Bitstream; import org.dspace.content.Collection; @@ -40,6 +41,13 @@ public List getCategories(DSpaceObject dso) { } else if (dso instanceof Community) { return mapping.get("community"); } else if (dso instanceof Collection) { + String entityType = getEntityType(dso); + if (StringUtils.isNotEmpty(entityType)) { + List result = mapping.get("collection-" + entityType); + if (result != null) { + return result; + } + } return mapping.get("collection"); } else if (dso instanceof Item) { Item item = (Item) dso; @@ -59,6 +67,16 @@ public List getCategories(DSpaceObject dso) { return null; } + private String getEntityType(DSpaceObject dso) { + return dso.getMetadata() + .stream() + .filter(metadataValue -> + "dspace.entity.type".equals(metadataValue.getMetadataField().toString('.'))) + .map(MetadataValue::getValue) + .findFirst() + .orElse(""); + } + public UsageReportGenerator getReportGenerator(DSpaceObject dso, String reportId) { List categories = getCategories(dso); Optional cat = categories.stream().filter(x -> x.getReports().containsKey(reportId)) diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/TopCategoriesGenerator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/TopCategoriesGenerator.java index e0ced24e0eb2..fbba8f902ee1 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/TopCategoriesGenerator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/TopCategoriesGenerator.java @@ -22,7 +22,6 @@ import org.dspace.app.rest.model.UsageReportRest; import org.dspace.app.rest.utils.UsageReportUtils; import org.dspace.content.DSpaceObject; -import org.dspace.content.MetadataValue; import org.dspace.core.Context; import org.dspace.discovery.configuration.DiscoveryConfiguration; import org.dspace.discovery.configuration.DiscoveryConfigurationService; @@ -51,11 +50,8 @@ public class TopCategoriesGenerator extends AbstractUsageReportGenerator { private Map categoryQueries; - private Map> entityCategoryQueries; - public UsageReportRest createUsageReport(Context context, DSpaceObject dso, String startDate, String endDate) { - setCategoryQueries(getEntityType(dso)); Map categoriesCount = getCategoriesCount(dso, startDate, endDate); UsageReportRest usageReportRest = new UsageReportRest(); @@ -76,8 +72,8 @@ private Map getCategoriesCount(DSpaceObject dso, String startDa Map categoriesCount = new HashMap(); - for (String category : categoryQueries.keySet()) { - String categoryQuery = categoryQueries.get(category); + for (String category : getCategoryQueries().keySet()) { + String categoryQuery = getCategoryQueries().get(category); Integer categoryCount = getCategoryCount(dso, discoveryConfiguration, categoryQuery, startDate, endDate); categoriesCount.put(category, categoryCount); } @@ -114,7 +110,7 @@ private String composeCategoryQuery(DSpaceObject dso, DiscoveryConfiguration con } private String getAllCategoryQueriesReverted() { - return categoryQueries.values().stream() + return getCategoryQueries().values().stream() .filter(categoryQuery -> !OTHER_CATEGORY.equals(categoryQuery)) .map(categoryQuery -> "-" + formatCategoryQuery(categoryQuery)) .collect(Collectors.joining(" AND ")); @@ -138,23 +134,15 @@ public String getReportType() { return UsageReportUtils.TOP_CATEGORIES_REPORT_ID; } - private String getEntityType(DSpaceObject dso) { - return dso.getMetadata() - .stream() - .filter(metadataValue -> - "dspace.entity.type".equals(metadataValue.getMetadataField().toString('.'))) - .map(MetadataValue::getValue) - .findFirst() - .orElse(""); + public Map getCategoryQueries() { + if (categoryQueries == null) { + return getDefaultCategoryQueries(); + } + return categoryQueries; } - private void setCategoryQueries(String entityType) { - if (entityCategoryQueries != null && - entityCategoryQueries.containsKey(entityType)) { - categoryQueries = entityCategoryQueries.get(entityType); - } else { - categoryQueries = getDefaultCategoryQueries(); - } + public void setCategoryQueries(Map categoryQueries) { + this.categoryQueries = categoryQueries; } private Map getDefaultCategoryQueries() { @@ -169,13 +157,4 @@ private String[] getDefaultEntityTypes() { return configurationService.getArrayProperty("cris.entity-type"); } - public Map> getEntityCategoryQueries() { - return entityCategoryQueries; - } - - public void setEntityCategoryQueries( - Map> entityCategoryQueries) { - this.entityCategoryQueries = entityCategoryQueries; - } - } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/UsageReportGenerator.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/UsageReportGenerator.java index 70e0bd499cae..ee542785c8cb 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/UsageReportGenerator.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/statistics/UsageReportGenerator.java @@ -45,10 +45,4 @@ public UsageReportRest createUsageReport(Context context, */ public String getViewMode(); - /** - * - * @return true if the report applicable for the provided entity type - */ - public boolean isApplicable(String entityType); - } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java index 199f57b33405..47dbcef1749d 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/UsageReportUtils.java @@ -27,7 +27,6 @@ import org.dspace.content.Bitstream; import org.dspace.content.DSpaceObject; import org.dspace.content.Item; -import org.dspace.content.MetadataValue; import org.dspace.content.Site; import org.dspace.core.Constants; import org.dspace.core.Context; @@ -127,8 +126,7 @@ public List getUsageReportsOfDSO(Context context, for (UsageReportCategoryRest cat : categories) { if (category == null || StringUtils.equals(cat.getId(), category)) { for (Entry entry : cat.getReports().entrySet()) { - if (!reportIds.contains(entry.getKey()) && - entry.getValue().isApplicable(getEntityType(dso))) { + if (!reportIds.contains(entry.getKey())) { reportIds.add(entry.getKey()); reports.add(createUsageReport(context, dso, entry.getKey(), startDate, endDate)); } @@ -138,16 +136,6 @@ public List getUsageReportsOfDSO(Context context, return reports; } - private String getEntityType(DSpaceObject dso) { - return dso.getMetadata() - .stream() - .filter(metadataValue -> - "dspace.entity.type".equals(metadataValue.getMetadataField().toString('.'))) - .map(MetadataValue::getValue) - .findFirst() - .orElse(""); - } - private List getReports(Context context, DSpaceObject dso, String category) { List reports = new ArrayList(); if (dso instanceof Site) { diff --git a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java index ac28124b4ad4..b0d740142c9d 100644 --- a/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java +++ b/dspace-server-webapp/src/test/java/org/dspace/app/rest/StatisticsRestRepositoryIT.java @@ -2614,7 +2614,7 @@ public void usageReportsSearch_Collection_ItemReports() throws Exception { // And request the collections global usage report (show top most popular items) getClient(adminToken) .perform(get("/api/statistics/usagereports/search/object") - .param("category", "collection-itemReports") + .param("category", "publicationCollection-itemReports") .param("uri", "http://localhost:8080/server/api/core/collections/" + collectionNotVisited.getID())) .andExpect(status().isOk()) .andExpect(jsonPath("$._embedded.usagereports", not(empty()))) diff --git a/dspace/config/spring/rest/statistics.xml b/dspace/config/spring/rest/statistics.xml index f5028b6e3e6d..2ec3f3be3eed 100644 --- a/dspace/config/spring/rest/statistics.xml +++ b/dspace/config/spring/rest/statistics.xml @@ -441,23 +441,63 @@ - - - Person - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -503,6 +543,10 @@ + + + + @@ -833,6 +877,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -922,6 +1083,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From fc82fa80dbc44696d91f43ffe0b47ceb7941e593 Mon Sep 17 00:00:00 2001 From: Vincenzo Mecca Date: Wed, 13 Sep 2023 16:34:05 +0200 Subject: [PATCH 19/23] [DSC-1072] Refactored ternary operator with optional --- .../security/CrisSecurityServiceImpl.java | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/content/security/CrisSecurityServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/security/CrisSecurityServiceImpl.java index 3b92ca5985f4..99add81e862b 100644 --- a/dspace-api/src/main/java/org/dspace/content/security/CrisSecurityServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/security/CrisSecurityServiceImpl.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.UUID; import org.apache.commons.collections.CollectionUtils; @@ -18,7 +19,6 @@ import org.dspace.content.Collection; import org.dspace.content.Item; import org.dspace.content.MetadataValue; -import org.dspace.content.logic.Filter; import org.dspace.content.security.service.CrisSecurityService; import org.dspace.content.service.ItemService; import org.dspace.core.Context; @@ -56,19 +56,17 @@ public boolean hasAccess(Context context, Item item, EPerson user, AccessItemMod .anyMatch(security -> hasAccess(context, item, user, accessMode, security)); } - private boolean hasAccess(Context context, Item item, EPerson user, AccessItemMode accessMode, - CrisSecurity crisSecurity) { - + private boolean hasAccess( + Context context, Item item, EPerson user, AccessItemMode accessMode, CrisSecurity crisSecurity + ) { try { + final boolean checkSecurity = checkSecurity(context, item, user, accessMode, crisSecurity); - boolean checkSecurity = checkSecurity(context, item, user, accessMode, crisSecurity); - Filter additionalFilter = accessMode.getAdditionalFilter(); - - return additionalFilter == null ? checkSecurity - : checkSecurity && additionalFilter.getResult(context, item); - + return Optional.ofNullable(accessMode.getAdditionalFilter()) + .map(filter -> checkSecurity && filter.getResult(context, item)) + .orElse(checkSecurity); } catch (SQLException e) { - throw new RuntimeException(e); + throw new SQLRuntimeException(e); } } From 4efae73ab889227991261f83554f98b9c17fcaf2 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Wed, 20 Sep 2023 10:59:54 +0200 Subject: [PATCH 20/23] [DSC-1241] Bitstream restrictions not reflected on its thumbnail --- .../dspace/content/BitstreamServiceImpl.java | 62 +++++++++++++++++++ .../content/service/BitstreamService.java | 5 ++ ...rcePolicyEPersonReplaceRestController.java | 9 +++ ...ourcePolicyGroupReplaceRestController.java | 9 +++ .../ResourcePolicyRestRepository.java | 10 +++ 5 files changed, 95 insertions(+) diff --git a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java index f2a8680ee58d..b07f23ee23ff 100644 --- a/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java +++ b/dspace-api/src/main/java/org/dspace/content/BitstreamServiceImpl.java @@ -7,12 +7,15 @@ */ package org.dspace.content; +import static org.apache.commons.lang.StringUtils.startsWith; + import java.io.IOException; import java.io.InputStream; import java.sql.SQLException; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Spliterators; import java.util.UUID; import java.util.regex.Pattern; @@ -606,4 +609,63 @@ private Stream streamOf(Iterator iterator) { return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, 0), false); } + @Override + public boolean isOriginalBitstream(DSpaceObject dso) throws SQLException { + + if (dso.getType() != Constants.BITSTREAM) { + return false; + } + + Bitstream bitstream = (Bitstream) dso; + + return bitstream.getBundles().stream() + .anyMatch(bundle -> "ORIGINAL".equals(bundle.getName())); + + } + + @Override + public void updateThumbnailResourcePolicies(Context context, Bitstream bitstream) throws SQLException { + getThumbnail(bitstream) + .ifPresent(thumbnail -> replacePolicies(context, bitstream, thumbnail)); + } + + private void replacePolicies(Context context, Bitstream bitstream, Bitstream thumbnail) { + try { + authorizeService.replaceAllPolicies(context, bitstream, thumbnail); + } catch (SQLException | AuthorizeException e) { + throw new RuntimeException(e); + } + } + + private Optional getThumbnail(Bitstream bitstream) throws SQLException { + return getItem(bitstream) + .flatMap(item -> getThumbnail(item, bitstream.getName())); + } + + private Optional getItem(Bitstream bitstream) throws SQLException { + return bitstream.getBundles().stream() + .flatMap(bundle -> bundle.getItems().stream()) + .findFirst(); + } + + private Optional getThumbnail(Item item, String name) { + List bundles = getThumbnailBundles(item); + if (CollectionUtils.isEmpty(bundles)) { + return Optional.empty(); + } + + return bundles.stream() + .flatMap(bundle -> bundle.getBitstreams().stream()) + .filter(bitstream -> startsWith(bitstream.getName(), name)) + .findFirst(); + } + + private List getThumbnailBundles(Item item) { + try { + return itemService.getBundles(item, "THUMBNAIL"); + } catch (SQLException e) { + throw new RuntimeException(e); + } + } + } diff --git a/dspace-api/src/main/java/org/dspace/content/service/BitstreamService.java b/dspace-api/src/main/java/org/dspace/content/service/BitstreamService.java index 3f5b17630a27..85a4fd140e9a 100644 --- a/dspace-api/src/main/java/org/dspace/content/service/BitstreamService.java +++ b/dspace-api/src/main/java/org/dspace/content/service/BitstreamService.java @@ -22,6 +22,7 @@ import org.dspace.content.Bundle; import org.dspace.content.Collection; import org.dspace.content.Community; +import org.dspace.content.DSpaceObject; import org.dspace.content.Item; import org.dspace.core.Context; @@ -243,4 +244,8 @@ List findShowableByItem(Context context, UUID itemId, String bundleNa List findByItemAndBundleAndMetadata(Context context, Item item, String bundleName, Map filterMetadata); + boolean isOriginalBitstream(DSpaceObject dso) throws SQLException; + + void updateThumbnailResourcePolicies(Context context, Bitstream bitstream) throws SQLException; + } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyEPersonReplaceRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyEPersonReplaceRestController.java index e772aa0abe18..b02869962156 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyEPersonReplaceRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyEPersonReplaceRestController.java @@ -23,7 +23,9 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.ResourcePolicyService; +import org.dspace.content.Bitstream; import org.dspace.content.DSpaceObject; +import org.dspace.content.service.BitstreamService; import org.dspace.core.Context; import org.dspace.eperson.EPerson; import org.springframework.beans.factory.annotation.Autowired; @@ -51,6 +53,8 @@ public class ResourcePolicyEPersonReplaceRestController { private Utils utils; @Autowired private ResourcePolicyService resourcePolicyService; + @Autowired + private BitstreamService bitstreamService; @PreAuthorize("hasPermission(#id, 'resourcepolicy', 'ADMIN')") @RequestMapping(method = PUT, consumes = {"text/uri-list"}) @@ -75,6 +79,11 @@ public ResponseEntity> replaceEPersonOfResourcePolicy(@Pa } EPerson newEPerson = (EPerson) dsoList.get(0); resourcePolicy.setEPerson(newEPerson); + + if (bitstreamService.isOriginalBitstream(resourcePolicy.getdSpaceObject())) { + bitstreamService.updateThumbnailResourcePolicies(context, (Bitstream) resourcePolicy.getdSpaceObject()); + } + context.commit(); return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyGroupReplaceRestController.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyGroupReplaceRestController.java index e9ba0dff4429..40a82068dbce 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyGroupReplaceRestController.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/ResourcePolicyGroupReplaceRestController.java @@ -23,7 +23,9 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.ResourcePolicyService; +import org.dspace.content.Bitstream; import org.dspace.content.DSpaceObject; +import org.dspace.content.service.BitstreamService; import org.dspace.core.Context; import org.dspace.eperson.Group; import org.springframework.beans.factory.annotation.Autowired; @@ -51,6 +53,8 @@ public class ResourcePolicyGroupReplaceRestController { private Utils utils; @Autowired private ResourcePolicyService resourcePolicyService; + @Autowired + private BitstreamService bitstreamService; @PreAuthorize("hasPermission(#id, 'resourcepolicy', 'ADMIN')") @RequestMapping(method = PUT, consumes = {"text/uri-list"}) @@ -75,6 +79,11 @@ public ResponseEntity> replaceGroupOfResourcePolicy(@Path Group newGroup = (Group) dsoList.get(0); resourcePolicy.setGroup(newGroup); + + if (bitstreamService.isOriginalBitstream(resourcePolicy.getdSpaceObject())) { + bitstreamService.updateThumbnailResourcePolicies(context, (Bitstream) resourcePolicy.getdSpaceObject()); + } + context.commit(); return ControllerUtils.toEmptyResponse(HttpStatus.NO_CONTENT); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java index 0b77f96b9b5f..72ca3f254256 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/repository/ResourcePolicyRestRepository.java @@ -30,7 +30,9 @@ import org.dspace.authorize.AuthorizeException; import org.dspace.authorize.ResourcePolicy; import org.dspace.authorize.service.ResourcePolicyService; +import org.dspace.content.Bitstream; import org.dspace.content.DSpaceObject; +import org.dspace.content.service.BitstreamService; import org.dspace.core.Constants; import org.dspace.core.Context; import org.dspace.eperson.EPerson; @@ -76,6 +78,9 @@ public class ResourcePolicyRestRepository extends DSpaceRestRepository Date: Mon, 25 Sep 2023 13:52:41 +0200 Subject: [PATCH 22/23] [DSC-1246] change authentication-oidc.cfg --- dspace/config/modules/authentication-oidc.cfg | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dspace/config/modules/authentication-oidc.cfg b/dspace/config/modules/authentication-oidc.cfg index c9f4a3fdbc86..ed31e69af169 100644 --- a/dspace/config/modules/authentication-oidc.cfg +++ b/dspace/config/modules/authentication-oidc.cfg @@ -32,7 +32,9 @@ authentication-oidc.redirect-url = ${dspace.server.url}/api/authn/oidc # The scopes to request -authentication-oidc.scopes = openid,email,profile +authentication-oidc.scopes = openid +authentication-oidc.scopes = email +authentication-oidc.scopes = profile # Specify if the user can self register using OIDC (true|false). If not specified, true is assumed # This should match the configuration of the OIDC server you are using. The default setting for From d7ca5e1fa729ee84c5af72f356642641e88bfb02 Mon Sep 17 00:00:00 2001 From: Luca Giamminonni Date: Wed, 27 Sep 2023 11:23:24 +0200 Subject: [PATCH 23/23] [CST-11876] Fixed bulk import values split --- .../org/dspace/app/bulkedit/BulkImport.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/dspace-api/src/main/java/org/dspace/app/bulkedit/BulkImport.java b/dspace-api/src/main/java/org/dspace/app/bulkedit/BulkImport.java index 491039cff835..6b1e6d42a095 100644 --- a/dspace-api/src/main/java/org/dspace/app/bulkedit/BulkImport.java +++ b/dspace-api/src/main/java/org/dspace/app/bulkedit/BulkImport.java @@ -13,7 +13,7 @@ import static org.apache.commons.lang3.StringUtils.isAllBlank; import static org.apache.commons.lang3.StringUtils.isBlank; import static org.apache.commons.lang3.StringUtils.isNotBlank; -import static org.apache.commons.lang3.StringUtils.split; +import static org.apache.commons.lang3.StringUtils.splitByWholeSeparator; import static org.apache.commons.lang3.StringUtils.startsWith; import static org.apache.commons.lang3.exception.ExceptionUtils.getRootCauseMessage; import static org.apache.commons.lang3.math.NumberUtils.isCreatable; @@ -609,7 +609,8 @@ private boolean areMetadataValuesValid(Row row, boolean manyMetadataValuesAllowe for (int index = firstMetadataIndex; index < row.getLastCellNum(); index++) { String cellValue = WorkbookUtils.getCellValue(row, index); - String[] values = isNotBlank(cellValue) ? split(cellValue, METADATA_SEPARATOR) : new String[] { "" }; + String[] values = isNotBlank(cellValue) ? splitByWholeSeparator(cellValue, METADATA_SEPARATOR) + : new String[] { "" }; if (values.length > 1 && !manyMetadataValuesAllowed) { handleValidationErrorOnRow(row, "Multiple metadata value on the same cell not allowed " + "in the metadata group sheets: " + cellValue); @@ -743,7 +744,7 @@ private List validateAccessConditions(Row row) { Map accessConditionOptions = getUploadAccessConditions(); return Arrays.stream(getAccessConditionValues(row)) - .map(accessCondition -> split(accessCondition, ACCESS_CONDITION_ATTRIBUTES_SEPARATOR)[0]) + .map(accessCondition -> splitByWholeSeparator(accessCondition, ACCESS_CONDITION_ATTRIBUTES_SEPARATOR)[0]) .filter(accessConditionName -> !accessConditionOptions.containsKey(accessConditionName)) .collect(Collectors.toList()); } @@ -788,14 +789,14 @@ private List buildAccessConditions(Row row, String[] accessCond } return Arrays.stream(accessConditions) - .map(accessCondition -> split(accessCondition, ACCESS_CONDITION_ATTRIBUTES_SEPARATOR)) + .map(accessCondition -> splitByWholeSeparator(accessCondition, ACCESS_CONDITION_ATTRIBUTES_SEPARATOR)) .map(accessConditionAttributes -> buildAccessCondition(accessConditionAttributes)) .collect(Collectors.toList()); } private String[] getAccessConditionValues(Row row) { String accessConditionCellValue = getCellValue(row, ACCESS_CONDITION_HEADER); - return split(accessConditionCellValue, METADATA_SEPARATOR); + return splitByWholeSeparator(accessConditionCellValue, METADATA_SEPARATOR); } private AccessCondition buildAccessCondition(String[] accessCondition) { @@ -1306,12 +1307,13 @@ private void removeSingleMetadata(DSpaceObject dso, MetadataField field, String } private String getMetadataField(String field) { - return field.contains(LANGUAGE_SEPARATOR_PREFIX) ? split(field, LANGUAGE_SEPARATOR_PREFIX)[0] : field; + return field.contains(LANGUAGE_SEPARATOR_PREFIX) ? splitByWholeSeparator(field, LANGUAGE_SEPARATOR_PREFIX)[0] + : field; } private String getMetadataLanguage(String field) { if (field.contains(LANGUAGE_SEPARATOR_PREFIX)) { - return split(field, LANGUAGE_SEPARATOR_PREFIX)[1].replace(LANGUAGE_SEPARATOR_SUFFIX, ""); + return splitByWholeSeparator(field, LANGUAGE_SEPARATOR_PREFIX)[1].replace(LANGUAGE_SEPARATOR_SUFFIX, ""); } return null; } @@ -1364,7 +1366,8 @@ private MultiValuedMap getMetadataFromRow(Row row, Map< if (index >= firstMetadataIndex) { String cellValue = WorkbookUtils.getCellValue(row, index); - String[] values = isNotBlank(cellValue) ? split(cellValue, METADATA_SEPARATOR) : new String[] { "" }; + String[] values = isNotBlank(cellValue) ? splitByWholeSeparator(cellValue, METADATA_SEPARATOR) + : new String[] { "" }; List metadataValues = Arrays.stream(values) .map(value -> buildMetadataValueVO(row, value, isMetadataGroupsSheet))