Merge branch 'master' of git.alfresco.com:records-management/records-management into RM-4431_CompleteRecordActionRestAPI

This commit is contained in:
Sara Aspery
2017-06-01 14:42:02 +01:00
14 changed files with 298 additions and 158 deletions

View File

@@ -1,27 +1,23 @@
Configuring and starting Alfresco/Share: Configuring and starting Alfresco/Share:
---------------------------------------- ----------------------------------------
- Clone the project from git - Clone the project (e.g. git clone git@gitlab.alfresco.com:records-management/records-management.git)
- Import the project as a maven project - Import the project as a maven project
- Start the Alfresco/Share instances with the following commands: - Start the Alfresco/Share instances with the following commands:
To start the repo:
cd rm-community/rm-community-repo
mvn clean install -Pstart-repo mvn clean install -Pstart-repo
To start Share:
cd rm-community/rm-community-share
mvn clean install -Pstart-share mvn clean install -Pstart-share
NOTE: If you have the enterprise code, see rm-enterprise/README.txt for instructions on how to build/start the enterprise code. (these commands work best if run from the specific directories, e.g. start share from
rm-enterprise/rm-enterprise-share/ or rm-community/rm-community-share/ )
Configuring a different DB other than H2 (e.g. MySQL or PostgreSQL): Configuring a different DB other than H2 (e.g. MySQL or PostgreSQL):
-------------------------------------------------------------------- --------------------------------------------------------------------
- Create a file called "local.properties" under src/main/resources in rm-community-repo (you may need to create the directory) - Create a file called "local.properties" under src/main/resources in alfresco-rm-enterprise-repo
- Add the following properties in this new file - Add the following properties in this new file
my.db.name -> The name of the database schema my.db.name -> The name of the database schema
@@ -51,12 +47,12 @@ To run the automated UI tests, change to the rm-automation directory and run:
mvn clean install -Dskip.automationtests=false mvn clean install -Dskip.automationtests=false
Note: due to Selenium Firefox driver changes, the highest supported Firefox version for UI tests is 43.0.4 (with Selenium 2.52.0). Note: due to Selenium Firefox driver changes, the highest supported Firefox version for UI tests is 43.0.4 (with Selenium 2.52.0).
It is possible to have multiple versions of Firefox installed onto your workstation (e.g. one for running the UI tests and the other, kept It is possible to have multiple versions of Firefox installed onto your workstation (e.g. one for running the UI tests and the other, kept
up to date, for everyday browsing) but beware Firefox auto-updates. In this scenario the best approach is to create a non-default profile up to date, for everyday browsing) but beware Firefox auto-updates. In this scenario the best approach is to create a non-default profile
(default profiles will be shared between your Firefox installations!) for which auto-updates are disabled and forcing the use of this (default profiles will be shared between your Firefox installations!) for which auto-updates are disabled and forcing the use of this
profile in your tests (-Dwebdriver.firefox.profile="ProfileName"). If your Firefox 43 install isn't in your path, you can use the profile in your tests (-Dwebdriver.firefox.profile="ProfileName"). If your Firefox 43 install isn't in your path, you can use the
-Dwebdriver.firefox.profile option set to the full path of its "firefox-bin" executable. -Dwebdriver.firefox.profile option set to the full path of its "firefox-bin" executable.
MacOS X Sierra users: if you experience by order of magnitude slower performance when connected to a WiFi network (e.g. office WiFi) MacOS X Sierra users: if you experience by order of magnitude slower performance when connected to a WiFi network (e.g. office WiFi)
@@ -90,9 +86,9 @@ To download and run RM with the Outlook Integration AMPs installed on the repo a
mvn clean install -Pstart-share,outlook-integration mvn clean install -Pstart-share,outlook-integration
Follow these instructions install licence and Outlook plugin: Follow these instructions install licence and Outlook plugin:
- http://docs.alfresco.com/outlook2.1/tasks/Outlook-license.html - http://docs.alfresco.com/outlook2.1/tasks/Outlook-license.html
- http://docs.alfresco.com/outlook2.1/tasks/Outlook-install_v2.html - http://docs.alfresco.com/outlook2.1/tasks/Outlook-install_v2.html
SNAPSHOT dependencies: SNAPSHOT dependencies:
@@ -115,7 +111,7 @@ This project follows the usual Alfresco Coding Standards. If you use Eclipse or
Surf build errors: Surf build errors:
------------------ ------------------
If you get: If you get:
[ERROR] Failed to execute goal on project alfresco-rm-community-share: Could not resolve dependencies for project org.alfresco:alfresco-rm-community-share:amp:2.6-SNAPSHOT: Failed to collect dependencies at org.alfresco.surf:spring-surf-api:jar:6.3 -> org.alfresco.surf:spring-surf:jar:${dependency.surf.version}: Failed to read artifact descriptor for org.alfresco.surf:spring-surf:jar:${dependency.surf.version}: Could not transfer artifact org.alfresco.surf:spring-surf:pom:${dependency.surf.version} from/to alfresco-internal (https://artifacts.alfresco.com/nexus/content/groups/private): Not authorized , ReasonPhrase:Unauthorized. -> [Help 1] [ERROR] Failed to execute goal on project alfresco-rm-community-share: Could not resolve dependencies for project org.alfresco:alfresco-rm-community-share:amp:2.6-SNAPSHOT: Failed to collect dependencies at org.alfresco.surf:spring-surf-api:jar:6.3 -> org.alfresco.surf:spring-surf:jar:${dependency.surf.version}: Failed to read artifact descriptor for org.alfresco.surf:spring-surf:jar:${dependency.surf.version}: Could not transfer artifact org.alfresco.surf:spring-surf:pom:${dependency.surf.version} from/to alfresco-internal (https://artifacts.alfresco.com/nexus/content/groups/private): Not authorized , ReasonPhrase:Unauthorized. -> [Help 1]
then please re-run with -Ddependency.surf.version=6.3 then please re-run with -Ddependency.surf.version=6.3
@@ -127,3 +123,19 @@ Install lombok plugin for IDEs:
To allow automation and benchmark projects to be built within an IDE the lombok 'plugin' needs to be installed. To allow automation and benchmark projects to be built within an IDE the lombok 'plugin' needs to be installed.
Execute lombok.jar (doubleclick it, or run java -jar lombok.jar). Follow instructions. Execute lombok.jar (doubleclick it, or run java -jar lombok.jar). Follow instructions.
Use Solr 6 with Alfresco 5.2.x:
-------------------------------
In alfresco-global.properties (depending on the RM edition /records-management/rm-community/rm-community-repo/src/test/properties/local or /records-management/rm-enterprise/rm-enterprise-repo/src/test/properties/local)
change the value for "index.subsystem.name" from "solr4" to "solr6".
Add also the following property "solr.port=8983".
Download the latest Alfresco Search Services from
https://nexus.alfresco.com/nexus/#nexus-search;gav~~alfresco-search-services~~~
Currently it's 1.0.0 (alfresco-search-services-1.0.0.zip)
Unzip it and change to the "solr" folder within it. Start the Solr server using the following command:
solr start -a "-Dcreate.alfresco.defaults=alfresco,archive"
Start your repository

View File

@@ -389,6 +389,7 @@
<bean id="declareRecord" class="org.alfresco.module.org_alfresco_module_rm.action.impl.DeclareRecordAction" parent="rmAction"> <bean id="declareRecord" class="org.alfresco.module.org_alfresco_module_rm.action.impl.DeclareRecordAction" parent="rmAction">
<property name="publicAction" value="true"/> <property name="publicAction" value="true"/>
<property name="checkMandatoryPropertiesEnabled" value="${rm.completerecord.mandatorypropertiescheck.enabled}"/> <property name="checkMandatoryPropertiesEnabled" value="${rm.completerecord.mandatorypropertiescheck.enabled}"/>
<property name="transactionalResourceHelper" ref="rm.transactionalResourceHelper" />
</bean> </bean>
<!-- undeclare record --> <!-- undeclare record -->

View File

@@ -51,6 +51,8 @@ public interface RecordsManagementPolicies
QName ON_REMOVE_REFERENCE = QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveReference"); QName ON_REMOVE_REFERENCE = QName.createQName(NamespaceService.ALFRESCO_URI, "onRemoveReference");
QName BEFORE_RECORD_DECLARATION = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeRecordDeclaration"); QName BEFORE_RECORD_DECLARATION = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeRecordDeclaration");
QName ON_RECORD_DECLARATION = QName.createQName(NamespaceService.ALFRESCO_URI, "onRecordDeclaration"); QName ON_RECORD_DECLARATION = QName.createQName(NamespaceService.ALFRESCO_URI, "onRecordDeclaration");
QName BEFORE_RECORD_REJECTION = QName.createQName(NamespaceService.ALFRESCO_URI, "beforeRecordRejection");
QName ON_RECORD_REJECTION = QName.createQName(NamespaceService.ALFRESCO_URI, "onRecordRejection");
/** Before records management action execution */ /** Before records management action execution */
interface BeforeRMActionExecution extends ClassPolicy interface BeforeRMActionExecution extends ClassPolicy
@@ -146,4 +148,22 @@ public interface RecordsManagementPolicies
{ {
void onRecordDeclaration(NodeRef nodeRef); void onRecordDeclaration(NodeRef nodeRef);
} }
/**
* Before record rejection
* @since 2.5
*/
interface BeforeRecordRejection extends ClassPolicy
{
void beforeRecordRejection(NodeRef nodeRef);
}
/**
* On record rejection
* @since 2.5
*/
interface OnRecordRejection extends ClassPolicy
{
void onRecordRejection(NodeRef nodeRef);
}
} }

