From 9faaad1d45e401414ce54e8e0534d60168ea641c Mon Sep 17 00:00:00 2001 From: Jamal Kaabi-Mofrad Date: Mon, 28 Nov 2016 16:53:39 +0000 Subject: [PATCH] Merged WEBAPP-API (5.2.1) to 5.2.N (5.2.1) 133081 jkaabimofrad: APPSREPO-61: First cut of automatic time expiry enhancement to the quick-sharing functionality. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/DEV/5.2.N/root@133216 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261 --- .../quickShareLinkExpiryActionSpace.xml | 17 + config/alfresco/import-export-context.xml | 1 + .../messages/bootstrap-spaces.properties | 4 + config/alfresco/model/quickShareModel.xml | 10 + .../alfresco/quickshare-services-context.xml | 23 ++ config/alfresco/repository.properties | 8 + .../org/alfresco/model/QuickShareModel.java | 46 +-- .../ScheduledPersistedActionServiceImpl.java | 75 ++-- .../QuickShareLinkExpiryActionException.java | 69 ++++ .../QuickShareLinkExpiryActionExecutor.java | 110 ++++++ .../QuickShareLinkExpiryActionImpl.java | 166 ++++++++ ...ickShareLinkExpiryActionPersisterImpl.java | 195 ++++++++++ .../quickshare/QuickShareServiceImpl.java | 330 +++++++++++++++- .../ScheduledPersistedActionService.java | 7 + .../service/cmr/quickshare/QuickShareDTO.java | 15 +- .../QuickShareLinkExpiryAction.java | 62 +++ .../QuickShareLinkExpiryActionPersister.java | 92 +++++ .../cmr/quickshare/QuickShareService.java | 18 + .../QuickShareServiceIntegrationTest.java | 354 +++++++++++++++++- 19 files changed, 1512 insertions(+), 90 deletions(-) create mode 100644 config/alfresco/bootstrap/quickShareLinkExpiryActionSpace.xml create mode 100644 source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionException.java create mode 100644 source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionExecutor.java create mode 100644 source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionImpl.java create mode 100644 source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionPersisterImpl.java create mode 100644 source/java/org/alfresco/service/cmr/quickshare/QuickShareLinkExpiryAction.java create mode 100644 source/java/org/alfresco/service/cmr/quickshare/QuickShareLinkExpiryActionPersister.java diff --git a/config/alfresco/bootstrap/quickShareLinkExpiryActionSpace.xml b/config/alfresco/bootstrap/quickShareLinkExpiryActionSpace.xml new file mode 100644 index 0000000000..0bce89f65d --- /dev/null +++ b/config/alfresco/bootstrap/quickShareLinkExpiryActionSpace.xml @@ -0,0 +1,17 @@ + + + + + + workspace + SpacesStore + shared_link_expiry_actions_space + ${spaces.quickshare.link_expiry_actions.name} + ${spaces.quickshare.link_expiry_actions.name} + ${spaces.quickshare.link_expiry_actions.description} + + + + \ No newline at end of file diff --git a/config/alfresco/import-export-context.xml b/config/alfresco/import-export-context.xml index 3bba4d0416..a030948dcc 100644 --- a/config/alfresco/import-export-context.xml +++ b/config/alfresco/import-export-context.xml @@ -376,6 +376,7 @@ ${spaces.solr_facets.root.childname} ${spaces.smartfolders.childname} ${spaces.smartdownloads.childname} + ${spaces.quickshare.link_expiry_actions.childname} diff --git a/config/alfresco/messages/bootstrap-spaces.properties b/config/alfresco/messages/bootstrap-spaces.properties index 62792f713e..26d36c00aa 100644 --- a/config/alfresco/messages/bootstrap-spaces.properties +++ b/config/alfresco/messages/bootstrap-spaces.properties @@ -194,3 +194,7 @@ spaces.smartfoldertemplates.description=Smart Folder Templates spaces.smartdownloads.name=Smart Folder Downloads spaces.smartdownloads.description=Smart Folder downloads temporary association data + + +spaces.quickshare.link_expiry_actions.name=Quick Share Link Expiry Actions Space +spaces.quickshare.link_expiry_actions.description=A space used by the system to persist quick share link expiry actions. \ No newline at end of file diff --git a/config/alfresco/model/quickShareModel.xml b/config/alfresco/model/quickShareModel.xml index bc685c5596..cd84f46b3c 100644 --- a/config/alfresco/model/quickShareModel.xml +++ b/config/alfresco/model/quickShareModel.xml @@ -35,6 +35,16 @@ false + + Shared Link Expiry Date + d:datetime + true + false + false + + false + + diff --git a/config/alfresco/quickshare-services-context.xml b/config/alfresco/quickshare-services-context.xml index e53367597d..d053bc4ce0 100644 --- a/config/alfresco/quickshare-services-context.xml +++ b/config/alfresco/quickshare-services-context.xml @@ -84,6 +84,9 @@ + + + @@ -94,4 +97,24 @@ + + + + + + + + + + + + + /${spaces.company_home.childname}/${spaces.dictionary.childname} + alfresco/bootstrap/quickShareLinkExpiryActionSpace.xml + alfresco/messages/bootstrap-spaces + + + + diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 4f3539cecf..98abbe1195 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -485,6 +485,8 @@ spaces.solr_facets.root.childname=srft:facets spaces.smartfolders.childname=app:smart_folders spaces.smartdownloads.childname=app:smart_downloads spaces.transfer_summary_report.location=/${spaces.company_home.childname}/${spaces.dictionary.childname}/${spaces.transfers.childname}/${spaces.inbound_transfer_records.childname} +spaces.quickshare.link_expiry_actions.childname=app:quick_share_link_expiry_actions + # ADM VersionStore Configuration version.store.initialVersion=true @@ -982,6 +984,12 @@ authority.useBridgeTable=true # enable QuickShare - if false then the QuickShare-specific REST APIs will return 403 Forbidden system.quickshare.enabled=true system.quickshare.email.from.default=noreply@alfresco.com +# By default the difference between the quick share expiry date and the current time must be at least 1 day (24 hours). +# However, this can be changed to at least 1 hour or 1 minute for testing purposes. For example, +# setting the value to MINUTES, means the service will calculate the difference between NOW and the given expiry date +# in terms of minutes and checks for the difference to be greater than 1 minute. +# DAYS | HOURS | MINUTES +system.quickshare.expiry_date.enforce.period=DAYS # Oubound Mail mail.service.corePoolSize=8 diff --git a/source/java/org/alfresco/model/QuickShareModel.java b/source/java/org/alfresco/model/QuickShareModel.java index 81158a0be1..de1239a29e 100644 --- a/source/java/org/alfresco/model/QuickShareModel.java +++ b/source/java/org/alfresco/model/QuickShareModel.java @@ -23,25 +23,27 @@ * along with Alfresco. If not, see . * #L% */ -package org.alfresco.model; - -import org.alfresco.service.namespace.QName; - - -/** - * QuickShare Model Constants - * - * @author janv - */ -public interface QuickShareModel -{ - // Namespaces - static final String QSHARE_MODEL_1_0_URI = "http://www.alfresco.org/model/qshare/1.0"; - - // Aspects - static final QName ASPECT_QSHARE = QName.createQName(QSHARE_MODEL_1_0_URI, "shared"); - - // Properties - static final QName PROP_QSHARE_SHAREDID = QName.createQName(QSHARE_MODEL_1_0_URI, "sharedId"); - static final QName PROP_QSHARE_SHAREDBY = QName.createQName(QSHARE_MODEL_1_0_URI, "sharedBy"); -} +package org.alfresco.model; + +import org.alfresco.service.namespace.QName; + + +/** + * QuickShare Model Constants + * + * @author janv + */ +public interface QuickShareModel +{ + // Namespaces + static final String QSHARE_MODEL_1_0_URI = "http://www.alfresco.org/model/qshare/1.0"; + + // Aspects + static final QName ASPECT_QSHARE = QName.createQName(QSHARE_MODEL_1_0_URI, "shared"); + + // Properties + static final QName PROP_QSHARE_SHAREDID = QName.createQName(QSHARE_MODEL_1_0_URI, "sharedId"); + static final QName PROP_QSHARE_SHAREDBY = QName.createQName(QSHARE_MODEL_1_0_URI, "sharedBy"); + static final QName PROP_QSHARE_EXPIRY_DATE = QName.createQName(QSHARE_MODEL_1_0_URI, "expiryDate"); + +} diff --git a/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceImpl.java b/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceImpl.java index e394b36ac2..b94793630c 100644 --- a/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceImpl.java +++ b/source/java/org/alfresco/repo/action/scheduled/ScheduledPersistedActionServiceImpl.java @@ -1,28 +1,28 @@ -/* - * #%L - * Alfresco Repository - * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited - * %% - * This file is part of the Alfresco software. - * If the software was purchased under a paid Alfresco license, the terms of - * the paid license agreement will prevail. Otherwise, the software is - * provided under the following open source license terms: - * - * Alfresco is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * Alfresco is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with Alfresco. If not, see . - * #L% - */ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ package org.alfresco.repo.action.scheduled; import java.util.ArrayList; @@ -132,7 +132,7 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc { this.runtimeActionService = runtimeActionService; } - + protected void locatePersistanceFolder() { @@ -193,7 +193,7 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc public void saveSchedule(ScheduledPersistedAction schedule) { ScheduledPersistedActionImpl scheduleImpl = (ScheduledPersistedActionImpl)schedule; - + // Remove if already there removeFromScheduler(scheduleImpl); @@ -202,7 +202,7 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc // if not already persisted, create the persistent schedule createPersistentSchedule(scheduleImpl); } - + // update the persistent schedule with schedule properties updatePersistentSchedule(scheduleImpl); @@ -297,33 +297,36 @@ public class ScheduledPersistedActionServiceImpl implements ScheduledPersistedAc schedule.setPersistedAtNodeRef(null); } - /** - * Returns the schedule for the specified action, or null if it isn't - * currently scheduled. - */ + @Override public ScheduledPersistedAction getSchedule(Action persistedAction) { NodeRef nodeRef = persistedAction.getNodeRef(); - if (nodeRef == null) + return getSchedule(nodeRef); + } + + @Override + public ScheduledPersistedAction getSchedule(NodeRef persistedActionNodeRef) + { + if (persistedActionNodeRef == null) { // action is not persistent return null; } // locate associated schedule for action - List assocs = nodeService.getSourceAssocs(nodeRef, ActionModel.ASSOC_SCHEDULED_ACTION); + List assocs = nodeService.getSourceAssocs(persistedActionNodeRef, ActionModel.ASSOC_SCHEDULED_ACTION); AssociationRef scheduledAssoc = null; for (AssociationRef assoc : assocs) { scheduledAssoc = assoc; } - + if (scheduledAssoc == null) { // there is no associated schedule return null; } - + // load the scheduled action return loadPersistentSchedule(scheduledAssoc.getSourceRef()); } diff --git a/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionException.java b/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionException.java new file mode 100644 index 0000000000..153ee004bd --- /dev/null +++ b/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionException.java @@ -0,0 +1,69 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.repo.quickshare; + +import org.alfresco.error.AlfrescoRuntimeException; + +/** + * @author Jamal Kaabi-Mofrad + */ +public class QuickShareLinkExpiryActionException extends AlfrescoRuntimeException +{ + + private static final long serialVersionUID = 6298296507061784874L; + + public QuickShareLinkExpiryActionException(String msgId) + { + super(msgId); + } + + public QuickShareLinkExpiryActionException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + + public QuickShareLinkExpiryActionException(String msgId, Throwable cause) + { + super(msgId, cause); + } + + public static class InvalidExpiryDateException extends QuickShareLinkExpiryActionException + { + + private static final long serialVersionUID = 7529497485776706174L; + + public InvalidExpiryDateException(String msgId) + { + super(msgId); + } + + public InvalidExpiryDateException(String msgId, Object[] msgParams) + { + super(msgId, msgParams); + } + } +} diff --git a/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionExecutor.java b/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionExecutor.java new file mode 100644 index 0000000000..61eeb084cd --- /dev/null +++ b/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionExecutor.java @@ -0,0 +1,110 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.repo.quickshare; + +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.quickshare.QuickShareLinkExpiryAction; +import org.alfresco.service.cmr.quickshare.QuickShareService; +import org.alfresco.service.cmr.repository.NodeRef; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.util.List; + +/** + * This action executor unshares the shared link when the quick share link expiry action is triggered. + * + * @author Jamal Kaabi-Mofrad + */ +public class QuickShareLinkExpiryActionExecutor extends ActionExecuterAbstractBase +{ + private static final Log LOGGER = LogFactory.getLog(QuickShareLinkExpiryActionExecutor.class); + + private QuickShareService quickShareService; + + public void setQuickShareService(QuickShareService quickShareService) + { + this.quickShareService = quickShareService; + } + + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + if (!(action instanceof QuickShareLinkExpiryAction)) + { + if (action.getActionDefinitionName().equals(QuickShareLinkExpiryActionImpl.EXECUTOR_NAME)) + { + action = new QuickShareLinkExpiryActionImpl(action); + } + else + { + return; + } + } + QuickShareLinkExpiryAction quickShareLinkExpiryAction = (QuickShareLinkExpiryAction) action; + String sharedId = quickShareLinkExpiryAction.getSharedId(); + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Unsharing the shared id [" + sharedId + "] for the node:" + quickShareService.getMetaData(sharedId).get("name")); + } + + if (StringUtils.isEmpty(sharedId)) + { + throw new QuickShareLinkExpiryActionException("Shared id is not specified."); + + } + try + { + quickShareService.unshareContent(sharedId); + if (LOGGER.isDebugEnabled()) + { + LOGGER.debug("Unshared the shared id [" + sharedId + "] for the node:" + quickShareService.getMetaData(sharedId).get("name")); + } + } + catch (Exception ex) + { + if (ex instanceof QuickShareLinkExpiryActionException) + { + LOGGER.error("Couldn't delete the quick share expiry action [" + quickShareLinkExpiryAction.getNodeRef() + "] for the sharedId:" + + sharedId); + } + else + { + LOGGER.error("Couldn't unshare the shared Id:" + sharedId); + } + } + } + + @Override + protected void addParameterDefinitions(List paramList) + { + // Not used - our definitions hold everything + } +} diff --git a/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionImpl.java b/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionImpl.java new file mode 100644 index 0000000000..ab76c9ce3f --- /dev/null +++ b/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionImpl.java @@ -0,0 +1,166 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.repo.quickshare; + +import org.alfresco.repo.action.ActionImpl; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedAction; +import org.alfresco.service.cmr.quickshare.QuickShareLinkExpiryAction; +import org.alfresco.service.namespace.QName; + +import java.io.Serializable; +import java.util.Date; + +/** + * Quick share link expiry action implementation class. + * + * @author Jamal Kaabi-Mofrad + */ +public class QuickShareLinkExpiryActionImpl extends ActionImpl implements QuickShareLinkExpiryAction +{ + + public static final String EXECUTOR_NAME = "quickShareLinkExpiryActionExecutor"; + public static final String QUICK_SHARE_LINK_EXPIRY_ACTION_NAME = "quickShareLinkExpiryActionName"; + + private static final long serialVersionUID = 2497810872555230797L; + + private ScheduledPersistedAction schedule; + + /** + * @param id the action id + * @param sharedId a unique name for the quick share link expiry action. + * @param description the action description + */ + public QuickShareLinkExpiryActionImpl(String id, String sharedId, String description) + { + super(null, id, EXECUTOR_NAME); + setActionQName(createQName(sharedId)); + setDescription(description); + } + + public QuickShareLinkExpiryActionImpl(Action action) + { + super(action); + } + + protected void setActionQName(QName actionQName) + { + setParameterValue(QUICK_SHARE_LINK_EXPIRY_ACTION_NAME, actionQName); + } + + @Override + public QName getActionQName() + { + Serializable parameterValue = getParameterValue(QUICK_SHARE_LINK_EXPIRY_ACTION_NAME); + return (QName) parameterValue; + } + + @Override + public ScheduledPersistedAction getSchedule() + { + return this.schedule; + } + + @Override + public void setSchedule(ScheduledPersistedAction schedule) + { + this.schedule = schedule; + } + + @Override + public String getSharedId() + { + // As we use the sharedId to generate the action's QName. + QName qName = getActionQName(); + if (qName != null) + { + return qName.getLocalName(); + } + return null; + } + + @Override + public Date getScheduleStart() + { + if (schedule == null) + return null; + return schedule.getScheduleStart(); + } + + @Override + public void setScheduleStart(Date startDate) + { + if (schedule == null) + { + throw new IllegalStateException("Scheduling is not enabled."); + } + schedule.setScheduleStart(startDate); + } + + @Override + public Integer getScheduleIntervalCount() + { + if (schedule == null) + { + return null; + } + return schedule.getScheduleIntervalCount(); + } + + @Override + public void setScheduleIntervalCount(Integer count) + { + if (schedule == null) + { + throw new IllegalStateException("Scheduling is not enabled."); + } + schedule.setScheduleIntervalCount(count); + } + + @Override + public IntervalPeriod getScheduleIntervalPeriod() + { + if (schedule == null) + { + return null; + } + return schedule.getScheduleIntervalPeriod(); + } + + @Override + public void setScheduleIntervalPeriod(IntervalPeriod period) + { + if (schedule == null) + throw new IllegalStateException("Scheduling is not enabled."); + schedule.setScheduleIntervalPeriod(period); + } + + public static QName createQName(String sharedId) + { + return QName.createQName(null, sharedId); + } +} diff --git a/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionPersisterImpl.java b/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionPersisterImpl.java new file mode 100644 index 0000000000..7d1517b646 --- /dev/null +++ b/source/java/org/alfresco/repo/quickshare/QuickShareLinkExpiryActionPersisterImpl.java @@ -0,0 +1,195 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.repo.quickshare; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.RuntimeActionService; +import org.alfresco.repo.importer.ImporterBootstrap; +import org.alfresco.repo.policy.BehaviourFilter; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.quickshare.QuickShareLinkExpiryAction; +import org.alfresco.service.cmr.quickshare.QuickShareLinkExpiryActionPersister; +import org.alfresco.service.cmr.repository.ChildAssociationRef; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.repository.StoreRef; +import org.alfresco.service.namespace.QName; +import org.alfresco.util.ParameterCheck; + +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +/** + * Default implementation of the {@link QuickShareLinkExpiryActionPersister}. + * It is responsible for persisting and retrieving the quick share link expiry actions. + * + * @author Jamal Kaabi-Mofrad + */ +public class QuickShareLinkExpiryActionPersisterImpl implements QuickShareLinkExpiryActionPersister +{ + protected static final NodeRef QUICK_SHARE_LINK_EXPIRY_ACTIONS_ROOT = new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, + "shared_link_expiry_actions_space"); + + /* Injected services */ + private NodeService nodeService; + private RuntimeActionService runtimeActionService; + private BehaviourFilter behaviourFilter; + private ImporterBootstrap importerBootstrap; + private Properties bootstrapView; + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + public void setRuntimeActionService(RuntimeActionService runtimeActionService) + { + this.runtimeActionService = runtimeActionService; + } + + public void setBehaviourFilter(BehaviourFilter behaviourFilter) + { + this.behaviourFilter = behaviourFilter; + } + + public void setImporterBootstrap(ImporterBootstrap importerBootstrap) + { + this.importerBootstrap = importerBootstrap; + } + + public void setBootstrapView(Properties bootstrapView) + { + this.bootstrapView = bootstrapView; + } + + @Override + public void saveQuickShareLinkExpiryAction(QuickShareLinkExpiryAction linkExpiryAction) + { + ParameterCheck.mandatory("linkExpiryAction", linkExpiryAction); + + NodeRef actionNodeRef = findOrCreateActionNode(linkExpiryAction); + try + { + behaviourFilter.disableBehaviour(actionNodeRef); + runtimeActionService.saveActionImpl(actionNodeRef, linkExpiryAction); + } + finally + { + behaviourFilter.enableBehaviour(actionNodeRef); + } + } + + @Override + public NodeRef getQuickShareLinkExpiryActionNode(QName linkExpiryActionName) + { + ParameterCheck.mandatory("linkExpiryActionName", linkExpiryActionName); + + NodeRef rootNodeRef = getOrCreateActionsRootNodeRef(); + List childAssocs = nodeService.getChildAssocs(rootNodeRef, ContentModel.ASSOC_CONTAINS, linkExpiryActionName); + if (!childAssocs.isEmpty()) + { + if (childAssocs.size() > 1) + { + throw new QuickShareLinkExpiryActionException( + "Multiple quick share link expiry actions with the name: " + linkExpiryActionName + " exist!"); + } + return childAssocs.get(0).getChildRef(); + } + return null; + } + + @Override + public QuickShareLinkExpiryAction loadQuickShareLinkExpiryAction(QName linkExpiryActionName) + { + NodeRef actionNode = getQuickShareLinkExpiryActionNode(linkExpiryActionName); + return loadQuickShareLinkExpiryAction(actionNode); + } + + @Override + public QuickShareLinkExpiryAction loadQuickShareLinkExpiryAction(NodeRef linkExpiryActionNodeRef) + { + if (linkExpiryActionNodeRef != null) + { + Action action = runtimeActionService.createAction(linkExpiryActionNodeRef); + return new QuickShareLinkExpiryActionImpl(action); + } + + return null; + } + + @Override + public void deleteQuickShareLinkExpiryAction(QuickShareLinkExpiryAction linkExpiryAction) + { + ParameterCheck.mandatory("linkExpiryAction", linkExpiryAction); + + NodeRef actionNodeRef = findOrCreateActionNode(linkExpiryAction); + if (actionNodeRef != null) + { + nodeService.deleteNode(actionNodeRef); + } + } + + private NodeRef findOrCreateActionNode(QuickShareLinkExpiryAction linkExpiryAction) + { + QName actionQName = linkExpiryAction.getActionQName(); + NodeRef actionNode = getQuickShareLinkExpiryActionNode(actionQName); + if (actionNode == null) + { + NodeRef rootNodeRef = getOrCreateActionsRootNodeRef(); + actionNode = runtimeActionService.createActionNodeRef(linkExpiryAction, rootNodeRef, ContentModel.ASSOC_CONTAINS, actionQName); + } + return actionNode; + } + + /** + * Gets the folder containing quick share link expiry action nodes. + * If it doesn't exist then it tries to create it. + * + * @throws QuickShareLinkExpiryActionException if the folder node can't be created. + */ + private NodeRef getOrCreateActionsRootNodeRef() + { + if (!nodeService.exists(QUICK_SHARE_LINK_EXPIRY_ACTIONS_ROOT)) + { + //import + // This lazy create approach, avoids the need to create a patch for existing repo. + List singletonList = new ArrayList<>(); + singletonList.add(bootstrapView); + importerBootstrap.setBootstrapViews(singletonList); + importerBootstrap.setUseExistingStore(true); + importerBootstrap.bootstrap(); + + // if still doesn't exist, throw an exception. + if (!nodeService.exists(QUICK_SHARE_LINK_EXPIRY_ACTIONS_ROOT)) + { + throw new QuickShareLinkExpiryActionException("Couldn't import the quick share link expiry actions root node."); + } + } + return QUICK_SHARE_LINK_EXPIRY_ACTIONS_ROOT; + } +} diff --git a/source/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java b/source/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java index a65c37409a..14ad841f06 100644 --- a/source/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java +++ b/source/java/org/alfresco/repo/quickshare/QuickShareServiceImpl.java @@ -29,6 +29,7 @@ import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -64,12 +65,17 @@ import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; import org.alfresco.repo.thumbnail.ThumbnailDefinition; import org.alfresco.service.cmr.action.Action; import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.action.scheduled.SchedulableAction.IntervalPeriod; +import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedAction; +import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedActionService; import org.alfresco.service.cmr.attributes.AttributeService; import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.preference.PreferenceService; import org.alfresco.service.cmr.quickshare.InvalidSharedIdException; import org.alfresco.service.cmr.quickshare.QuickShareDTO; import org.alfresco.service.cmr.quickshare.QuickShareDisabledException; +import org.alfresco.service.cmr.quickshare.QuickShareLinkExpiryAction; +import org.alfresco.service.cmr.quickshare.QuickShareLinkExpiryActionPersister; import org.alfresco.service.cmr.quickshare.QuickShareService; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ContentData; @@ -96,11 +102,13 @@ import org.alfresco.util.PropertyCheck; import org.apache.commons.codec.binary.Base64; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.joda.time.PeriodType; import org.safehaus.uuid.UUID; import org.safehaus.uuid.UUIDGenerator; import org.springframework.extensions.surf.util.I18NUtil; - /** * QuickShare Service implementation. * @@ -147,7 +155,10 @@ public class QuickShareServiceImpl implements QuickShareService, private boolean enabled; private String defaultEmailSender; private ClientAppConfig clientAppConfig; - + private ScheduledPersistedActionService scheduledPersistedActionService; + private QuickShareLinkExpiryActionPersister quickShareLinkExpiryActionPersister; + // The default period is in DAYS, but we allow HOURS|MINUTES as well for testing purposes. + private ExpiryDatePeriod expiryDatePeriod = ExpiryDatePeriod.DAYS; /** * Set the attribute service */ @@ -300,6 +311,39 @@ public class QuickShareServiceImpl implements QuickShareService, this.clientAppConfig = clientAppConfig; } + /** + * Spring configuration + * + * @param scheduledPersistedActionService the scheduledPersistedActionService to set + */ + public void setScheduledPersistedActionService(ScheduledPersistedActionService scheduledPersistedActionService) + { + this.scheduledPersistedActionService = scheduledPersistedActionService; + } + + /** + * Spring configuration + * + * @param quickShareLinkExpiryActionPersister the quickShareLinkExpiryActionPersister to set + */ + public void setQuickShareLinkExpiryActionPersister(QuickShareLinkExpiryActionPersister quickShareLinkExpiryActionPersister) + { + this.quickShareLinkExpiryActionPersister = quickShareLinkExpiryActionPersister; + } + + /** + * Spring configuration + * + * @param expiryDatePeriod the expiryDatePeriod to set + */ + public void setExpiryDatePeriod(String expiryDatePeriod) + { + if (expiryDatePeriod != null) + { + this.expiryDatePeriod = ExpiryDatePeriod.valueOf(expiryDatePeriod.toUpperCase()); + } + } + private void checkMandatoryProperties() { PropertyCheck.mandatory(this, "attributeService", attributeService); @@ -319,6 +363,8 @@ public class QuickShareServiceImpl implements QuickShareService, PropertyCheck.mandatory(this, "searchService", searchService); PropertyCheck.mandatory(this, "siteService", siteService); PropertyCheck.mandatory(this, "authorityService", authorityService); + PropertyCheck.mandatory(this, "scheduledPersistedActionService", scheduledPersistedActionService); + PropertyCheck.mandatory(this, "quickShareLinkExpiryActionPersister", quickShareLinkExpiryActionPersister); } /** @@ -349,29 +395,35 @@ public class QuickShareServiceImpl implements QuickShareService, @Override public QuickShareDTO shareContent(final NodeRef nodeRef) + { + return shareContent(nodeRef, DateTime.now().plusMinutes(1).toDate()); + } + + @Override + public QuickShareDTO shareContent(NodeRef nodeRef, Date expiryDate) throws QuickShareDisabledException, InvalidNodeRefException { checkEnabled(); - + //Check the node is the correct type final QName typeQName = nodeService.getType(nodeRef); if (isSharable(typeQName) == false) { throw new InvalidNodeRefException(nodeRef); } - + final String sharedId; - + // Only add the quick share aspect if it isn't already present. // If it is retura dto built from the existing properties. if (! nodeService.getAspects(nodeRef).contains(QuickShareModel.ASPECT_QSHARE)) { UUID uuid = UUIDGenerator.getInstance().generateRandomBasedUUID(); sharedId = Base64.encodeBase64URLSafeString(uuid.toByteArray()); // => 22 chars (eg. q3bEKPeDQvmJYgt4hJxOjw) - + final Map props = new HashMap(2); props.put(QuickShareModel.PROP_QSHARE_SHAREDID, sharedId); props.put(QuickShareModel.PROP_QSHARE_SHAREDBY, AuthenticationUtil.getRunAsUser()); - + // Disable audit to preserve modifier and modified date // see MNT-11960 behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); @@ -393,7 +445,7 @@ public class QuickShareServiceImpl implements QuickShareService, } final NodeRef tenantNodeRef = tenantService.getName(nodeRef); - + TenantUtil.runAsDefaultTenant(new TenantRunAsWork() { public Void doWork() throws Exception @@ -402,20 +454,20 @@ public class QuickShareServiceImpl implements QuickShareService, return null; } }); - + final StringBuffer sb = new StringBuffer(); sb.append("{").append("\"sharedId\":\"").append(sharedId).append("\"").append("}"); - + eventPublisher.publishEvent(new EventPreparator(){ @Override public Event prepareEvent(String user, String networkId, String transactionId) - { + { return new ActivityEvent("quickshare", transactionId, networkId, user, nodeRef.getId(), null, typeQName.toString(), Client.asType(ClientType.webclient), sb.toString(), null, null, 0l, null); } }); - + if (logger.isInfoEnabled()) { logger.info("QuickShare - shared content: "+sharedId+" ["+nodeRef+"]"); @@ -429,10 +481,28 @@ public class QuickShareServiceImpl implements QuickShareService, logger.debug("QuickShare - content already shared: "+sharedId+" ["+nodeRef+"]"); } } - - - return new QuickShareDTO(sharedId); - } + + if (expiryDate != null) + { + AuthenticationUtil.runAsSystem((RunAsWork) () -> { + // Create and save the expiry action + saveSharedLinkExpiryAction(sharedId, expiryDate); + // if we get here, it means the expiry date is validated and the action + // is created and saved, so now set the expiryDate property. + behaviourFilter.disableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); + try + { + nodeService.setProperty(nodeRef, QuickShareModel.PROP_QSHARE_EXPIRY_DATE, expiryDate); + } + finally + { + behaviourFilter.enableBehaviour(nodeRef, ContentModel.ASPECT_AUDITABLE); + } + return null; + }); + } + return new QuickShareDTO(sharedId, expiryDate); + } /** * Is this service enable? @@ -530,6 +600,8 @@ public class QuickShareServiceImpl implements QuickShareService, if (nodeProps.containsKey(QuickShareModel.PROP_QSHARE_SHAREDID)) { metadata.put("sharedId", nodeProps.get(QuickShareModel.PROP_QSHARE_SHAREDID)); + metadata.put("expiryDate", nodeProps.get(QuickShareModel.PROP_QSHARE_EXPIRY_DATE)); + } else { @@ -719,6 +791,20 @@ public class QuickShareServiceImpl implements QuickShareService, return null; } }); + + try + { + // Remove scheduled expiry action if any + NodeRef expiryActionNodeRef = getQuickShareLinkExpiryActionNode(sharedId); + if (expiryActionNodeRef != null) + { + deleteQuickShareLinkExpiryAction(expiryActionNodeRef); + } + } + catch (Exception ex) + { + throw new QuickShareLinkExpiryActionException("Couldn't delete the quick share link expiry action for the sharedId:" + sharedId); + } } @Override @@ -762,7 +848,7 @@ public class QuickShareServiceImpl implements QuickShareService, }, tenantDomain); removeSharedId(sharedId); - + if (logger.isInfoEnabled()) { logger.info("QuickShare - unshared content: "+sharedId+" ["+nodeRef+"]"); @@ -1100,5 +1186,215 @@ public class QuickShareServiceImpl implements QuickShareService, } } } + + /** + * Creates and persists the quick share link expiry action and its related schedule. + */ + protected void saveSharedLinkExpiryAction(String sharedId, Date expiryDate) + { + ParameterCheck.mandatory("expiryDate", expiryDate); + // Validate the given expiry date + checkExpiryDate(expiryDate); + + if (logger.isDebugEnabled()) + { + logger.debug("Creating shared link expiry action for the sharedId:" + sharedId); + } + + final NodeRef expiryActionNodeRef = getQuickShareLinkExpiryActionNode(sharedId); + // If an expiry action already exists for the specified shared Id, first remove it, before creating a new one. + if (expiryActionNodeRef != null) + { + deleteQuickShareLinkExpiryAction(expiryActionNodeRef); + } + + // Create the expiry action + final QuickShareLinkExpiryAction expiryAction = new QuickShareLinkExpiryActionImpl(java.util.UUID.randomUUID().toString(), sharedId, + "QuickShare link expiry action"); + // Create the persisted schedule + final ScheduledPersistedAction schedule = scheduledPersistedActionService.createSchedule(expiryAction); + + // first set the scheduledAction so we can set the other information + expiryAction.setSchedule(schedule); + expiryAction.setScheduleStart(expiryDate); + expiryAction.setScheduleIntervalCount(2); + expiryAction.setScheduleIntervalPeriod(IntervalPeriod.Minute); + + try + { + TenantUtil.runAsDefaultTenant((TenantRunAsWork) () -> { + quickShareLinkExpiryActionPersister.saveQuickShareLinkExpiryAction(expiryAction); + scheduledPersistedActionService.saveSchedule(schedule); + + return null; + }); + + } + catch (Exception ex) + { + throw new QuickShareLinkExpiryActionException("Couldn't create quick share link expiry action.", ex); + } + + if (logger.isDebugEnabled()) + { + logger.debug("Quick share link expiry action is created for sharedId[" + sharedId + "] and it's scheduled to be executed on: " + + expiryDate); + } + } + + @Override + public void deleteQuickShareLinkExpiryAction(QuickShareLinkExpiryAction linkExpiryAction) + { + ParameterCheck.mandatory("linkExpiryAction", linkExpiryAction); + + NodeRef nodeRef = null; + try + { + Pair pair = getTenantNodeRefFromSharedId(linkExpiryAction.getSharedId()); + nodeRef =pair.getSecond(); + } + catch (InvalidSharedIdException ex) + { + // do nothing, as the node might be already unshared + } + final NodeRef sharedNodeRef = nodeRef; + + AuthenticationUtil.runAsSystem(() -> { + // Delete the expiry action and its related persisted schedule + deleteQuickShareLinkExpiryActionImpl(linkExpiryAction); + + // As the method is called directly (ie. not via unshareContent method which removes the aspect properties), + // then we have to remove the 'expiryDate' property as well. + if (sharedNodeRef != null && nodeService.getProperty(sharedNodeRef, QuickShareModel.PROP_QSHARE_EXPIRY_DATE) != null) + { + behaviourFilter.disableBehaviour(sharedNodeRef, ContentModel.ASPECT_AUDITABLE); + try + { + nodeService.removeProperty(sharedNodeRef, QuickShareModel.PROP_QSHARE_EXPIRY_DATE); + } + finally + { + behaviourFilter.enableBehaviour(sharedNodeRef, ContentModel.ASPECT_AUDITABLE); + } + } + return null; + }); + } + + /** + * Removes (hard deletes) the previously persisted {@link QuickShareLinkExpiryAction} and its related + * schedule {@link ScheduledPersistedAction} from the repository. + */ + protected void deleteQuickShareLinkExpiryAction(NodeRef linkExpiryActionNodeRef) + { + AuthenticationUtil.runAsSystem(() -> { + QuickShareLinkExpiryAction linkExpiryAction = quickShareLinkExpiryActionPersister.loadQuickShareLinkExpiryAction(linkExpiryActionNodeRef); + // Delete the expiry action and its related persisted schedule + deleteQuickShareLinkExpiryActionImpl(linkExpiryAction); + return null; + }); + } + + private void deleteQuickShareLinkExpiryActionImpl(QuickShareLinkExpiryAction linkExpiryAction) + { + // Attach the schedule if null. This could be the case when the Action is + // loaded from the quickShareLinkExpiryActionPersister + attachSchedule(linkExpiryAction); + if (linkExpiryAction.getSchedule() != null) + { + scheduledPersistedActionService.deleteSchedule(linkExpiryAction.getSchedule()); + } + quickShareLinkExpiryActionPersister.deleteQuickShareLinkExpiryAction(linkExpiryAction); + } + + private QuickShareLinkExpiryAction attachSchedule(QuickShareLinkExpiryAction quickShareLinkExpiryAction) + { + if (quickShareLinkExpiryAction.getSchedule() == null) + { + ScheduledPersistedAction schedule = scheduledPersistedActionService.getSchedule(quickShareLinkExpiryAction); + quickShareLinkExpiryAction.setSchedule(schedule); + + } + return quickShareLinkExpiryAction; + } + + private NodeRef getQuickShareLinkExpiryActionNode(String sharedId) + { + final QName expiryActionQName = QuickShareLinkExpiryActionImpl.createQName(sharedId); + return TenantUtil.runAsDefaultTenant(() -> quickShareLinkExpiryActionPersister.getQuickShareLinkExpiryActionNode(expiryActionQName)); + } + + private void checkExpiryDate(Date expiryDate) + { + DateTime now = DateTime.now(); + if (now.isAfter(expiryDate.getTime())) + { + throw new QuickShareLinkExpiryActionException.InvalidExpiryDateException("Invalid expiry date. Expiry date can't be in the past."); + } + if (expiryDatePeriod.getDuration(now, new DateTime(expiryDate)) < 1) + { + throw new QuickShareLinkExpiryActionException.InvalidExpiryDateException( + "Invalid expiry date. Expiry date can't be less then 1 " + expiryDatePeriod.getMessage() + '.'); + } + + } + + /** + * A helper enum to get the number of days/hours/minutes between two dates. + * + * @author Jamal Kaabi-Mofrad + */ + private enum ExpiryDatePeriod + { + DAYS + { + @Override + int getDuration(DateTime now, DateTime expiryDate) + { + Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate); + return interval.toPeriod(PeriodType.days()).getDays(); + } + + @Override + String getMessage() + { + return "day (24 hours)"; + } + }, + HOURS + { + @Override + int getDuration(DateTime now, DateTime expiryDate) + { + Interval interval = new Interval(now.withSecondOfMinute(0).withMillisOfSecond(0), expiryDate); + return interval.toPeriod(PeriodType.hours()).getHours(); + } + + @Override + String getMessage() + { + return "hour"; + } + }, + MINUTES + { + @Override + public int getDuration(DateTime now, DateTime expiryDate) + { + Interval interval = new Interval(now.withMillisOfSecond(0), expiryDate); + return interval.toPeriod(PeriodType.minutes()).getMinutes(); + } + + @Override + String getMessage() + { + return "minute"; + } + }; + + abstract int getDuration(DateTime now, DateTime expiryDate); + + abstract String getMessage(); + } } diff --git a/source/java/org/alfresco/service/cmr/action/scheduled/ScheduledPersistedActionService.java b/source/java/org/alfresco/service/cmr/action/scheduled/ScheduledPersistedActionService.java index 0462e768a1..d62ea00229 100644 --- a/source/java/org/alfresco/service/cmr/action/scheduled/ScheduledPersistedActionService.java +++ b/source/java/org/alfresco/service/cmr/action/scheduled/ScheduledPersistedActionService.java @@ -28,6 +28,7 @@ package org.alfresco.service.cmr.action.scheduled; import java.util.List; import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.repository.NodeRef; /** * A service which handles the scheduling of the @@ -64,6 +65,12 @@ public interface ScheduledPersistedActionService * null if it isn't currently scheduled. */ public ScheduledPersistedAction getSchedule(Action persistedAction); + + /** + * Returns the schedule for the specified action nodeRef, or + * null if it isn't currently scheduled. + */ + public ScheduledPersistedAction getSchedule(NodeRef persistedActionNodeRef); /** * Returns all currently scheduled actions. diff --git a/source/java/org/alfresco/service/cmr/quickshare/QuickShareDTO.java b/source/java/org/alfresco/service/cmr/quickshare/QuickShareDTO.java index ce564f4335..08c257a4d6 100644 --- a/source/java/org/alfresco/service/cmr/quickshare/QuickShareDTO.java +++ b/source/java/org/alfresco/service/cmr/quickshare/QuickShareDTO.java @@ -26,6 +26,7 @@ package org.alfresco.service.cmr.quickshare; import java.io.Serializable; +import java.util.Date; /** * Data transfer object for holding quick share information. @@ -38,6 +39,7 @@ public class QuickShareDTO implements Serializable private static final long serialVersionUID = -2163618127531335360L; private String sharedId; + private Date expiresAt; /** * Default constructor @@ -45,8 +47,14 @@ public class QuickShareDTO implements Serializable * @param sharedId The quick share id */ public QuickShareDTO(String sharedId) + { + this(sharedId, null); + } + + public QuickShareDTO(String sharedId, Date expiresAt) { this.sharedId = sharedId; + this.expiresAt = expiresAt; } /** @@ -54,7 +62,7 @@ public class QuickShareDTO implements Serializable */ public QuickShareDTO(QuickShareDTO from) { - this(from.getId()); + this(from.getId(), from.getExpiresAt()); } /** @@ -64,4 +72,9 @@ public class QuickShareDTO implements Serializable { return this.sharedId; } + + public Date getExpiresAt() + { + return expiresAt; + } } diff --git a/source/java/org/alfresco/service/cmr/quickshare/QuickShareLinkExpiryAction.java b/source/java/org/alfresco/service/cmr/quickshare/QuickShareLinkExpiryAction.java new file mode 100644 index 0000000000..6b1c730ffd --- /dev/null +++ b/source/java/org/alfresco/service/cmr/quickshare/QuickShareLinkExpiryAction.java @@ -0,0 +1,62 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.service.cmr.quickshare; + +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.scheduled.SchedulableAction; +import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedAction; +import org.alfresco.service.namespace.QName; + +import java.io.Serializable; + +/** + * This interface defines the details for a quick share link expiry action. + * + * @author Jamal Kaabi-Mofrad + */ +public interface QuickShareLinkExpiryAction extends Action, SchedulableAction, Serializable +{ + /** + * Gets the quick share sharedId. + */ + String getSharedId(); + + /** + * Gets the qualified name which uniquely identifies this quick share link expiry action. + */ + QName getActionQName(); + + /** + * Gets the schedule ({@link ScheduledPersistedAction} used to get the trigger details. + */ + ScheduledPersistedAction getSchedule(); + + /** + * Sets the schedule ({@link ScheduledPersistedAction} used to set the trigger details. + */ + void setSchedule(ScheduledPersistedAction schedule); +} diff --git a/source/java/org/alfresco/service/cmr/quickshare/QuickShareLinkExpiryActionPersister.java b/source/java/org/alfresco/service/cmr/quickshare/QuickShareLinkExpiryActionPersister.java new file mode 100644 index 0000000000..ef9bccda33 --- /dev/null +++ b/source/java/org/alfresco/service/cmr/quickshare/QuickShareLinkExpiryActionPersister.java @@ -0,0 +1,92 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2016 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Alfresco. If not, see . + * #L% + */ + +package org.alfresco.service.cmr.quickshare; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.namespace.QName; + +/** + * This interface defines the persistence and the retrieval of {@link QuickShareLinkExpiryAction} actions. + * + * @author Jamal Kaabi-Mofrad + */ +public interface QuickShareLinkExpiryActionPersister +{ + /** + * Serializes the {@link QuickShareLinkExpiryAction} and stores it in + * the repository. The {@link QuickShareLinkExpiryAction}s saved in this way maybe + * retrieved using the load() method. + * + * @param linkExpiryAction The {@link QuickShareLinkExpiryAction} to be persisted. + */ + void saveQuickShareLinkExpiryAction(QuickShareLinkExpiryAction linkExpiryAction); + + /** + * Retrieves a {@link QuickShareLinkExpiryAction} that has been stored + * in the repository using the save() method. If no + * {@link QuickShareLinkExpiryAction} exists in the repository with the specified + * QName then this method returns null. + * + * @param linkExpiryActionName The unique identifier used to specify the + * {@link QuickShareLinkExpiryAction} to retrieve. + * @return The NodeRef of the specified {@link QuickShareLinkExpiryAction} or null. + */ + NodeRef getQuickShareLinkExpiryActionNode(QName linkExpiryActionName); + + /** + * Retrieves a {@link QuickShareLinkExpiryAction} that has been stored + * in the repository using the save() method. If no + * {@link QuickShareLinkExpiryAction} exists in the repository with the specified + * QName then this method returns null. + * + * @param linkExpiryActionName The unique identifier used to specify the + * {@link QuickShareLinkExpiryAction} to retrieve. + * @return The specified {@link QuickShareLinkExpiryAction} or null. + */ + QuickShareLinkExpiryAction loadQuickShareLinkExpiryAction(QName linkExpiryActionName); + + /** + * Retrieves a {@link QuickShareLinkExpiryAction} that has been stored + * in the repository using the save() method. If no + * {@link QuickShareLinkExpiryAction} exists in the repository with the specified + * QName then this method returns null. + * + * @param linkExpiryActionNodeRef The nodeRef of the + * {@link QuickShareLinkExpiryAction} to retrieve. + * @return The specified {@link QuickShareLinkExpiryAction} or null. + */ + QuickShareLinkExpiryAction loadQuickShareLinkExpiryAction(NodeRef linkExpiryActionNodeRef); + + /** + * Removes the previously serialized {@link QuickShareLinkExpiryAction} + * from the repository. The {@link QuickShareLinkExpiryAction} will then no longer + * be available using the load methods. + * + * @param linkExpiryAction The {@link QuickShareLinkExpiryAction} to be deleted. + */ + void deleteQuickShareLinkExpiryAction(QuickShareLinkExpiryAction linkExpiryAction); +} diff --git a/source/java/org/alfresco/service/cmr/quickshare/QuickShareService.java b/source/java/org/alfresco/service/cmr/quickshare/QuickShareService.java index df9ee0ae86..22c4fe55cc 100644 --- a/source/java/org/alfresco/service/cmr/quickshare/QuickShareService.java +++ b/source/java/org/alfresco/service/cmr/quickshare/QuickShareService.java @@ -25,6 +25,7 @@ */ package org.alfresco.service.cmr.quickshare; +import java.util.Date; import java.util.Map; import org.alfresco.repo.quickshare.QuickShareServiceImpl.QuickShareEmailRequest; @@ -50,6 +51,15 @@ public interface QuickShareService */ public QuickShareDTO shareContent(NodeRef nodeRef) throws QuickShareDisabledException, InvalidNodeRefException; + /** + * Share content identified by nodeRef and optionally set an expiry date for the shared link. + * + * @param nodeRef The NodeRef of the content to share + * @param expiryDate The expiry date of the shared link + * @return QuickDTO with details of the share + */ + QuickShareDTO shareContent(NodeRef nodeRef, Date expiryDate) throws QuickShareDisabledException, InvalidNodeRefException; + /** * Get QuickShare related metadata for the given node. * @@ -105,4 +115,12 @@ public interface QuickShareService * @since 5.2 */ boolean isQuickShareEnabled(); + + /** + * Removes (hard deletes) the previously persisted {@link QuickShareLinkExpiryAction} and its related + * schedule {@link org.alfresco.service.cmr.action.scheduled.ScheduledPersistedAction} from the repository. + * + * @param quickShareLinkExpiryAction The {@link QuickShareLinkExpiryAction} to be deleted. + */ + void deleteQuickShareLinkExpiryAction(QuickShareLinkExpiryAction quickShareLinkExpiryAction); } diff --git a/source/test-java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java b/source/test-java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java index 3c26848185..61463b0016 100644 --- a/source/test-java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java +++ b/source/test-java/org/alfresco/repo/quickshare/QuickShareServiceIntegrationTest.java @@ -34,7 +34,9 @@ import static org.junit.Assert.fail; import java.io.ByteArrayInputStream; import java.io.Serializable; +import java.util.Date; import java.util.Map; +import java.util.Properties; import org.alfresco.model.ContentModel; import org.alfresco.model.QuickShareModel; @@ -47,10 +49,14 @@ import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.repo.tenant.TenantUtil; import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork; +import org.alfresco.repo.transaction.RetryingTransactionHelper; +import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedAction; +import org.alfresco.service.cmr.action.scheduled.ScheduledPersistedActionService; import org.alfresco.service.cmr.attributes.AttributeService; -import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.quickshare.InvalidSharedIdException; import org.alfresco.service.cmr.quickshare.QuickShareDTO; +import org.alfresco.service.cmr.quickshare.QuickShareLinkExpiryAction; +import org.alfresco.service.cmr.quickshare.QuickShareLinkExpiryActionPersister; import org.alfresco.service.cmr.quickshare.QuickShareService; import org.alfresco.service.cmr.repository.CopyService; import org.alfresco.service.cmr.repository.InvalidNodeRefException; @@ -64,6 +70,7 @@ import org.alfresco.util.test.junitrules.ApplicationContextInit; import org.alfresco.util.test.junitrules.TemporaryModels; import org.alfresco.util.test.junitrules.TemporaryNodes; import org.apache.commons.codec.binary.Base64; +import org.joda.time.DateTime; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; @@ -118,11 +125,15 @@ public class QuickShareServiceIntegrationTest private static CopyService copyService; private static NodeService nodeService; private static QuickShareService quickShareService; - private static DictionaryService dictionaryService; + private static QuickShareService directDuickShareService; private static Repository repository; private static AttributeService attributeService; private static PermissionService permissionService; private static NodeArchiveService nodeArchiveService; + private static ScheduledPersistedActionService scheduledPersistedActionService; + private static QuickShareLinkExpiryActionPersister quickShareLinkExpiryActionPersister; + private static RetryingTransactionHelper transactionHelper; + private static Properties globalProperties; private static AlfrescoPerson user1 = new AlfrescoPerson(testContext, "UserOne"); private static AlfrescoPerson user2 = new AlfrescoPerson(testContext, "UserTwo"); @@ -151,13 +162,17 @@ public class QuickShareServiceIntegrationTest ApplicationContext ctx = testContext.getApplicationContext(); copyService = ctx.getBean("CopyService", CopyService.class); - dictionaryService = ctx.getBean("dictionaryService", DictionaryService.class); nodeService = ctx.getBean("NodeService", NodeService.class); + directDuickShareService = ctx.getBean("quickShareService", QuickShareService.class); quickShareService = ctx.getBean("QuickShareService", QuickShareService.class); repository = ctx.getBean("repositoryHelper", Repository.class); attributeService = ctx.getBean("AttributeService", AttributeService.class); permissionService = ctx.getBean("PermissionService", PermissionService.class); nodeArchiveService = ctx.getBean("nodeArchiveService", NodeArchiveService.class); + scheduledPersistedActionService = ctx.getBean("scheduledPersistedActionService", ScheduledPersistedActionService.class); + quickShareLinkExpiryActionPersister = ctx.getBean("quickShareLinkExpiryActionPersister", QuickShareLinkExpiryActionPersister.class); + transactionHelper = ctx.getBean("retryingTransactionHelper", RetryingTransactionHelper.class); + globalProperties = ctx.getBean("global-properties", Properties.class); } @Before public void createTestData() @@ -219,7 +234,7 @@ public class QuickShareServiceIntegrationTest } @Test public void unshare() { - final QuickShareDTO dto = share(testNode, user1.getUsername()); + final QuickShareDTO dto = share(testNode, user1.getUsername()); unshare(dto.getId(), user1.getUsername()); AuthenticationUtil.runAsSystem(new RunAsWork(){ @@ -359,18 +374,16 @@ public class QuickShareServiceIntegrationTest }, userName); } - private QuickShareDTO share(final NodeRef nodeRef, String username) + private QuickShareDTO share(final NodeRef nodeRef, final String username) { - return AuthenticationUtil.runAs(new RunAsWork() - { - @Override - public QuickShareDTO doWork() throws Exception - { - return quickShareService.shareContent(nodeRef); - } - }, username); + return share(nodeRef, username, null); } - + + private QuickShareDTO share(final NodeRef nodeRef, final String username, final Date expiryDate) + { + return AuthenticationUtil.runAs(() -> quickShareService.shareContent(nodeRef, expiryDate), username); + } + @Test public void getMetadataFromShareId() { QuickShareDTO dto = share(testNode, user1.getUsername()); @@ -587,4 +600,317 @@ public class QuickShareServiceIntegrationTest assertEquals("The modifier has changed after sharing.", user1.getUsername(), modifier); } + /** + * Test the quick share link expiry date action. + */ + @Test + public void testSharedLinkExpiryScheduling() throws Exception + { + // First record the number of available schedules + final int numOfSchedules = listSchedules(); + + // 1 day from now + Date expiryDate = DateTime.now().plusDays(1).toDate(); + QuickShareDTO quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + assertEquals(quickShareDTO.getId(), getProperty(testNode, QuickShareModel.PROP_QSHARE_SHAREDID)); + assertNotNull(quickShareDTO.getExpiresAt()); + assertEquals(expiryDate, quickShareDTO.getExpiresAt()); + // Check that the expiry action is persisted + QuickShareLinkExpiryAction expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(quickShareDTO.getId(), expiryAction.getSharedId()); + assertEquals(quickShareDTO.getExpiresAt(), expiryAction.getScheduleStart()); + // assertNull("We haven't set interval count.", expiryAction.getScheduleIntervalCount()); + //assertNull("We haven't set interval period.", expiryAction.getScheduleIntervalPeriod()); + + // Try to share the already shared node with a different expiry date. + // This basically will update the expiry action start time + expiryDate = DateTime.now().plusDays(7).toDate(); + quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertEquals(expiryDate, quickShareDTO.getExpiresAt()); + assertEquals(expiryDate, getProperty(testNode, QuickShareModel.PROP_QSHARE_EXPIRY_DATE)); + assertTrue(hasQuickShareAspect(testNode)); + assertEquals(quickShareDTO.getId(), getProperty(testNode, QuickShareModel.PROP_QSHARE_SHAREDID)); + // Check that the expiry action is persisted + expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(quickShareDTO.getId(), expiryAction.getSharedId()); + assertEquals(quickShareDTO.getExpiresAt(), expiryAction.getScheduleStart()); + //assertNull("We haven't set interval count.", expiryAction.getScheduleIntervalCount()); + // assertNull("We haven't set interval period.", expiryAction.getScheduleIntervalPeriod()); + + // Delete the expiry action + deleteExpiryAction(expiryAction); + + // Check that the expiry action has been deleted + QuickShareLinkExpiryAction deletedExpiryAction = getExpiryAction(quickShareDTO.getId()); + assertNull(deletedExpiryAction); + assertNull(getProperty(testNode, QuickShareModel.PROP_QSHARE_EXPIRY_DATE)); + // Unshare + unshare(quickShareDTO.getId(), user1.getUsername()); + + // Share the testNode, with expiry date of 1 day from now + expiryDate = DateTime.now().plusDays(1).toDate(); + quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(expiryDate, expiryAction.getScheduleStart()); + assertEquals(numOfSchedules + 1, listSchedules()); + // Now update the schedule to be executed in 5 seconds. + expiryAction.setScheduleStart(DateTime.now().plusSeconds(5).toDate()); + // Here we'll bypass the QuickShareService in order to force the new time. + // As the QuickShareService by default will enforce the expiry date to not be less than 24 hours. + forceSaveNewExpiryTime(expiryAction); + + // wait 10 seconds + Thread.sleep(10000L); + // Check that the expiry action was successful and it removed the shared link + assertFalse(hasQuickShareAspect(testNode)); + // Also check the expiry date property is removed + assertNull(getProperty(testNode, QuickShareModel.PROP_QSHARE_EXPIRY_DATE)); + // Check that the persisted expiry action is removed + assertNull(getExpiryAction(quickShareDTO.getId())); + // Check that the persisted schedule is removed as well + assertEquals(numOfSchedules, listSchedules()); + + // Share the testNode, with expiry date of 1 day from now + expiryDate = DateTime.now().plusDays(1).toDate(); + quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(expiryDate, expiryAction.getScheduleStart()); + + // Delete the shared testNode as user1 + AuthenticationUtil.runAs(() -> { + nodeService.deleteNode(testNode); + + return null; + }, user1.getUsername()); + + // Check that the persisted expiry action is removed, as we have deleted the source node + assertNull(getExpiryAction(quickShareDTO.getId())); + // Check that the persisted schedule is removed as well + assertEquals(numOfSchedules, listSchedules()); + + // Restore the testNode as user1 + AuthenticationUtil.runAs(() -> { + final NodeRef archivedNode = nodeArchiveService.getArchivedNode(testNode); + RestoreNodeReport restoreNodeReport = nodeArchiveService.restoreArchivedNode(archivedNode); + assertNotNull(restoreNodeReport); + assertTrue(restoreNodeReport.getStatus() == RestoreStatus.SUCCESS); + testNode = restoreNodeReport.getRestoredNodeRef(); + + return null; + }, user1.getUsername()); + + // Check that restoring the node hasn't brought back the shared aspect or the persisted expiry action + assertFalse(hasQuickShareAspect(testNode)); + assertNull(getExpiryAction(quickShareDTO.getId())); + assertEquals(numOfSchedules, listSchedules()); + } + + /** + * Test date validator for the quick share link expiry date action. + */ + @Test + public void testSharedLinkExpiryDateValidator() throws Exception + { + // Try to share with invalid time - passed time + try + { + share(testNode, user1.getUsername(), DateTime.now().minusDays(1).toDate()); + fail("Should have failed as the expiry date is invalid (passed time)."); + } + catch (QuickShareLinkExpiryActionException.InvalidExpiryDateException ex) + { + // Expected + } + + final String defaultExpiryDatePeriod = globalProperties.getProperty("system.quickshare.expiry_date.enforce.period"); + + // Test expiry date period enforcement + try + { + /* + * Set the expiry date period enforcement to Days + */ + { + ((QuickShareServiceImpl) directDuickShareService).setExpiryDatePeriod("DAYS"); + + try + { + // Try to share with invalid time - less than 1 day + share(testNode, user1.getUsername(), DateTime.now().plusHours(1).toDate()); + fail("Should have failed as the expiry date is invalid (less than 1 day)."); + } + catch (QuickShareLinkExpiryActionException.InvalidExpiryDateException ex) + { + // Expected + } + try + { + // Try to share with invalid time - less than 1 day + share(testNode, user1.getUsername(), DateTime.now().plusMinutes(30).toDate()); + fail("Should have failed as the expiry date is invalid (less than 1 day)."); + } + catch (QuickShareLinkExpiryActionException.InvalidExpiryDateException ex) + { + // Expected + } + // Set the expiry date to be in 24 hours + Date expiryDate = DateTime.now().plusHours(24).toDate(); + QuickShareDTO quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + QuickShareLinkExpiryAction expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(expiryDate, expiryAction.getScheduleStart()); + // Unshare + quickShareService.unshareContent(quickShareDTO.getId()); + + // Set the expiry date to be next year + expiryDate = DateTime.now().plusYears(1).toDate(); + quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(expiryDate, expiryAction.getScheduleStart()); + // Unshare + quickShareService.unshareContent(quickShareDTO.getId()); + } + /* + * Set the expiry date period enforcement to Hours + */ + { + ((QuickShareServiceImpl) directDuickShareService).setExpiryDatePeriod("HOURS"); + + try + { + // Try to share with invalid time - less than 1 hour + share(testNode, user1.getUsername(), DateTime.now().plusMinutes(30).toDate()); + fail("Should have failed as the expiry date is invalid (less than 1 hour)."); + } + catch (QuickShareLinkExpiryActionException.InvalidExpiryDateException ex) + { + // Expected + } + // Set the expiry date to be in the next hour + Date expiryDate = DateTime.now().plusHours(1).toDate(); + QuickShareDTO quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + QuickShareLinkExpiryAction expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(expiryDate, expiryAction.getScheduleStart()); + // Unshare + quickShareService.unshareContent(quickShareDTO.getId()); + + // Set the expiry date to be in the next 2 days, even though we did set the date period to HOURS. + expiryDate = DateTime.now().plusDays(2).toDate(); + quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(expiryDate, expiryAction.getScheduleStart()); + // Unshare + quickShareService.unshareContent(quickShareDTO.getId()); + } + /* + * Set the expiry date period enforcement to Minutes + */ + { + ((QuickShareServiceImpl) directDuickShareService).setExpiryDatePeriod("MINUTES"); + + try + { + // Try to share with invalid time - less than 1 minute + share(testNode, user1.getUsername(), DateTime.now().plusSeconds(10).toDate()); + fail("Should have failed as the expiry date is invalid (less than 1 minute)."); + } + catch (QuickShareLinkExpiryActionException.InvalidExpiryDateException ex) + { + // Expected + } + // Set the expiry date to be in 5 minutes time + Date expiryDate = DateTime.now().plusMinutes(5).toDate(); + QuickShareDTO quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + QuickShareLinkExpiryAction expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(expiryDate, expiryAction.getScheduleStart()); + // Unshare + quickShareService.unshareContent(quickShareDTO.getId()); + + // Set the expiry date to be in 60 days + expiryDate = DateTime.now().plusDays(60).toDate(); + quickShareDTO = share(testNode, user1.getUsername(), expiryDate); + assertTrue(hasQuickShareAspect(testNode)); + expiryAction = getExpiryActionAndAttachSchedule(quickShareDTO.getId()); + assertEquals(expiryDate, expiryAction.getScheduleStart()); + // Unshare + quickShareService.unshareContent(quickShareDTO.getId()); + } + } + finally + { + ((QuickShareServiceImpl) directDuickShareService).setExpiryDatePeriod(defaultExpiryDatePeriod); + } + } + + private QuickShareLinkExpiryAction getExpiryActionAndAttachSchedule(String sharedId) + { + + // Check that the expiry action is persisted + QuickShareLinkExpiryAction expiryAction = getExpiryAction(sharedId); + assertNotNull(expiryAction); + assertNotNull("Expiry action should have been persisted.", expiryAction.getNodeRef()); + assertNull("The schedule hasn't been attached yet.", expiryAction.getSchedule()); + ScheduledPersistedAction scheduledPersistedAction = getSchedule(expiryAction); + assertNotNull("Scheduled action should have been persisted.", scheduledPersistedAction); + //Attach the schedule + expiryAction.setSchedule(scheduledPersistedAction); + + return expiryAction; + + } + + private QuickShareLinkExpiryAction getExpiryAction(final String sharedId) + { + return AuthenticationUtil.runAsSystem( + () -> quickShareLinkExpiryActionPersister.loadQuickShareLinkExpiryAction(QuickShareLinkExpiryActionImpl.createQName(sharedId))); + } + + private ScheduledPersistedAction getSchedule(final QuickShareLinkExpiryAction linkExpiryAction) + { + return AuthenticationUtil.runAsSystem( + () -> scheduledPersistedActionService.getSchedule(linkExpiryAction)); + } + + private int listSchedules() + { + return AuthenticationUtil.runAsSystem(() -> scheduledPersistedActionService.listSchedules().size()); + + } + + private void deleteExpiryAction(final QuickShareLinkExpiryAction linkExpiryAction) + { + transactionHelper.doInTransaction(() -> { + quickShareService.deleteQuickShareLinkExpiryAction(linkExpiryAction); + return null; + }); + } + + private boolean hasQuickShareAspect(NodeRef nodeRef) + { + return AuthenticationUtil.runAsSystem(() -> nodeService.hasAspect(nodeRef, QuickShareModel.ASPECT_QSHARE)); + } + + private Serializable getProperty(NodeRef nodeRef, QName property) + { + return AuthenticationUtil.runAsSystem(() -> nodeService.getProperty(nodeRef, property)); + } + + private void forceSaveNewExpiryTime(final QuickShareLinkExpiryAction linkExpiryAction) + { + transactionHelper.doInTransaction(() -> { + AuthenticationUtil.runAsSystem(() -> { + quickShareLinkExpiryActionPersister.saveQuickShareLinkExpiryAction(linkExpiryAction); + scheduledPersistedActionService.saveSchedule(linkExpiryAction.getSchedule()); + return null; + }); + return null; + }); + } }