diff --git a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml index 07aa36fb2b..100eb5e220 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/item-submission.xml @@ -20,6 +20,7 @@ + @@ -75,6 +76,18 @@ submission + + submit.progressbar.accessCondition + org.dspace.app.rest.submit.step.AccessConditionStep + accessCondition + + + + submit.progressbar.accessCondition + org.dspace.app.rest.submit.step.AccessConditionStep + accessCondition + + + @@ -167,6 +181,12 @@ + + + + + + diff --git a/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml index 1f500f6a36..a879f1be91 100644 --- a/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml +++ b/dspace-api/src/test/data/dspaceFolder/config/spring/api/access-conditions.xml @@ -82,7 +82,7 @@ - + diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionAddPatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionAddPatchOperation.java new file mode 100644 index 0000000000..364098bb24 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionAddPatchOperation.java @@ -0,0 +1,105 @@ +/** + * 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.rest.submit.factory.impl; +import java.sql.SQLException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import javax.servlet.http.HttpServletRequest; + +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.AccessConditionDTO; +import org.dspace.app.rest.model.patch.LateObjectEvaluator; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.service.AuthorizeService; +import org.dspace.authorize.service.ResourcePolicyService; +import org.dspace.content.InProgressSubmission; +import org.dspace.content.Item; +import org.dspace.core.Constants; +import org.dspace.core.Context; +import org.dspace.submit.model.AccessConditionConfiguration; +import org.dspace.submit.model.AccessConditionConfigurationService; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Submission "add" operation to add custom resource policies. + * + * @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com) + */ +public class AccessConditionAddPatchOperation extends AddPatchOperation { + + @Autowired + private AuthorizeService authorizeService; + @Autowired + private ResourcePolicyService resourcePolicyService; + @Autowired + private AccessConditionConfigurationService accessConditionConfigurationService; + + @Override + void add(Context context, HttpServletRequest currentRequest, InProgressSubmission source, String path, Object value) + throws Exception { + + String stepId = (String) currentRequest.getAttribute("accessConditionSectionId"); + AccessConditionConfiguration configuration = accessConditionConfigurationService.getMap().get(stepId); + + //"path": "/sections/<:name-of-the-form>/accessConditions/-" + String[] split = getAbsolutePath(path).split("/"); + Item item = source.getItem(); + + List accessConditions = new ArrayList(); + + if (split.length == 1) { + // to replace completely the access conditions + authorizeService.removePoliciesActionFilter(context, item, Constants.READ); + accessConditions = evaluateArrayObject((LateObjectEvaluator) value); + } else if (split.length == 2) { + // to add an access condition + // contains "-", call index-based accessConditions it make not sense + accessConditions.add(evaluateSingleObject((LateObjectEvaluator) value)); + } + + verifyAccessConditions(context, configuration, accessConditions); + // check duplicate policy + checkDuplication(context, item, accessConditions); + // apply policies + AccessConditionResourcePolicyUtils.findApplyResourcePolicy(context, configuration.getOptions(), item, + accessConditions); + } + + private void verifyAccessConditions(Context context, AccessConditionConfiguration configuration, + List accessConditions) throws SQLException, AuthorizeException, ParseException { + for (AccessConditionDTO dto : accessConditions) { + AccessConditionResourcePolicyUtils.canApplyResourcePolicy(context, configuration.getOptions(), + dto.getName(), dto.getStartDate(), dto.getEndDate()); + } + } + + private void checkDuplication(Context context, Item item, List accessConditions) + throws SQLException { + List policies = resourcePolicyService.find(context, item, ResourcePolicy.TYPE_CUSTOM); + for (ResourcePolicy resourcePolicy : policies) { + for (AccessConditionDTO dto : accessConditions) { + if (dto.getName().equals(resourcePolicy.getRpName())) { + throw new UnprocessableEntityException("A policy of the same type already exists!"); + } + } + } + } + + @Override + protected Class getArrayClassForEvaluation() { + return AccessConditionDTO[].class; + } + + @Override + protected Class getClassForEvaluation() { + return AccessConditionDTO.class; + } + +} \ No newline at end of file diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionRemovePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionRemovePatchOperation.java index 47fd4c384a..114c082000 100644 --- a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionRemovePatchOperation.java +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionRemovePatchOperation.java @@ -55,7 +55,7 @@ public class AccessConditionRemovePatchOperation extends RemovePatchOperation policies = resourcePolicyService.find(context, item, ResourcePolicy.TYPE_CUSTOM); - if ((idxToDelete < 0 || idxToDelete >= policies.size()) && policies.isEmpty()) { + if (idxToDelete < 0 || idxToDelete >= policies.size()) { throw new UnprocessableEntityException("The provided index:" + idxToDelete + " is not supported," + " currently the are " + policies.size() + " access conditions"); } diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionReplacePatchOperation.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionReplacePatchOperation.java new file mode 100644 index 0000000000..95c33c5770 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionReplacePatchOperation.java @@ -0,0 +1,178 @@ +/** + * 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.rest.submit.factory.impl; +import java.sql.SQLException; +import java.text.ParseException; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import javax.servlet.http.HttpServletRequest; + +import org.apache.commons.lang.StringUtils; +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.AccessConditionDTO; +import org.dspace.app.rest.model.patch.JsonValueEvaluator; +import org.dspace.app.rest.model.patch.LateObjectEvaluator; +import org.dspace.authorize.AuthorizeException; +import org.dspace.authorize.ResourcePolicy; +import org.dspace.authorize.service.ResourcePolicyService; +import org.dspace.content.InProgressSubmission; +import org.dspace.content.Item; +import org.dspace.core.Context; +import org.dspace.submit.model.AccessConditionConfiguration; +import org.dspace.submit.model.AccessConditionConfigurationService; +import org.dspace.submit.model.AccessConditionOption; +import org.springframework.beans.factory.annotation.Autowired; + +/** + * Submission "replace" operation to replace custom resource policies. + * + * @author Mykhaylo Boychuk (mykhaylo.boychuk at 4science.com) + */ +public class AccessConditionReplacePatchOperation extends ReplacePatchOperation { + + @Autowired + private ResourcePolicyService resourcePolicyService; + @Autowired + private AccessConditionConfigurationService accessConditionConfigurationService; + + @Override + void replace(Context context, HttpServletRequest currentRequest, InProgressSubmission source, String path, + Object value) throws Exception { + + String stepId = (String) currentRequest.getAttribute("accessConditionSectionId"); + AccessConditionConfiguration configuration = accessConditionConfigurationService.getMap().get(stepId); + + // "path" : "/sections/<:name-of-the-form>/accessConditions/0" + String[] split = getAbsolutePath(path).split("/"); + Item item = source.getItem(); + if (split.length == 2) { + int toReplace = Integer.parseInt(split[1]); + AccessConditionDTO accessConditionDTO = evaluateSingleObject((LateObjectEvaluator) value); + if (Objects.nonNull(accessConditionDTO) && Objects.nonNull(getOption(configuration, accessConditionDTO))) { + List policies = resourcePolicyService.find(context, item, ResourcePolicy.TYPE_CUSTOM); + if ((toReplace < 0 || toReplace >= policies.size()) && policies.isEmpty()) { + throw new UnprocessableEntityException("The provided index:" + toReplace + " is not supported," + + " currently the are " + policies.size() + " access conditions"); + } + if (checkDubblication(context, configuration, policies, accessConditionDTO, toReplace, item)) { + context.commit(); + resourcePolicyService.delete(context, policies.get(toReplace)); + getOption(configuration, accessConditionDTO).createPolicy(context, item, + accessConditionDTO.getName(), null, accessConditionDTO.getStartDate(), + accessConditionDTO.getEndDate()); + } + } + } else if (split.length == 3) { + String valueToReplare = getValue(value); + int toReplace = Integer.parseInt(split[1]); + String attributeReplace = split[2]; + List policies = resourcePolicyService.find(context, item, ResourcePolicy.TYPE_CUSTOM); + if ((toReplace < 0 || toReplace >= policies.size()) && policies.isEmpty()) { + throw new UnprocessableEntityException("The provided index:" + toReplace + " is not supported," + + " currently the are " + policies.size() + " access conditions"); + } + ResourcePolicy rpToReplace = policies.get(toReplace); + AccessConditionDTO accessConditionDTO = createDTO(rpToReplace, attributeReplace, valueToReplare); + boolean canApplay = AccessConditionResourcePolicyUtils.canApplyResourcePolicy(context, + configuration.getOptions(), accessConditionDTO.getName(), accessConditionDTO.getStartDate(), + accessConditionDTO.getEndDate()); + if (canApplay) { + switch (attributeReplace) { + case "name": + rpToReplace.setRpName(valueToReplare); + break; + case "startDate": + rpToReplace.setStartDate(new Date(valueToReplare)); + break; + case "endDate": + rpToReplace.setEndDate(new Date(valueToReplare)); + break; + default: + } + } + resourcePolicyService.update(context, rpToReplace); + } + } + + private AccessConditionDTO createDTO(ResourcePolicy rpToReplace, String attributeReplace, String valueToReplare) { + AccessConditionDTO accessCondition = new AccessConditionDTO(); + switch (attributeReplace) { + case "name": + accessCondition.setName(valueToReplare); + accessCondition.setStartDate(rpToReplace.getStartDate()); + accessCondition.setEndDate(rpToReplace.getEndDate()); + return accessCondition; + case "startDate": + accessCondition.setName(rpToReplace.getRpName()); + accessCondition.setStartDate(new Date(valueToReplare)); + accessCondition.setEndDate(rpToReplace.getEndDate()); + return accessCondition; + case "endDate": + accessCondition.setName(rpToReplace.getRpName()); + accessCondition.setStartDate(rpToReplace.getStartDate()); + accessCondition.setEndDate(new Date(valueToReplare)); + return accessCondition; + default: + throw new UnprocessableEntityException("The provided attribute: " + + attributeReplace + " is not supported"); + } + } + + private String getValue(Object value) { + if (value instanceof JsonValueEvaluator) { + JsonValueEvaluator jsonValue = (JsonValueEvaluator) value; + if (jsonValue.getValueNode().fields().hasNext()) { + return jsonValue.getValueNode().fields().next().getValue().asText(); + } + } + return StringUtils.EMPTY; + } + private AccessConditionOption getOption(AccessConditionConfiguration configuration, + AccessConditionDTO accessConditionDTO) { + for (AccessConditionOption option :configuration.getOptions()) { + if (option.getName().equals(accessConditionDTO.getName())) { + return option; + } + } + return null; + } + + private boolean checkDubblication(Context context, AccessConditionConfiguration configuration, + List policies, AccessConditionDTO accessConditionDTO, int toReplace, Item item) + throws SQLException, AuthorizeException, ParseException { + ResourcePolicy rp = policies.get(toReplace); + if (rp.getRpName().equals(accessConditionDTO.getName())) { + boolean canApplay = AccessConditionResourcePolicyUtils.canApplyResourcePolicy(context, + configuration.getOptions(), accessConditionDTO.getName(), + accessConditionDTO.getStartDate(), accessConditionDTO.getEndDate()); + if (canApplay) { + item.getResourcePolicies().remove(rp); + return true; + } + } + for (ResourcePolicy resourcePolicy : policies) { + if (resourcePolicy.getRpName().equals(accessConditionDTO.getName())) { + return false; + } + } + item.getResourcePolicies().remove(rp); + return true; + } + + @Override + protected Class getArrayClassForEvaluation() { + return AccessConditionDTO[].class; + } + + @Override + protected Class getClassForEvaluation() { + return AccessConditionDTO.class; + } + +} \ No newline at end of file diff --git a/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionResourcePolicyUtils.java b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionResourcePolicyUtils.java new file mode 100644 index 0000000000..b9c5215063 --- /dev/null +++ b/dspace-server-webapp/src/main/java/org/dspace/app/rest/submit/factory/impl/AccessConditionResourcePolicyUtils.java @@ -0,0 +1,70 @@ +/** + * 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.rest.submit.factory.impl; +import java.sql.SQLException; +import java.text.ParseException; +import java.util.Date; +import java.util.List; + +import org.dspace.app.rest.exception.UnprocessableEntityException; +import org.dspace.app.rest.model.AccessConditionDTO; +import org.dspace.authorize.AuthorizeException; +import org.dspace.content.DSpaceObject; +import org.dspace.core.Context; +import org.dspace.submit.model.AccessConditionOption; + +/** + * Utility class to reuse methods related to resource-policies + * + * @author Mykhaylo Boychuk (mykhaylo.boychuk@4science.com) + */ +public class AccessConditionResourcePolicyUtils { + + private AccessConditionResourcePolicyUtils() {} + + public static void findApplyResourcePolicy(Context context, List accessConditionOptions, + DSpaceObject obj, List newAccessConditions) + throws SQLException, AuthorizeException, ParseException { + for (AccessConditionDTO newAccessCondition : newAccessConditions) { + String name = newAccessCondition.getName(); + String description = newAccessCondition.getDescription(); + + Date startDate = newAccessCondition.getStartDate(); + Date endDate = newAccessCondition.getEndDate(); + + findApplyResourcePolicy(context, accessConditionOptions, obj, name, description, startDate, endDate); + } + } + + + public static void findApplyResourcePolicy(Context context, + List accessConditionOptions, DSpaceObject obj, String name, + String description, Date startDate, Date endDate) throws SQLException, AuthorizeException, ParseException { + boolean found = false; + for (AccessConditionOption accessConditionOption : accessConditionOptions) { + if (!found && accessConditionOption.getName().equalsIgnoreCase(name)) { + accessConditionOption.createResourcePolicy(context, obj, name, description, startDate, endDate); + found = true; + } + } + if (!found) { + throw new UnprocessableEntityException("The provided policy: " + name + " is not supported!"); + } + } + + public static boolean canApplyResourcePolicy(Context context, + List accessConditionOptions, String name, Date startDate, Date endDate) + throws SQLException, AuthorizeException, ParseException { + for (AccessConditionOption accessConditionOption : accessConditionOptions) { + if (accessConditionOption.getName().equalsIgnoreCase(name)) { + return accessConditionOption.canCreateResourcePolicy(context, name, startDate, endDate); + } + } + return false; + } +} \ No newline at end of file diff --git a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml index 8de0c3b8dc..bb56393d0b 100644 --- a/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml +++ b/dspace-server-webapp/src/main/resources/spring/spring-dspace-core-services.xml @@ -86,7 +86,7 @@ - +