View File

@@ -38,6 +38,8 @@ import java.util.Set;
import static org.alfresco.module.org_alfresco_module_rm.record.RecordUtils.generateRecordIdentifier; import static org.alfresco.module.org_alfresco_module_rm.record.RecordUtils.generateRecordIdentifier;
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase; import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
import org.alfresco.module.org_alfresco_module_rm.record.RecordServiceImpl;
import org.alfresco.module.org_alfresco_module_rm.util.TransactionalResourceHelper;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork; import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
@@ -72,13 +74,24 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase
/** check mandatory properties */ /** check mandatory properties */
private boolean checkMandatoryPropertiesEnabled = true; private boolean checkMandatoryPropertiesEnabled = true;
/** transactional resource helper */
private TransactionalResourceHelper transactionalResourceHelper;
/** /**
* @param checkMandatoryPropertiesEnabled true if check mandatory properties is enabled, false otherwise * @param checkMandatoryPropertiesEnabled true if check mandatory properties is enabled, false otherwise
*/ */
public void setCheckMandatoryPropertiesEnabled(boolean checkMandatoryPropertiesEnabled) public void setCheckMandatoryPropertiesEnabled(boolean checkMandatoryPropertiesEnabled)
{ {
this.checkMandatoryPropertiesEnabled = checkMandatoryPropertiesEnabled; this.checkMandatoryPropertiesEnabled = checkMandatoryPropertiesEnabled;
} }
/**
* @param transactionalResourceHelper
*/
public void setTransactionalResourceHelper(TransactionalResourceHelper transactionalResourceHelper)
{
this.transactionalResourceHelper = transactionalResourceHelper;
}
/** /**
* @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef)
@@ -92,8 +105,12 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase
{ {
if (!getRecordService().isDeclared(actionedUponNodeRef)) if (!getRecordService().isDeclared(actionedUponNodeRef))
{ {
// make sure the record identifier is set // if the record is newly created make sure the record identifier is set before completing the record
generateRecordIdentifier(getNodeService(), getIdentifierService(), actionedUponNodeRef); Set<NodeRef> newRecords = transactionalResourceHelper.getSet(RecordServiceImpl.KEY_NEW_RECORDS);
if(newRecords.contains(actionedUponNodeRef))
{
generateRecordIdentifier(getNodeService(), getIdentifierService(), actionedUponNodeRef);
}
List<String> missingProperties = new ArrayList<String>(5); List<String> missingProperties = new ArrayList<String>(5);
// Aspect not already defined - check mandatory properties then add // Aspect not already defined - check mandatory properties then add

View File

@@ -274,8 +274,6 @@ public class RecordFolderType extends AbstractDisposableItem
throw new IntegrityException(I18NUtil.getMessage(MSG_CANNOT_CREATE_RECORD_FOLDER_CHILD, nodeService.getType(child)), null); throw new IntegrityException(I18NUtil.getMessage(MSG_CANNOT_CREATE_RECORD_FOLDER_CHILD, nodeService.getType(child)), null);
} }
generateRecordIdentifier(nodeService, identifierService, child);
behaviourFilter.disableBehaviour(); behaviourFilter.disableBehaviour();
try try
{ {

View File

@@ -196,18 +196,6 @@ public class RecordsManagementContainerType extends BaseBehaviourBean
setIdenifierProperty(child); setIdenifierProperty(child);
} }
} }
else
{
NodeRef parentRef = childAssocRef.getParentRef();
QName parentType = nodeService.getType(parentRef);
boolean isContentSubType = dictionaryService.isSubClass(childType, ContentModel.TYPE_CONTENT);
boolean isUnfiledRecordContainer = parentType.equals(RecordsManagementModel.TYPE_UNFILED_RECORD_CONTAINER);
boolean isUnfiledRecordFolder = parentType.equals(RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER);
if (isContentSubType && (isUnfiledRecordContainer || isUnfiledRecordFolder))
{
generateRecordIdentifier(nodeService, identifierService, child);
}
}
} }
return null; return null;

View File

@@ -51,8 +51,10 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeFileRecord; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeFileRecord;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRecordDeclaration; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRecordDeclaration;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRecordRejection;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnFileRecord; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnFileRecord;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRecordDeclaration; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRecordDeclaration;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRecordRejection;
import org.alfresco.module.org_alfresco_module_rm.capability.Capability; import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService; import org.alfresco.module.org_alfresco_module_rm.capability.CapabilityService;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
@@ -134,6 +136,7 @@ public class RecordServiceImpl extends BaseBehaviourBean
implements RecordService, implements RecordService,
RecordsManagementModel, RecordsManagementModel,
RecordsManagementCustomModel, RecordsManagementCustomModel,
NodeServicePolicies.OnAddAspectPolicy,
NodeServicePolicies.OnCreateChildAssociationPolicy, NodeServicePolicies.OnCreateChildAssociationPolicy,
NodeServicePolicies.OnRemoveAspectPolicy, NodeServicePolicies.OnRemoveAspectPolicy,
NodeServicePolicies.OnUpdatePropertiesPolicy, NodeServicePolicies.OnUpdatePropertiesPolicy,
@@ -266,6 +269,8 @@ public class RecordServiceImpl extends BaseBehaviourBean
private ClassPolicyDelegate<OnFileRecord> onFileRecord; private ClassPolicyDelegate<OnFileRecord> onFileRecord;
private ClassPolicyDelegate<BeforeRecordDeclaration> beforeRecordDeclarationDelegate; private ClassPolicyDelegate<BeforeRecordDeclaration> beforeRecordDeclarationDelegate;
private ClassPolicyDelegate<OnRecordDeclaration> onRecordDeclarationDelegate; private ClassPolicyDelegate<OnRecordDeclaration> onRecordDeclarationDelegate;
private ClassPolicyDelegate<BeforeRecordRejection> beforeRecordRejectionDelegate;
private ClassPolicyDelegate<OnRecordRejection> onRecordRejectionDelegate;
/** /**
* @param identifierService identifier service * @param identifierService identifier service
@@ -421,6 +426,34 @@ public class RecordServiceImpl extends BaseBehaviourBean
onFileRecord = policyComponent.registerClassPolicy(OnFileRecord.class); onFileRecord = policyComponent.registerClassPolicy(OnFileRecord.class);
beforeRecordDeclarationDelegate = policyComponent.registerClassPolicy(BeforeRecordDeclaration.class); beforeRecordDeclarationDelegate = policyComponent.registerClassPolicy(BeforeRecordDeclaration.class);
onRecordDeclarationDelegate = policyComponent.registerClassPolicy(OnRecordDeclaration.class); onRecordDeclarationDelegate = policyComponent.registerClassPolicy(OnRecordDeclaration.class);
beforeRecordRejectionDelegate = policyComponent.registerClassPolicy(BeforeRecordRejection.class);
onRecordRejectionDelegate = policyComponent.registerClassPolicy(OnRecordRejection.class);
}
/**
* @see org.alfresco.repo.node.NodeServicePolicies.OnAddAspectPolicy#onAddAspect(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
*/
@Override
@Behaviour
(
kind = BehaviourKind.CLASS,
type = "rma:record",
notificationFrequency = NotificationFrequency.TRANSACTION_COMMIT
)
public void onAddAspect(NodeRef nodeRef, QName aspect)
{
authenticationUtil.runAsSystem(new RunAsWork<Void>()
{
@Override
public Void doWork() throws Exception
{
if (nodeService.exists(nodeRef) && nodeService.hasAspect(nodeRef, ASPECT_RECORD))
{
generateRecordIdentifier(nodeService, identifierService, nodeRef);
}
return null;
}
});
} }
/** /**
@@ -1404,6 +1437,9 @@ public class RecordServiceImpl extends BaseBehaviourBean
// Save the id of the currently logged in user // Save the id of the currently logged in user
final String userId = AuthenticationUtil.getFullyAuthenticatedUser(); final String userId = AuthenticationUtil.getFullyAuthenticatedUser();
// invoke policy
invokeBeforeRecordRejection(nodeRef);
// do the work of rejecting the record as the system user // do the work of rejecting the record as the system user
AuthenticationUtil.runAsSystem(new RunAsWork<Void>() AuthenticationUtil.runAsSystem(new RunAsWork<Void>()
{ {
@@ -1520,6 +1556,9 @@ public class RecordServiceImpl extends BaseBehaviourBean
} }
} }
}); });
// invoke policy
invokeOnRecordRejection(nodeRef);
} }
/** /**
@@ -1916,4 +1955,32 @@ public class RecordServiceImpl extends BaseBehaviourBean
OnRecordDeclaration policy = onRecordDeclarationDelegate.get(qnames); OnRecordDeclaration policy = onRecordDeclarationDelegate.get(qnames);
policy.onRecordDeclaration(nodeRef); policy.onRecordDeclaration(nodeRef);
} }
/**
* Invoke invokeBeforeRecordRejection policy
*
* @param nodeRef node reference
*/
protected void invokeBeforeRecordRejection(NodeRef nodeRef)
{
// get qnames to invoke against
Set<QName> qnames = PoliciesUtil.getTypeAndAspectQNames(nodeService, nodeRef);
// execute policy for node type and aspects
BeforeRecordRejection policy = beforeRecordRejectionDelegate.get(qnames);
policy.beforeRecordRejection(nodeRef);
}
/**
* Invoke invokeOnRecordRejection policy
*
* @param nodeRef node reference
*/
protected void invokeOnRecordRejection(NodeRef nodeRef)
{
// get qnames to invoke against
Set<QName> qnames = PoliciesUtil.getTypeAndAspectQNames(nodeService, nodeRef);
// execute policy for node type and aspects
OnRecordRejection policy = onRecordRejectionDelegate.get(qnames);
policy.onRecordRejection(nodeRef);
}
} }

