ACS-6917 Implement the Legal Holds v1 API (#2566)

---------

Co-authored-by: Tom Page <tpage-alfresco@users.noreply.github.com>
Co-authored-by: Domenico Sibilio <domenicosibilio@gmail.com>
This commit is contained in:
Damian Ujma
2024-04-04 11:46:56 +02:00
committed by GitHub
parent e8e747347a
commit 53d77f8d71
45 changed files with 3731 additions and 114 deletions

View File

@@ -27,6 +27,7 @@
package org.alfresco.module.org_alfresco_module_rm.audit.event;
import static org.alfresco.module.org_alfresco_module_rm.audit.event.HoldUtils.HOLD_DELETION_REASON;
import static org.alfresco.repo.policy.Behaviour.NotificationFrequency.EVERY_EVENT;
import java.io.Serializable;
@@ -77,6 +78,8 @@ public class DeleteHoldAuditEvent extends AuditEvent implements NodeServicePolic
public void beforeDeleteNode(NodeRef holdNodeRef)
{
Map<QName, Serializable> auditProperties = HoldUtils.makePropertiesMap(holdNodeRef, nodeService);
auditProperties.put(HOLD_DELETION_REASON, nodeService.getProperty(holdNodeRef, PROP_HOLD_DELETION_REASON));
recordsManagementAuditService.auditEvent(holdNodeRef, getName(), auditProperties, null, true, false);
}
}

View File