View File

@@ -79,6 +79,7 @@ import org.alfresco.rm.rest.api.RMSites;
import org.alfresco.rm.rest.api.model.RMNode; import org.alfresco.rm.rest.api.model.RMNode;
import org.alfresco.rm.rest.api.model.RMSite; import org.alfresco.rm.rest.api.model.RMSite;
import org.alfresco.rm.rest.api.model.TransferContainer; import org.alfresco.rm.rest.api.model.TransferContainer;
import org.alfresco.rm.rest.api.model.UploadInfo;
import org.alfresco.service.cmr.activities.ActivityInfo; import org.alfresco.service.cmr.activities.ActivityInfo;
import org.alfresco.service.cmr.activities.ActivityPoster; import org.alfresco.service.cmr.activities.ActivityPoster;
import org.alfresco.service.cmr.attributes.DuplicateAttributeException; import org.alfresco.service.cmr.attributes.DuplicateAttributeException;
@@ -672,32 +673,50 @@ public class FilePlanComponentsApiUtils
/** /**
* Upload a record * Upload a record
* *
* @param parentNodeRef the parent of the record * @param parentNodeRef the parent of the record
* @param name the name of the record * @param uploadInfo the infos of the uploaded record
* @param type the type of the record (if null the record's type will be cm:content) * @param parameters the object to get the parameters passed into the request
* @param properties properties to set (can be null)
* @param stream the stream to write
* @return the new record * @return the new record
*/ */
public NodeRef uploadRecord(NodeRef parentNodeRef, String name, String type, Map<String, Object> properties, InputStream stream) public NodeRef uploadRecord(NodeRef parentNodeRef, UploadInfo uploadInfo, Parameters parameters)
{ {
checkNotBlank(RMNode.PARAM_NAME, name); mandatory("parentNodeRef", parentNodeRef);
mandatory("uploadInfo", uploadInfo);
mandatory("parameters", parameters);
String nodeName = uploadInfo.getFileName();
String nodeType = uploadInfo.getNodeType();
InputStream stream = uploadInfo.getContent().getInputStream();
mandatory("stream", stream); mandatory("stream", stream);
checkNotBlank(RMNode.PARAM_NAME, nodeName);
// Create the node // Create the node
QName typeQName = StringUtils.isBlank(type) ? ContentModel.TYPE_CONTENT : nodes.createQName(type); QName typeQName = StringUtils.isBlank(nodeType) ? ContentModel.TYPE_CONTENT : nodes.createQName(nodeType);
if(!dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT)) if (!dictionaryService.isSubClass(typeQName, ContentModel.TYPE_CONTENT))
{ {
throw new InvalidArgumentException("Can only upload type of cm:content: " + typeQName); throw new InvalidArgumentException("Can only upload type of cm:content: " + typeQName);
} }
NodeRef newNodeRef = fileFolderService.create(parentNodeRef, name, typeQName).getNodeRef();
// Existing file/folder name handling
boolean autoRename = Boolean.valueOf(parameters.getParameter(RMNode.PARAM_AUTO_RENAME));
if (autoRename)
{
NodeRef existingNode = nodeService.getChildByName(parentNodeRef, ContentModel.ASSOC_CONTAINS, nodeName);
if (existingNode != null)
{
// File already exists, find a unique name
nodeName = findUniqueName(parentNodeRef, nodeName);
}
}
NodeRef newNodeRef = fileFolderService.create(parentNodeRef, nodeName, typeQName).getNodeRef();
// Write content // Write content
writeContent(newNodeRef, name, stream, true); writeContent(newNodeRef, nodeName, stream, true);
// Set the provided properties if any // Set the provided properties if any
Map<QName, Serializable> qnameProperties = mapToNodeProperties(properties); Map<QName, Serializable> qnameProperties = mapToNodeProperties(uploadInfo.getProperties());
if(qnameProperties != null) if (qnameProperties != null)
{ {
nodeService.addProperties(newNodeRef, qnameProperties); nodeService.addProperties(newNodeRef, qnameProperties);
} }

View File

@@ -207,8 +207,7 @@ public class RecordFolderChildrenRelation implements RelationshipResourceAction.
{ {
public NodeRef execute() public NodeRef execute()
{ {
return apiUtils.uploadRecord(parentNodeRef, uploadInfo.getFileName(), uploadInfo.getNodeType(), uploadInfo.getProperties(), return apiUtils.uploadRecord(parentNodeRef, uploadInfo, parameters);
uploadInfo.getContent().getInputStream());
} }
}; };
NodeRef newNode = transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true); NodeRef newNode = transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true);

View File

@@ -223,7 +223,7 @@ public class UnfiledContainerChildrenRelation implements RelationshipResourceAct
{ {
public NodeRef execute() public NodeRef execute()
{ {
return apiUtils.uploadRecord(parentNodeRef, uploadInfo.getFileName(), uploadInfo.getNodeType(), uploadInfo.getProperties(), uploadInfo.getContent().getInputStream()); return apiUtils.uploadRecord(parentNodeRef, uploadInfo, parameters);
} }
}; };
NodeRef newNode = transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true); NodeRef newNode = transactionService.getRetryingTransactionHelper().doInTransaction(callback, false, true);

View File

@@ -232,7 +232,7 @@ public class UnfiledRecordFolderChildrenRelation implements RelationshipResource
public Pair<NodeRef,NodeRef> execute() public Pair<NodeRef,NodeRef> execute()
{ {
final NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfiledRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER, uploadInfo.getRelativePath()); final NodeRef parentNodeRef = apiUtils.lookupAndValidateNodeType(unfiledRecordFolderId, RecordsManagementModel.TYPE_UNFILED_RECORD_FOLDER, uploadInfo.getRelativePath());
NodeRef newNode = apiUtils.uploadRecord(parentNodeRef, uploadInfo.getFileName(), uploadInfo.getNodeType(), uploadInfo.getProperties(), uploadInfo.getContent().getInputStream()); NodeRef newNode = apiUtils.uploadRecord(parentNodeRef, uploadInfo, parameters);
return new Pair<NodeRef, NodeRef>(newNode, parentNodeRef); return new Pair<NodeRef, NodeRef>(newNode, parentNodeRef);
} }
}; };