@@ -47,6 +47,7 @@ class HoldUtils
{
/** A QName to display for the hold name. */
public static final QName HOLD_NAME = QName.createQName(RecordsManagementModel.RM_URI, "Hold Name");
public static final QName HOLD_DELETION_REASON = QName.createQName(RecordsManagementModel.RM_URI, "Hold deletion reason");
/** A QName to display for the hold node ref. */
public static final QName HOLD_NODEREF = QName.createQName(RecordsManagementModel.RM_URI, "Hold NodeRef");

View File

@@ -111,6 +111,24 @@ public interface HoldService
*/
void setHoldReason(NodeRef hold, String reason);
/**
* Sets the reason for the hold deletion
*
* @param hold The {@link NodeRef} of the hold
* @param reason {@link String} The reason for the hold
*/
void setHoldDeletionReason(NodeRef hold, String reason);
/**
* Updates a hold with the given name, reason and description
*
* @param hold The {@link NodeRef} of the hold
* @param name {@link String} The name of the hold
* @param reason {@link String} The reason of the hold
* @param description {@link String} The description of the hold
*/
void updateHold(NodeRef hold, String name, String reason, String description);
/**
* Deletes the hold
*

View File

@@ -29,6 +29,7 @@ package org.alfresco.module.org_alfresco_module_rm.hold;
import static org.alfresco.model.ContentModel.ASPECT_LOCKABLE;
import static org.alfresco.model.ContentModel.ASSOC_CONTAINS;
import static org.alfresco.model.ContentModel.PROP_DESCRIPTION;
import static org.alfresco.model.ContentModel.PROP_NAME;
import java.io.Serializable;
@@ -458,11 +459,11 @@ public class HoldServiceImpl extends ServiceBaseImpl
// create map of properties
Map<QName, Serializable> properties = new HashMap<>(3);
properties.put(ContentModel.PROP_NAME, name);
properties.put(PROP_NAME, name);
properties.put(PROP_HOLD_REASON, reason);
if (description != null && !description.isEmpty())
{
properties.put(ContentModel.PROP_DESCRIPTION, description);
properties.put(PROP_DESCRIPTION, description);
}
// create assoc name
@@ -512,6 +513,39 @@ public class HoldServiceImpl extends ServiceBaseImpl
}
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.hold.HoldService#setHoldDeletionReason(org.alfresco.service.cmr.repository.NodeRef, java.lang.String)
*/
@Override
public void setHoldDeletionReason(NodeRef hold, String reason)
{
ParameterCheck.mandatory("hold", hold);
ParameterCheck.mandatory("reason", reason);
if (nodeService.exists(hold) && isHold(hold))
{
nodeService.setProperty(hold, PROP_HOLD_DELETION_REASON, reason);
}
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.hold.HoldService#updateHold(org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, java.lang.String) (org.alfresco.service.cmr.repository.NodeRef, java.lang.String, java.lang.String, java.lang.String)
*/
@Override
public void updateHold(NodeRef hold, String name, String reason, String description)
{
ParameterCheck.mandatory("hold", hold);
ParameterCheck.mandatory("name", name);
ParameterCheck.mandatory("reason", reason);
if (nodeService.exists(hold) && isHold(hold))
{
nodeService.setProperty(hold, PROP_NAME, name);
nodeService.setProperty(hold, PROP_HOLD_REASON, reason);
nodeService.setProperty(hold, PROP_DESCRIPTION, description);
}
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.hold.HoldService#deleteHold(org.alfresco.service.cmr.repository.NodeRef)
*/
@@ -563,7 +597,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
if (permissionService.hasPermission(nodeRef, permission) == AccessStatus.DENIED)
{
heldNames.add((String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME));
heldNames.add((String) nodeService.getProperty(nodeRef, PROP_NAME));
}
}
catch (AccessDeniedException ade)
@@ -630,7 +664,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
{
if (!isHold(hold))
{
final String holdName = (String) nodeService.getProperty(hold, ContentModel.PROP_NAME);
final String holdName = (String) nodeService.getProperty(hold, PROP_NAME);
throw new IntegrityException(I18NUtil.getMessage("rm.hold.not-hold", holdName), null);
}
@@ -688,7 +722,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
{
if (!isRecordFolder(nodeRef) && !instanceOf(nodeRef, ContentModel.TYPE_CONTENT))
{
final String nodeName = (String) nodeService.getProperty(nodeRef, ContentModel.PROP_NAME);
final String nodeName = (String) nodeService.getProperty(nodeRef, PROP_NAME);
throw new IntegrityException(I18NUtil.getMessage("rm.hold.add-to-hold-invalid-type", nodeName), null);
}
@@ -795,7 +829,7 @@ public class HoldServiceImpl extends ServiceBaseImpl
{
if (!isHold(hold))
{
final String holdName = (String) nodeService.getProperty(hold, ContentModel.PROP_NAME);
final String holdName = (String) nodeService.getProperty(hold, PROP_NAME);
throw new IntegrityException(I18NUtil.getMessage("rm.hold.not-hold", holdName), null);
}

View File

@@ -35,6 +35,7 @@ import org.alfresco.service.namespace.QName;
*
* @author Roy Wetherall
*/
@SuppressWarnings("PMD.ConstantsInInterface")
@AlfrescoPublicApi
public interface RecordsManagementModel extends RecordsManagementCustomModel
{
@@ -200,6 +201,7 @@ public interface RecordsManagementModel extends RecordsManagementCustomModel
// Hold type
QName TYPE_HOLD = QName.createQName(RM_URI, "hold");
QName PROP_HOLD_REASON = QName.createQName(RM_URI, "holdReason");
QName PROP_HOLD_DELETION_REASON = QName.createQName(RM_URI, "holdDeletionReason");
//since 3.2
@Deprecated
QName ASSOC_FROZEN_RECORDS = QName.createQName(RM_URI, "frozenRecords");

View File

@@ -0,0 +1,155 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.fileplans;
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
import static org.alfresco.util.ParameterCheck.mandatory;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
import org.alfresco.rm.rest.api.model.HoldModel;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.transaction.TransactionService;
import org.springframework.beans.factory.InitializingBean;
/**
* File plan holds relation
*
* @author Damian Ujma
*/
@RelationshipResource(name = "holds", entityResource = FilePlanEntityResource.class, title = "Holds in a file plan")
public class FilePlanHoldsRelation implements
RelationshipResourceAction.Create<HoldModel>,
RelationshipResourceAction.Read<HoldModel>,
InitializingBean
{
private FilePlanComponentsApiUtils apiUtils;
private ApiNodesModelFactory nodesModelFactory;
private HoldService holdService;
private FileFolderService fileFolderService;
private TransactionService transactionService;
@Override
public void afterPropertiesSet() throws Exception
{
mandatory("apiUtils", this.apiUtils);
mandatory("nodesModelFactory", this.nodesModelFactory);
mandatory("holdService", this.holdService);
mandatory("fileFolderService", this.fileFolderService);
mandatory("transactionService", this.transactionService);
}
@Override
@WebApiDescription(title = "Return a paged list of holds for the file plan identified by 'filePlanId'")
public CollectionWithPagingInfo<HoldModel> readAll(String filePlanId, Parameters parameters)
{
checkNotBlank("filePlanId", filePlanId);
mandatory("parameters", parameters);
NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, RecordsManagementModel.TYPE_FILE_PLAN);
List<NodeRef> holds = holdService.getHolds(parentNodeRef);
List<HoldModel> page = holds.stream()
.map(hold -> fileFolderService.getFileInfo(hold))
.map(nodesModelFactory::createHoldModel)
.skip(parameters.getPaging().getSkipCount())
.limit(parameters.getPaging().getMaxItems())
.collect(Collectors.toCollection(LinkedList::new));
int totalItems = holds.size();
boolean hasMore = parameters.getPaging().getSkipCount() + parameters.getPaging().getMaxItems() < totalItems;
return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, hasMore, totalItems);
}
@Override
@WebApiDescription(title = "Create one (or more) holds in a file plan identified by 'filePlanId'")
public List<HoldModel> create(String filePlanId, List<HoldModel> holds, Parameters parameters)
{
checkNotBlank("filePlanId", filePlanId);
mandatory("holds", holds);
mandatory("parameters", parameters);
NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(filePlanId, RecordsManagementModel.TYPE_FILE_PLAN);
RetryingTransactionCallback<List<NodeRef>> callback = () -> {
List<NodeRef> createdNodes = new LinkedList<>();
for (HoldModel nodeInfo : holds)
{
NodeRef newNodeRef = holdService.createHold(parentNodeRef, nodeInfo.name(), nodeInfo.reason(),
nodeInfo.description());
createdNodes.add(newNodeRef);
}
return createdNodes;
};
List<NodeRef> createdNodes = transactionService.getRetryingTransactionHelper()
.doInTransaction(callback, false, true);
return createdNodes.stream()
.map(hold -> fileFolderService.getFileInfo(hold))
.map(nodesModelFactory::createHoldModel)
.collect(Collectors.toCollection(LinkedList::new));
}
public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
{
this.apiUtils = apiUtils;
}
public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
{
this.nodesModelFactory = nodesModelFactory;
}
public void setHoldService(HoldService holdService)
{
this.holdService = holdService;
}
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
}

View File

@@ -0,0 +1,207 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.holds;
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
import static org.alfresco.util.ParameterCheck.mandatory;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.node.integrity.IntegrityException;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.core.exceptions.InvalidArgumentException;
import org.alfresco.rest.framework.core.exceptions.PermissionDeniedException;
import org.alfresco.rest.framework.resource.RelationshipResource;
import org.alfresco.rest.framework.resource.actions.interfaces.RelationshipResourceAction;
import org.alfresco.rest.framework.resource.parameters.CollectionWithPagingInfo;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
import org.alfresco.rm.rest.api.model.HoldChild;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.security.AccessStatus;
import org.alfresco.service.cmr.security.PermissionService;
import org.alfresco.service.transaction.TransactionService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.extensions.surf.util.I18NUtil;
/**
* Hold children relation
*
* @author Damian Ujma
*/
@RelationshipResource(name = "children", entityResource = HoldsEntityResource.class, title = "Children of a hold")
public class HoldsChildrenRelation implements
RelationshipResourceAction.Create<HoldChild>,
RelationshipResourceAction.Read<HoldChild>,
RelationshipResourceAction.Delete,
InitializingBean
{
private HoldService holdService;
private FilePlanComponentsApiUtils apiUtils;
private ApiNodesModelFactory nodesModelFactory;
private TransactionService transactionService;
private FileFolderService fileFolderService;
private PermissionService permissionService;
@Override
public void afterPropertiesSet() throws Exception
{
mandatory("holdService", holdService);
mandatory("apiUtils", apiUtils);
mandatory("nodesModelFactory", nodesModelFactory);
mandatory("transactionService", transactionService);
mandatory("fileFolderService", fileFolderService);
}
@Override
@WebApiDescription(title = "Add one (or more) children as children of a hold identified by 'holdId'")
public List<HoldChild> create(String holdId, List<HoldChild> children, Parameters parameters)
{
// validate parameters
checkNotBlank("holdId", holdId);
mandatory("children", children);
mandatory("parameters", parameters);
NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD);
RetryingTransactionCallback<List<NodeRef>> callback = () -> {
List<NodeRef> createdNodes = children.stream()
.map(holdChild -> new NodeRef(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE, holdChild.id()))
.collect(Collectors.toList());
try
{
holdService.addToHold(parentNodeRef, createdNodes);
}
catch (IntegrityException exception)
{
// Throw 400 Bad Request when a node with id 'holdId' is not a hold or a child cannot be added to a hold
throw new InvalidArgumentException(exception.getMsgId()).initCause(exception);
}
return createdNodes;
};
List<NodeRef> nodeInfos = transactionService.getRetryingTransactionHelper()
.doInTransaction(callback, false, true);
return nodeInfos.stream()
.map(nodeRef -> new HoldChild(nodeRef.getId()))
.collect(Collectors.toCollection(LinkedList::new));
}
@Override
@WebApiDescription(title = "Return a paged list of hold children for the hold identified by 'holdId'")
public CollectionWithPagingInfo<HoldChild> readAll(String holdId, Parameters parameters)
{
checkNotBlank("holdId", holdId);
mandatory("parameters", parameters);
NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD);
List<NodeRef> children = holdService.getHeld(parentNodeRef);
List<HoldChild> page = children.stream()
.map(NodeRef::getId)
.map(HoldChild::new)
.skip(parameters.getPaging().getSkipCount())
.limit(parameters.getPaging().getMaxItems())
.collect(Collectors.toCollection(LinkedList::new));
int totalItems = children.size();
boolean hasMore = parameters.getPaging().getSkipCount() + parameters.getPaging().getMaxItems() < totalItems;
return CollectionWithPagingInfo.asPaged(parameters.getPaging(), page, hasMore, totalItems);
}
@Override
@WebApiDescription(title = "Remove a child from a hold", description = "Remove a child with id 'childId' from a hold with id 'holdId'")
public void delete(String holdId, String childId, Parameters parameters)
{
checkNotBlank("holdId", holdId);
checkNotBlank("childId", childId);
mandatory("parameters", parameters);
NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD);
NodeRef childRef = apiUtils.lookupByPlaceholder(childId);
if (permissionService.hasReadPermission(childRef) == AccessStatus.DENIED)
{
throw new PermissionDeniedException(I18NUtil.getMessage("permissions.err_access_denied"));
}
RetryingTransactionCallback<List<NodeRef>> callback = () -> {
try
{
holdService.removeFromHold(nodeRef, childRef);
}
catch (IntegrityException exception)
{
// Throw 400 Bad Request when a node with id 'holdId' is not a hold
throw new InvalidArgumentException(exception.getMsgId()).initCause(exception);
}
return null;
};
transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true);
}
public void setHoldService(HoldService holdService)
{
this.holdService = holdService;
}
public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
{
this.apiUtils = apiUtils;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
{
this.nodesModelFactory = nodesModelFactory;
}
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
public void setPermissionService(PermissionService permissionService)
{
this.permissionService = permissionService;
}
}

View File

@@ -0,0 +1,184 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.holds;
import static org.alfresco.module.org_alfresco_module_rm.util.RMParameterCheck.checkNotBlank;
import static org.alfresco.util.ParameterCheck.mandatory;
import jakarta.servlet.http.HttpServletResponse;
import org.alfresco.module.org_alfresco_module_rm.hold.HoldService;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback;
import org.alfresco.rest.framework.Operation;
import org.alfresco.rest.framework.WebApiDescription;
import org.alfresco.rest.framework.WebApiParam;
import org.alfresco.rest.framework.resource.EntityResource;
import org.alfresco.rest.framework.resource.actions.interfaces.EntityResourceAction;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rest.framework.webscripts.WithResponse;
import org.alfresco.rm.rest.api.impl.ApiNodesModelFactory;
import org.alfresco.rm.rest.api.impl.FilePlanComponentsApiUtils;
import org.alfresco.rm.rest.api.model.HoldDeletionReason;
import org.alfresco.rm.rest.api.model.HoldModel;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.model.FileInfo;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.transaction.TransactionService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
/**
* Hold entity resource
*
* @author Damian Ujma
*/
@EntityResource(name = "holds", title = "Holds")
public class HoldsEntityResource implements
EntityResourceAction.ReadById<HoldModel>,
EntityResourceAction.Update<HoldModel>,
EntityResourceAction.Delete,
InitializingBean
{
private FilePlanComponentsApiUtils apiUtils;
private FileFolderService fileFolderService;
private ApiNodesModelFactory nodesModelFactory;
private HoldService holdService;
private TransactionService transactionService;
@Override
public void afterPropertiesSet() throws Exception
{
mandatory("nodesModelFactory", nodesModelFactory);
mandatory("apiUtils", apiUtils);
mandatory("fileFolderService", fileFolderService);
mandatory("holdService", holdService);
mandatory("transactionService", transactionService);
}
@Override
@WebApiDescription(title = "Get hold information", description = "Get information for a hold with id 'holdId'")
@WebApiParam(name = "holdId", title = "The hold id")
public HoldModel readById(String holdId, Parameters parameters)
{
checkNotBlank("holdId", holdId);
mandatory("parameters", parameters);
NodeRef hold = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD);
FileInfo info = fileFolderService.getFileInfo(hold);
return nodesModelFactory.createHoldModel(info);
}
@Override
@WebApiDescription(title = "Update a hold", description = "Updates a hold with id 'holdId'")
public HoldModel update(String holdId, HoldModel holdModel, Parameters parameters)
{
checkNotBlank("holdId", holdId);
mandatory("holdModel", holdModel);
mandatory("holdModel.name", holdModel.name());
mandatory("holdModel.reason", holdModel.reason());
mandatory("parameters", parameters);
NodeRef nodeRef = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD);
RetryingTransactionCallback<Void> callback = () -> {
holdService.updateHold(nodeRef, holdModel.name(), holdModel.reason(), holdModel.description());
return null;
};
transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true);
RetryingTransactionCallback<FileInfo> readCallback = () -> fileFolderService.getFileInfo(nodeRef);
FileInfo info = transactionService.getRetryingTransactionHelper().doInTransaction(readCallback, false, true);
return nodesModelFactory.createHoldModel(info);
}
@Override
@WebApiDescription(title = "Delete hold", description = "Deletes a hold with id 'holdId'")
public void delete(String holdId, Parameters parameters)
{
checkNotBlank("holdId", holdId);
mandatory("parameters", parameters);
NodeRef hold = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD);
RetryingTransactionCallback<Void> callback = () -> {
holdService.deleteHold(hold);
return null;
};
transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true);
}
@Operation("delete")
@WebApiDescription(title = "Delete hold with a reason",
successStatus = HttpServletResponse.SC_OK)
public HoldDeletionReason deleteHoldWithReason(String holdId, HoldDeletionReason reason, Parameters parameters,
WithResponse withResponse)
{
checkNotBlank("holdId", holdId);
mandatory("reason", reason);
mandatory("parameters", parameters);
NodeRef hold = apiUtils.lookupAndValidateNodeType(holdId, RecordsManagementModel.TYPE_HOLD);
String deletionReason = reason.reason();
RetryingTransactionCallback<Void> callback = () -> {
if (StringUtils.isNotBlank(deletionReason))
{
holdService.setHoldDeletionReason(hold, deletionReason);
}
holdService.deleteHold(hold);
return null;
};
transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true);
return reason;
}
public void setApiUtils(FilePlanComponentsApiUtils apiUtils)
{
this.apiUtils = apiUtils;
}
public void setFileFolderService(FileFolderService fileFolderService)
{
this.fileFolderService = fileFolderService;
}
public void setNodesModelFactory(ApiNodesModelFactory nodesModelFactory)
{
this.nodesModelFactory = nodesModelFactory;
}
public void setHoldService(HoldService holdService)
{
this.holdService = holdService;
}
public void setTransactionService(TransactionService transactionService)
{
this.transactionService = transactionService;
}
}