View File

@@ -39,9 +39,6 @@ share.protocol=http
# Validates and auto-recover if validation fails # Validates and auto-recover if validation fails
index.recovery.mode=AUTO index.recovery.mode=AUTO
# As we run embedded, we set Lucene
# TODO: Find a better solution for indexing, as buildonly (embedded Lucene) is deprecated and going to be removed soon
index.subsystem.name=buildonly
# These jobs seem to require Lucene (Unsupported Operation with Solr) so we disable them / set to future date # These jobs seem to require Lucene (Unsupported Operation with Solr) so we disable them / set to future date
# See https://forums.alfresco.com/en/viewtopic.php?f=52&t=41597 # See https://forums.alfresco.com/en/viewtopic.php?f=52&t=41597

View File

@@ -35,7 +35,9 @@ import java.util.Set;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRecordDeclaration; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRecordDeclaration;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.BeforeRecordRejection;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRecordDeclaration; import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRecordDeclaration;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementPolicies.OnRecordRejection;
import org.alfresco.module.org_alfresco_module_rm.capability.Capability; import org.alfresco.module.org_alfresco_module_rm.capability.Capability;
import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel; import org.alfresco.module.org_alfresco_module_rm.capability.RMPermissionModel;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel; import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
@@ -62,7 +64,10 @@ import org.alfresco.util.GUID;
* @author Tuna Aksoy * @author Tuna Aksoy
* @since 2.1 * @since 2.1
*/ */
public class RecordServiceImplTest extends BaseRMTestCase implements BeforeRecordDeclaration, OnRecordDeclaration public class RecordServiceImplTest extends BaseRMTestCase implements BeforeRecordDeclaration,
OnRecordDeclaration,
BeforeRecordRejection,
OnRecordRejection
{ {
/** /**
* This is a user test * This is a user test
@@ -835,4 +840,62 @@ public class RecordServiceImplTest extends BaseRMTestCase implements BeforeRecor
assertEquals(nodeRef, dmDocument); assertEquals(nodeRef, dmDocument);
onRecordDeclaration = true; onRecordDeclaration = true;
} }
/**
* RM-5180 - integration test for policies for record rejection
* @see RecordService#rejectRecord(org.alfresco.service.cmr.repository.NodeRef)
*/
private boolean beforeRecordRejection = false;
private boolean onRecordRejection = false;
@Override
public void beforeRecordRejection(NodeRef nodeRef)
{
assertEquals(nodeRef, dmDocument);
beforeRecordRejection = true;
}
@Override
public void onRecordRejection(NodeRef nodeRef)
{
assertEquals(nodeRef, dmDocument);
onRecordRejection = true;
}
public void testPolicyNotificationForRecordRejection() throws Exception
{
doTestInTransaction(new Test<Void>()
{
@Override
public Void run()
{
assertFalse(recordService.isRecord(dmDocument));
BehaviourDefinition<ClassBehaviourBinding> beforeRecordRejectionBehaviour = policyComponent.bindClassBehaviour(
RecordsManagementPolicies.BEFORE_RECORD_REJECTION, ContentModel.TYPE_CONTENT,
new JavaBehaviour(RecordServiceImplTest.this, "beforeRecordRejection", NotificationFrequency.EVERY_EVENT));
BehaviourDefinition<ClassBehaviourBinding> onRecordRejectionBehaviour = policyComponent.bindClassBehaviour(
RecordsManagementPolicies.ON_RECORD_REJECTION, ContentModel.TYPE_CONTENT,
new JavaBehaviour(RecordServiceImplTest.this, "onRecordRejection", NotificationFrequency.EVERY_EVENT));
recordService.createRecord(filePlan, dmDocument);
assertFalse(beforeRecordRejection);
assertFalse(onRecordRejection);
assertTrue(recordService.isRecord(dmDocument));
recordService.rejectRecord(dmDocument, "test reasons");
assertTrue(beforeRecordRejection);
assertTrue(onRecordRejection);
assertFalse(recordService.isRecord(dmDocument));
policyComponent.removeClassDefinition(beforeRecordRejectionBehaviour);
policyComponent.removeClassDefinition(onRecordRejectionBehaviour);
return null;
}
}, dmCollaborator);
}
} }

View File

@@ -3,7 +3,7 @@ info:
description: | description: |
**GS Core API** **GS Core API**
Provides access to the core features of Governance Services. Provides access to the core features of Alfresco Governance Services.
version: '1' version: '1'
title: Alfresco Governance Services REST API title: Alfresco Governance Services REST API
basePath: /alfresco/api/-default-/public/gs/versions/1 basePath: /alfresco/api/-default-/public/gs/versions/1
@@ -186,7 +186,7 @@ paths:
$ref: '#/definitions/RMSiteEntry' $ref: '#/definitions/RMSiteEntry'
'400': '400':
description: | description: |
Invalid parameter: PUT request is suported only for the RM site, or **siteBodyUpdate** invalid Invalid parameter: PUT request is supported only for the RM site, or **siteBodyUpdate** invalid
'401': '401':
description: Authentication failed description: Authentication failed
'403': '403':
@@ -686,13 +686,13 @@ paths:
description: | description: |
Invalid parameter: **unfiledContainerId** is not a valid format or **unfiledContainerId** is invalid Invalid parameter: **unfiledContainerId** is not a valid format or **unfiledContainerId** is invalid
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If current user does not have permission to add children to **unfiledContainerId** description: Current user does not have permission to add children to **unfiledContainerId**
'404': '404':
description: If **unfiledContainerId** does not exist description: "**unfiledContainerId** does not exist"
'409': '409':
description: If new name clashes with an existing node in the current parent container description: New name clashes with an existing node in the current parent container
'422': '422':
description: Model integrity exception, including node name with invalid characters description: Model integrity exception, including node name with invalid characters
## Unfiled record folders ## Unfiled record folders
@@ -704,7 +704,7 @@ paths:
description: | description: |
Get information for unfiled record folder id **unfiledRecordFolderId** Get information for unfiled record folder id **unfiledRecordFolderId**
Besides mandatory fields the unfiled record folder's aspects and properties are returned by default. Mandatory fields and the unfiled record folder's aspects and properties are returned by default.
You can use the **include** parameter (include=allowableOperations) to return additional information. You can use the **include** parameter (include=allowableOperations) to return additional information.
operationId: getUnfiledRecordFolder operationId: getUnfiledRecordFolder
@@ -724,11 +724,11 @@ paths:
description: | description: |
Invalid parameter: **unfiledRecordFolderId** is not a valid format Invalid parameter: **unfiledRecordFolderId** is not a valid format
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If current user does not have permission to read **unfiledRecordFolderId** description: Current user does not have permission to read **unfiledRecordFolderId**
'404': '404':
description: If **unfiledRecordFolderId** does not exist description: "**unfiledRecordFolderId** does not exist"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -780,13 +780,13 @@ paths:
description: | description: |
Invalid parameter: The update request is invalid or **unfiledRecordFolderId** is not a valid format or **unfiledRecordFolderBodyUpdate** is invalid Invalid parameter: The update request is invalid or **unfiledRecordFolderId** is not a valid format or **unfiledRecordFolderBodyUpdate** is invalid
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If current user does not have permission to update **unfiledRecordFolderId** description: Current user does not have permission to update **unfiledRecordFolderId**
'404': '404':
description: If **unfiledRecordFolderId** does not exist description: "**unfiledRecordFolderId** does not exist"
'409': '409':
description: If the updated name clashes with an existing unfiled record folder in the current parent category description: Updated name clashes with an existing unfiled record folder in the current parent category
'422': '422':
description: Model integrity exception, including file name with invalid characters description: Model integrity exception, including file name with invalid characters
default: default:
@@ -811,13 +811,13 @@ paths:
description: | description: |
Invalid parameter: **unfiledRecordFolderId** is not a valid format Invalid parameter: **unfiledRecordFolderId** is not a valid format
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If the current user does not have permission to delete **unfiledRecordFolderId** description: Current user does not have permission to delete **unfiledRecordFolderId**
'404': '404':
description: If **unfiledRecordFolderId** does not exist description: "**unfiledRecordFolderId** does not exist"
'409': '409':
description: If **unfiledRecordFolderId** is locked and cannot be deleted description: "**unfiledRecordFolderId** is locked and cannot be deleted"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -1085,13 +1085,13 @@ paths:
description: | description: |
Invalid parameter: The update request is invalid or **recordCategoryId** is not a valid format or **recordCategoryBodyUpdate** is invalid Invalid parameter: The update request is invalid or **recordCategoryId** is not a valid format or **recordCategoryBodyUpdate** is invalid
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If current user does not have permission to update **recordCategoryId** description: Current user does not have permission to update **recordCategoryId**
'404': '404':
description: If **recordCategoryId** does not exist description: "**recordCategoryId** does not exist"
'409': '409':
description: If the updated name clashes with an existing record category in the current parent category description: Updated name clashes with an existing record category in the current parent category
'422': '422':
description: Model integrity exception, including file name with invalid characters description: Model integrity exception, including file name with invalid characters
default: default:
@@ -1116,13 +1116,13 @@ paths:
description: | description: |
Invalid parameter: **recordCategoryId** is not a valid format Invalid parameter: **recordCategoryId** is not a valid format
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If the current user does not have permission to delete **recordCategoryId** description: Current user does not have permission to delete **recordCategoryId**
'404': '404':
description: If **recordCategoryId** does not exist description: "**recordCategoryId** does not exist"
'409': '409':
description: If **recordCategoryId** is locked and cannot be deleted description: "**recordCategoryId** is locked and cannot be deleted"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -1158,11 +1158,11 @@ paths:
schema: schema:
$ref: '#/definitions/RecordCategoryChildPaging' $ref: '#/definitions/RecordCategoryChildPaging'
'401': '401':
description: If authentication fails description: Authentication fails
'403': '403':
description: If current user does not have permission to read **recordCategoryId** description: Current user does not have permission to read **recordCategoryId**
'404': '404':
description: If **recordCategoryId** does not exist description: "**recordCategoryId** does not exist"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -1294,13 +1294,13 @@ paths:
description: | description: |
Invalid parameter: **recordCategoryId** is not a valid format or **nodeBodyCreate** is invalid Invalid parameter: **recordCategoryId** is not a valid format or **nodeBodyCreate** is invalid
'401': '401':
description: If authentication fails description: Authentication fails
'403': '403':
description: If current user does not have permission to add children to **recordCategoryId** description: Current user does not have permission to add children to **recordCategoryId**
'404': '404':
description: If **recordCategoryId** does not exist description: "**recordCategoryId** does not exist"
'409': '409':
description: If new name clashes with an existing node in the current parent container description: Name clashes with an existing node in the current parent container
'422': '422':
description: Model integrity exception, including node name with invalid characters description: Model integrity exception, including node name with invalid characters
## Record folders ## Record folders
@@ -1331,11 +1331,11 @@ paths:
description: | description: |
Invalid parameter: **recordFolderId** is not a valid format Invalid parameter: **recordFolderId** is not a valid format
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If current user does not have permission to read **recordFolderId** description: Current user does not have permission to read **recordFolderId**
'404': '404':
description: If **recordFolderId** does not exist description: "**recordFolderId** does not exist"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -1386,13 +1386,13 @@ paths:
description: | description: |
Invalid parameter: The update request is invalid or **recordFolderId** is not a valid format or **recordFolderBodyUpdate** is invalid Invalid parameter: The update request is invalid or **recordFolderId** is not a valid format or **recordFolderBodyUpdate** is invalid
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If current user does not have permission to update **recordFolderId** description: Current user does not have permission to update **recordFolderId**
'404': '404':
description: If **recordFolderId** does not exist description: "**recordFolderId** does not exist"
'409': '409':
description: If the updated name clashes with an existing record folder in the current parent category description: Updated name clashes with an existing record folder in the current parent category
'422': '422':
description: Model integrity exception, including file name with invalid characters description: Model integrity exception, including file name with invalid characters
default: default:
@@ -1417,13 +1417,13 @@ paths:
description: | description: |
Invalid parameter: **recordFolderId** is not a valid format Invalid parameter: **recordFolderId** is not a valid format
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If the current user does not have permission to delete **recordFolderId** description: Current user does not have permission to delete **recordFolderId**
'404': '404':
description: If **recordFolderId** does not exist description: "**recordFolderId** does not exist"
'409': '409':
description: If **recordFolderId** is locked and cannot be deleted description: "**recordFolderId** is locked and cannot be deleted"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -1458,11 +1458,11 @@ paths:
schema: schema:
$ref: '#/definitions/RecordFolderAssociationPaging' $ref: '#/definitions/RecordFolderAssociationPaging'
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If current user does not have permission to read **recordFolderId** description: Current user does not have permission to read **recordFolderId**
'404': '404':
description: If **recordFolderId** does not exist description: "**recordFolderId** does not exist"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -1587,11 +1587,11 @@ paths:
description: | description: |
Invalid parameter: **recordFolderId** is not a valid format or **recordBodyCreate** is invalid Invalid parameter: **recordFolderId** is not a valid format or **recordBodyCreate** is invalid
'401': '401':
description: If authentication fails description: Authentication failed
'403': '403':
description: If current user does not have permission to add children to **recordFolderId** description: Current user does not have permission to add children to **recordFolderId**
'404': '404':
description: If **recordFolderId** does not exist description: "**recordFolderId** does not exist"
'422': '422':
description: Model integrity exception, including node name with invalid characters description: Model integrity exception, including node name with invalid characters
## Records ## Records
@@ -1603,7 +1603,7 @@ paths:
description: | description: |
Get information for record **recordId** Get information for record **recordId**
Besides mandatory fields the record's aspects and properties are returned by default. Mandatory fields and the record's aspects and properties are returned by default.
You can use the **include** parameter (include=allowableOperations) to return additional information. You can use the **include** parameter (include=allowableOperations) to return additional information.
operationId: getRecord operationId: getRecord
@@ -1622,11 +1622,11 @@ paths:
description: | description: |
Invalid parameter: **recordId** is not a valid format Invalid parameter: **recordId** is not a valid format
'401': '401':
description: If authentication fails description: Authentication fails
'403': '403':
description: If current user does not have permission to read **recordId** description: Current user does not have permission to read **recordId**
'404': '404':
description: If **recordId** does not exist description: "**recordId** does not exist"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -1677,13 +1677,13 @@ paths:
description: | description: |
Invalid parameter: the update request is invalid or **recordId** is not a valid format or **recordBodyUpdate** is invalid Invalid parameter: the update request is invalid or **recordId** is not a valid format or **recordBodyUpdate** is invalid
'401': '401':
description: If authentication description: Authentication failed
'403': '403':
description: If current user does not have permission to update **recordId**fails description: Current user does not have permission to update **recordId**
'404': '404':
description: If **recordId** does not exist description: "**recordId** does not exist"
'409': '409':
description: If the updated name clashes with an existing node in the current parent folder description: Updated name clashes with an existing node in the current parent folder
'422': '422':
description: Model integrity exception, including file name with invalid characters description: Model integrity exception, including file name with invalid characters
default: default:
@@ -1708,13 +1708,13 @@ paths:
description: | description: |
Invalid parameter: **recordId** is not a valid format Invalid parameter: **recordId** is not a valid format
'401': '401':
description: If authentication fails description: Authentication fails
'403': '403':
description: If the current user does not have permission to delete **recordId** description: Current user does not have permission to delete **recordId**
'404': '404':
description: If **recordId** does not exist description: "**recordId** does not exist"
'409': '409':
description: If **recordId** is locked and cannot be deleted description: "**recordId** is locked and cannot be deleted"
default: default:
description: Unexpected error description: Unexpected error
schema: schema:
@@ -1800,47 +1800,6 @@ paths:
description: Unexpected error description: Unexpected error
schema: schema:
$ref: '#/definitions/Error' $ref: '#/definitions/Error'
'/records/{recordId}/complete':
post:
tags:
- records
summary: Complete a record
description: |
Completes the record **recordId**.
operationId: completeRecord
parameters:
- $ref: '#/parameters/recordIdParam'
- $ref: '#/parameters/recordEntryIncludeParam'
- $ref: '#/parameters/fieldsParam'
consumes:
- application/json
produces:
- application/json
responses:
'200':
description: Successful response
schema:
$ref: '#/definitions/RecordEntry'
'400':
description: |
Invalid parameter: **recordIdParam** is not a valid format or
**recordIdParam** is not a record
'401':
description: Authentication failed
'403':
description: >-
Current user does not have permission to complete record
**recordIdParam**
'404':
description: |
**recordIdParam** does not exist
'422':
description: |
Model integrity exception: the record is already completed
default:
description: Unexpected error
schema:
$ref: '#/definitions/Error'
## Files ## Files
'/files/{fileId}/declare': '/files/{fileId}/declare':
post: post:
@@ -1897,7 +1856,7 @@ paths:
description: | description: |
Get information for transfer container **transferContainerId** Get information for transfer container **transferContainerId**
Besides mandatory fields the transfer container's aspects and properties are returned by default. Mandatory fields and the transfer container's aspects and properties are returned by default.
You can use the **include** parameter (include=allowableOperations) to return additional information. You can use the **include** parameter (include=allowableOperations) to return additional information.
operationId: getTransferContainer operationId: getTransferContainer