View File

@@ -0,0 +1,36 @@
/*
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
/**
* Package info that defines the Information Governance Holds REST API
*
* @author Damian Ujma
*/
@WebApi(name="gs", scope=Api.SCOPE.PUBLIC, version=1)
package org.alfresco.rm.rest.api.holds;
import org.alfresco.rest.framework.Api;
import org.alfresco.rest.framework.WebApi;

View File

@@ -47,6 +47,7 @@ import org.alfresco.rest.api.model.UserInfo;
import org.alfresco.rest.framework.jacksonextensions.BeanPropertiesFilter;
import org.alfresco.rest.framework.resource.parameters.Parameters;
import org.alfresco.rm.rest.api.model.FilePlan;
import org.alfresco.rm.rest.api.model.HoldModel;
import org.alfresco.rm.rest.api.model.RMNode;
import org.alfresco.rm.rest.api.model.Record;
import org.alfresco.rm.rest.api.model.RecordCategory;
@@ -637,6 +638,21 @@ public class ApiNodesModelFactory
}
}
/**
* Creates an object of type HoldModel
*
* @param info info of the hold
* @return HoldModel object
*/
public HoldModel createHoldModel(FileInfo info)
{
return new HoldModel(info.getNodeRef().getId(),
(String) info.getProperties().get(ContentModel.PROP_NAME),
(String) info.getProperties().get(ContentModel.PROP_DESCRIPTION),
(String) info.getProperties().get(RecordsManagementModel.PROP_HOLD_REASON));
}
/**
* Creates an object of type FilePlan
*

View File

@@ -0,0 +1,36 @@
/*-
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Hold Child POJO for use in the v1 REST API.
*
* @author Damian Ujma
*/
public record HoldChild(String id)
{
}

View File

@@ -0,0 +1,36 @@
/*-
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Hold Deletion Reason POJO for use in the v1 REST API.
*
* @author Damian Ujma
*/
public record HoldDeletionReason(String reason)
{
}

View File

@@ -0,0 +1,36 @@
/*-
* #%L
* Alfresco Records Management Module
* %%
* Copyright (C) 2005 - 2024 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 <http://www.gnu.org/licenses/>.
* #L%
*/
package org.alfresco.rm.rest.api.model;
/**
* Hold POJO for use in the v1 REST API.
*
* @author Damian Ujma
*/
public record HoldModel(String id, String name, String description, String reason)
{
}