Merged V2.0 to HEAD:

39658: RM-469: Modified date is updated for all items in File Plan at the same time
   41343: Merged V2.0-BUG-FIX to V2.0 :
        39590: RM-452: RM seurity context will break core Alfresco if Alfresco's public services change
        39591: RM: Removal of System out's and replace with logging.
        39592: RM: Update properties of 2.0.1 build
        40344: RM-477: RM 2.0 does not work with SOLR
            * first pass at supporting SOLR in RM2
            * currently requires reimplementation of permissionServiceImpl due to visibility of methods and member variables
            * job execution delayed to try and prevent start up errors
            * fetchSOLR task added to gradle to retrieve SOLR zip from Maven (could do with some scripts to help with setup for module)
            * TODO more testing and refinement of solution
        40383: RM: Fix unit test
        40656: Updated ReadMe.txt
        40696: RM-439: Folders have no File to... option
        40778: RM-434: The user has all rights for audit once "Access Audit" capability is added to him
        40784: RM-482: RM .classpath files have local library references
        40785: RM: Remove .settings dir for Eclipse projects
            * See RM-482
        40786: RM:  Set svn:ignore for generated eclipse project files
        40800: RM-434
        40904: It was not possible to edit/delete a custom meta data
        40920: Disabled the delete button for custom meta data
        40948: RM-438: Custom metadata fields and Special Types aren't available in Email Mappings
        41021: RM-438: Custom metadata fields and Special Types aren't available in Email Mappings
            * ensured the 'group' information was being passed to property-menu (so the custom and dynamic record meta-data is available)
            * also tweaked audit in the same way
            * removed code to add custom meta-data props as no longer needed
        41023: Merged HEAD to BANCHES/V2.0-BUG-FIX:
             40944: Adds Spanish RM translation
        41263: RM-492: RMMetaDataGet: Namespace prefix Alfresco is not mapped to a namespace URI
        41305: RM-495: Search for Workflow Group Assignee does not work
        41307: RM-494: Access when changing type in Records Management 2.0
   41403: RM-496: Incorrect behavior on Move action
   41638: RM:  Spanish translation
   42412: RM-499: Collabarator can add categories when they should not be able to
   42413: Fix up unit test.
   42506: RM-507: Incorrect RM module version is displayed after the module installation and during the startup
   42555: RM-501: Incorrect behaviour on Move To Action
   42558: RM-503: Record Search - Category is not found by VitalRecordReviewPeriod
   42564: Merged modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX to modules/recordsmanagement/BRANCHES/V2.0
        42532: RM-510 (CLONE - RM Email Mapping tool behaves strangely)
   42567: Merged modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX to modules/recordsmanagement/BRANCHES/V2.0
        42539: RM-506 (RM Console - Audit - Some Audit Events are displayed incorrectly.)
   42569: Merged modules/recordsmanagement/BRANCHES/V2.0-BUG-FIX to modules/recordsmanagement/BRANCHES/V2.0
        42527: RM-240 (In the RM Console: Can add two events of the same name)
   42602: RM-500: SOLR: "No items" message after complete Transfer action
       * filtered non-existant node from search results
   42603: RM-502: SOLR: Search by Records properties doesn't work
   42633: RM-512 (RM actions are odd after renaming a category/folder/record)
   42634: RM-511 (Repository Descriptior (originally installed) is broken if RM installed)
   42645: RM-507: Incorrect RM module version is displayed after the module installation and during the startup
   42673: RM-520 (RM Site - File Plan - 'Show Folders' and 'Sort' icons show incorrect pop-ups)
   42675: RM-521 (RM Site - File Plan - 'Show Folders' link is incorrect)
   42682: RM-515 (Incorrect labels for categories, folders and records on Repository Browser page)
   42697: RM-517 ('Import' button is enabled in a closed folder)
   42701: RM-517: 'Import' button is enabled in a closed folder
   42707: RM-519: RM 2.0.1 NPE ERROR on startup
   42749: RM-512 (RM actions are odd after renaming a category/folder/record)
   42777: RM-514: SOLR throws several errors during the startup
   42778: RM-529: Not possible to delete reference from target record
   42779: RM-523: Installation: Impossible to install RM module without -force option
   42780: Re-adding custom bubbling js file that has IE7 specific fixes.
   42794: There is no need to override the documentlist.get.properties. The rm specific properties have been moved to rm.properties. In doing so we'll prevent missing "standard" labels
   42795: No need to keep documentlist.get.js in the rm module. There are no changes in this file
   42877: We cannot delete documentlist.get.js. It is needed for the file plan site. This file does not replace the original file. The paths are different.
   42897: RM-452: RM seurity context will break core Alfresco if Alfresco's public services change
       * ensure the '*' definition is at the end of the updated value
       * add missing method definitions
   42898: RM-522: Disposition actions for record are not available for non-admin users
   42908: RM-526 (Incorrect pop-up for EXIF icon)
   42909: RM-531: Impossible to declare email record
   42911: RM-533 (RM AMP doesn't work with latest community build)
   42940: RM-527 (Simple view is not correct on the File Plan page - thumbnails are displayed instead of filetype icons)
   42983: RM-528 (It's possible to create relationship with the same name as already exists)
   43482: RM: add -force parameter to build scripts
   43486: Merged V2.0-BUG-FIX to V2.0 :
        41757: RM-472: Spelling errors in Management Console (Events)
        42505: RM-462: Spelling errors in Edit Disposition Schedule
        42527: RM-240 (In the RM Console: Can add two events of the same name)
        42532: RM-510 (CLONE - RM Email Mapping tool behaves strangely)
        42539: RM-506 (RM Console - Audit - Some Audit Events are displayed incorrectly.
        42776: RM-463: Tooltip text inconsistent on Edit Disposition Schedule page
   43714: Merged V2.0-BUG-FIX to V2.0 :
        43623: RM-541 (Error shown in firebug when the details page of a record is open)
        43661: RM-550 (JS Error while declaring a record)



git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/modules/recordsmanagement/HEAD@43758 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Roy Wetherall
2012-11-20 23:33:27 +00:00
53 changed files with 1023 additions and 1008 deletions

View File

@@ -1127,6 +1127,12 @@ public class RecordsManagementAdminServiceImpl implements RecordsManagementAdmin
public QName addCustomAssocDefinition(String label)
{
ParameterCheck.mandatoryString("label", label);
// If this label is already taken...
if (existsLabel(label))
{
throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_LABEL_IN_USE, label));
}
NodeRef modelRef = getCustomModelRef(""); // defaults to RM_CUSTOM_URI
M2Model deserializedModel = readCustomContentModel(modelRef);
@@ -1140,12 +1146,6 @@ public class RecordsManagementAdminServiceImpl implements RecordsManagementAdmin
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNKNOWN_ASPECT, aspectName));
}
// If this label is already taken...
if (getQNameForClientId(label) != null)
{
throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_LABEL_IN_USE, label));
}
QName generatedQName = this.generateQNameFor(label);
String generatedShortQName = generatedQName.toPrefixString(namespaceService);
@@ -1178,6 +1178,18 @@ public class RecordsManagementAdminServiceImpl implements RecordsManagementAdmin
return generatedQName;
}
private boolean existsLabel(String label)
{
for (AssociationDefinition associationDefinition : getCustomReferenceDefinitions().values())
{
if (associationDefinition.getTitle().equalsIgnoreCase(label))
{
return true;
}
}
return false;
}
// note: currently RMC custom assocs only
public QName addCustomChildAssocDefinition(String source, String target)
@@ -1185,6 +1197,12 @@ public class RecordsManagementAdminServiceImpl implements RecordsManagementAdmin
ParameterCheck.mandatoryString("source", source);
ParameterCheck.mandatoryString("target", target);
String compoundID = this.getCompoundIdFor(source, target);
if (existsLabel(compoundID))
{
throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_LABEL_IN_USE, compoundID));
}
NodeRef modelRef = getCustomModelRef(""); // defaults to RM_CUSTOM_URI
M2Model deserializedModel = readCustomContentModel(modelRef);
@@ -1197,12 +1215,6 @@ public class RecordsManagementAdminServiceImpl implements RecordsManagementAdmin
throw new AlfrescoRuntimeException(I18NUtil.getMessage(MSG_UNKNOWN_ASPECT, aspectName));
}
String compoundID = this.getCompoundIdFor(source, target);
if (getQNameForClientId(compoundID) != null)
{
throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_LABEL_IN_USE, compoundID));
}
M2ClassAssociation customAssoc = customAssocsAspect.getAssociation(compoundID);
if (customAssoc != null)
{
@@ -1238,13 +1250,23 @@ public class RecordsManagementAdminServiceImpl implements RecordsManagementAdmin
public QName updateCustomChildAssocDefinition(QName refQName, String newSource, String newTarget)
{
String compoundId = getCompoundIdFor(newSource, newTarget);
// If this compoundId is already taken...
if (existsLabel(compoundId))
{
throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_LABEL_IN_USE, compoundId));
}
return persistUpdatedAssocTitle(refQName, compoundId);
}
// note: currently RMC custom assocs only
public QName updateCustomAssocDefinition(QName refQName, String newLabel)
{
return persistUpdatedAssocTitle(refQName, newLabel);
// If this label is already taken...
if (existsLabel(newLabel))
{
throw new IllegalArgumentException(I18NUtil.getMessage(MSG_REF_LABEL_IN_USE, newLabel));
}
return persistUpdatedAssocTitle(refQName, newLabel);
}
/**

View File

@@ -29,6 +29,7 @@ import java.util.Set;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.action.RMActionExecuterAbstractBase;
import org.alfresco.repo.action.executer.ActionExecuterAbstractBase;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.dictionary.AspectDefinition;
@@ -77,10 +78,11 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase
// remove all owner related rights
this.ownableService.setOwner(actionedUponNodeRef, OwnableService.NO_OWNER);
}
}
else
{
throw new AlfrescoRuntimeException(buildMissingPropertiesErrorString(missingProperties));
logger.debug(buildMissingPropertiesErrorString(missingProperties));
action.setParameterValue(ActionExecuterAbstractBase.PARAM_RESULT, "missingProperties");
}
}
}
@@ -95,9 +97,10 @@ public class DeclareRecordAction extends RMActionExecuterAbstractBase
StringBuilder builder = new StringBuilder(255);
builder.append(I18NUtil.getMessage(MSG_NO_DECLARE_MAND_PROP));
builder.append(" ");
for (String missingProperty : missingProperties)
for (String missingProperty : missingProperties)
{
builder.append(missingProperty).append(", ");
builder.append(missingProperty)
.append(", ");
}
return builder.toString();
}

View File

@@ -129,6 +129,8 @@ public class RecordsManagementAuditServiceImpl
private static final String MSG_WEB_RECORD = "rm.audit.web-record";
private static final String MSG_TRAIL_FILE_FAIL = "rm.audit.trail-file-fail";
private static final String MSG_AUDIT_REPORT = "rm.audit.audit-report";
private static final String MSG_CREATE_DISPOSITION_SCHEDULE = "rm.audit.create-disposition-schedule";
private static final String MSG_UNFREEZE = "rm.audit.unfreeze";
/** Logger */
private static Log logger = LogFactory.getLog(RecordsManagementAuditServiceImpl.class);
@@ -320,6 +322,10 @@ public class RecordsManagementAuditServiceImpl
new AuditEvent("applyDigitalPhotographRecord", MSG_PHOTO_RECORD));
this.auditEvents.put("applyWebRecord",
new AuditEvent("applyWebRecord", MSG_WEB_RECORD));
this.auditEvents.put("createDispositionSchedule",
new AuditEvent("createDispositionSchedule", MSG_CREATE_DISPOSITION_SCHEDULE));
this.auditEvents.put("unfreeze",
new AuditEvent("unfreeze", MSG_UNFREEZE));
}
@Override

View File

@@ -520,29 +520,36 @@ public class RMAfterInvocationProvider extends RMSecurityCommon
// All permission checks must pass
inclusionMask.set(i, true);
int parentCheckRead = checkRead(returnedObject.getChildAssocRef(i).getParentRef());
int childCheckRead = checkRead(returnedObject.getNodeRef(i));
for (ConfigAttributeDefintion cad : supportedDefinitions)
if (nodeService.exists(returnedObject.getNodeRef(i)) == false)
{
NodeRef testNodeRef = returnedObject.getNodeRef(i);
int checkRead = childCheckRead;
if (cad.parent)
{
testNodeRef = returnedObject.getChildAssocRef(i).getParentRef();
checkRead = parentCheckRead;
}
if (isUnfiltered(testNodeRef))
{
continue;
}
if (inclusionMask.get(i) && (testNodeRef != null) && (checkRead != AccessDecisionVoter.ACCESS_GRANTED))
{
inclusionMask.set(i, false);
}
inclusionMask.set(i, false);
}
else
{
int parentCheckRead = checkRead(returnedObject.getChildAssocRef(i).getParentRef());
int childCheckRead = checkRead(returnedObject.getNodeRef(i));
for (ConfigAttributeDefintion cad : supportedDefinitions)
{
NodeRef testNodeRef = returnedObject.getNodeRef(i);
int checkRead = childCheckRead;
if (cad.parent)
{
testNodeRef = returnedObject.getChildAssocRef(i).getParentRef();
checkRead = parentCheckRead;
}
if (isUnfiltered(testNodeRef))
{
continue;
}
if (inclusionMask.get(i) && (testNodeRef != null) && (checkRead != AccessDecisionVoter.ACCESS_GRANTED))
{
inclusionMask.set(i, false);
}
}
}
// Bug out if we are limiting by size

View File

@@ -73,6 +73,14 @@ public interface RecordsManagementEventService
*/
boolean existsEvent(String eventName);
/**
* Indicates whether a particular event display label exists. Returns true if it does, false otherwise.
*
* @param eventDisplayLabel event display label
* @return true if event display label exists, false otherwise
*/
boolean existsEventDisplayLabel(String eventDisplayLabel);
/**
* Add an event
*

View File

@@ -20,6 +20,7 @@ package org.alfresco.module.org_alfresco_module_rm.event;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -122,6 +123,23 @@ public class RecordsManagementEventServiceImpl implements RecordsManagementEvent
return getEventMap().containsKey(eventName);
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#existsEventDisplayLabel(java.lang.String)
*/
@SuppressWarnings("rawtypes")
public boolean existsEventDisplayLabel(String eventDisplayLabel)
{
for (Iterator iterator = getEventMap().values().iterator(); iterator.hasNext();)
{
RecordsManagementEvent recordsManagementEvent = (RecordsManagementEvent) iterator.next();
if (recordsManagementEvent.getDisplayLabel().equalsIgnoreCase(eventDisplayLabel))
{
return true;
}
}
return false;
}
/**
* @see org.alfresco.module.org_alfresco_module_rm.event.RecordsManagementEventService#addEvent(java.lang.String, java.lang.String, java.lang.String)
*/

View File

@@ -18,6 +18,7 @@
*/
package org.alfresco.module.org_alfresco_module_rm.forms;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -263,20 +264,31 @@ public class RecordsManagementNodeFormFilter extends RecordsManagementFormFilter
List<FieldDefinition> fieldDefs = form.getFieldDefinitions();
for (FieldDefinition fieldDef : fieldDefs)
{
if (fieldDef.getName().equals("cm:title") ||
fieldDef.getName().equals("cm:author") ||
fieldDef.getName().equals("rma:originator") ||
fieldDef.getName().equals("rma:publicationDate") ||
fieldDef.getName().equals("rma:dateReceived") ||
fieldDef.getName().equals("rma:address") ||
fieldDef.getName().equals("rma:otherAddress"))
{
fieldDef.setProtectedField(true);
String prefixName = fieldDef.getName();
// check the value of the property, if empty then do not mark property
// as read only
QName qname = QName.createQName(prefixName, namespaceService);
Serializable value = nodeService.getProperty(nodeRef, qname);
if (value != null)
{
if (prefixName.equals("cm:title") ||
prefixName.equals("cm:author") ||
prefixName.equals("rma:originator") ||
prefixName.equals("rma:publicationDate") ||
prefixName.equals("rma:dateReceived") ||
prefixName.equals("rma:address") ||
prefixName.equals("rma:otherAddress"))
{
fieldDef.setProtectedField(true);
}
}
}
if (logger.isDebugEnabled())
if (logger.isDebugEnabled() == true)
{
logger.debug("Set email related fields to be protected");
}
}
/**

View File

@@ -19,23 +19,21 @@
package org.alfresco.module.org_alfresco_module_rm.job;
import org.alfresco.module.org_alfresco_module_rm.model.RecordsManagementModel;
import org.alfresco.repo.admin.RepositoryState;
import org.alfresco.repo.transaction.RetryingTransactionHelper;
import org.springframework.context.ApplicationEvent;
import org.springframework.extensions.surf.util.AbstractLifecycleBean;
/**
* Records management job executer base class.
*
* @author Roy Wetherall
*/
public abstract class RecordsManagementJobExecuter extends AbstractLifecycleBean
implements RecordsManagementModel
public abstract class RecordsManagementJobExecuter implements RecordsManagementModel
{
/** Retrying transaction helper */
protected RetryingTransactionHelper retryingTransactionHelper;
/** Indicates whether the application bootstrap is complete or not */
protected boolean bootstrapComplete = false;
/** Repository state helper */
protected RepositoryState repositoryState;
/**
* @param retryingTransactionHelper retrying transaction helper
@@ -45,13 +43,21 @@ public abstract class RecordsManagementJobExecuter extends AbstractLifecycleBea
this.retryingTransactionHelper = retryingTransactionHelper;
}
/**
* @param repositoryState repository state helper component
*/
public void setRepositoryState(RepositoryState repositoryState)
{
this.repositoryState = repositoryState;
}
/**
* Executes the jobs work.
*/
public void execute()
{
// jobs not allowed to execute unless bootstrap is complete
if (bootstrapComplete == true)
if (repositoryState.isBootstrapping() == false)
{
executeImpl();
}
@@ -61,32 +67,4 @@ public abstract class RecordsManagementJobExecuter extends AbstractLifecycleBea
* Jobs work implementation.
*/
public abstract void executeImpl();
/**
* @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onBootstrap(org.springframework.context.ApplicationEvent)
*/
@Override
protected void onBootstrap(ApplicationEvent arg0)
{
// record that the bootstrap has complete
bootstrapComplete = true;
}
/**
* @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onShutdown(org.springframework.context.ApplicationEvent)
*/
@Override
protected void onShutdown(ApplicationEvent arg0)
{
// no implementation
}
/**
* @see org.springframework.extensions.surf.util.AbstractLifecycleBean#onApplicationEvent(org.springframework.context.ApplicationEvent)
*/
@Override
public void onApplicationEvent(ApplicationEvent arg0)
{
// no implementation
}
}

View File

@@ -498,15 +498,16 @@ public class RecordsManagementSearchBehaviour implements RecordsManagementModel
{
if (nodeService.exists(nodeRef) == true)
{
// Initialise the search parameteres as required
// Initialise the search parameters as required
setVitalRecordDefintionDetails(nodeRef);
}
}
public void vitalRecordDefintionAddAspect(NodeRef nodeRef, QName aspectTypeQName)
{
// Only care about record folders
if (recordsManagementService.isRecordFolder(nodeRef) == true)
// Only care about record folders or record categories
if (recordsManagementService.isRecordFolder(nodeRef) == true ||
recordsManagementService.isRecordCategory(nodeRef) == true)
{
updateVitalRecordDefinitionValues(nodeRef);
}
@@ -514,8 +515,9 @@ public class RecordsManagementSearchBehaviour implements RecordsManagementModel
public void vitalRecordDefintionUpdateProperties(NodeRef nodeRef, Map<QName, Serializable> before, Map<QName, Serializable> after)
{
// Only care about record folders
if (recordsManagementService.isRecordFolder(nodeRef) == true)
// Only care about record folders or record categories
if (recordsManagementService.isRecordFolder(nodeRef) == true ||
recordsManagementService.isRecordCategory(nodeRef) == true)
{
Set<QName> props = new HashSet<QName>(1);
props.add(PROP_REVIEW_PERIOD);
@@ -535,14 +537,17 @@ public class RecordsManagementSearchBehaviour implements RecordsManagementModel
applySearchAspect(nodeRef);
setVitalRecordDefintionDetails(nodeRef);
List<NodeRef> records = recordsManagementService.getRecords(nodeRef);
for (NodeRef record : records)
{
// Apply the search aspect
applySearchAspect(record);
// Set the vital record definition details
setVitalRecordDefintionDetails(record);
if (recordsManagementService.isRecordFolder(nodeRef) == true)
{
List<NodeRef> records = recordsManagementService.getRecords(nodeRef);
for (NodeRef record : records)
{
// Apply the search aspect
applySearchAspect(record);
// Set the vital record definition details
setVitalRecordDefintionDetails(record);
}
}
}

View File

@@ -28,13 +28,16 @@ import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.module.org_alfresco_module_rm.RecordsManagementService;
import org.alfresco.module.org_alfresco_module_rm.security.RecordsManagementSecurityService;
import org.alfresco.module.org_alfresco_module_rm.security.Role;
import org.alfresco.repo.model.Repository;
import org.alfresco.repo.notification.EMailNotificationProvider;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.repo.tenant.TenantAdminService;
import org.alfresco.repo.tenant.TenantUtil;
import org.alfresco.repo.tenant.TenantUtil.TenantRunAsWork;
import org.alfresco.service.cmr.notification.NotificationContext;
import org.alfresco.service.cmr.notification.NotificationService;
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.cmr.search.SearchService;
import org.alfresco.service.cmr.security.AuthorityService;
@@ -68,11 +71,12 @@ public class RecordsManagementNotificationHelper
private NotificationService notificationService;
private RecordsManagementService recordsManagementService;
private RecordsManagementSecurityService securityService;
private Repository repositoryHelper;
private SearchService searchService;
private NamespaceService namespaceService;
private SiteService siteService;
private AuthorityService authorityService;
private TenantAdminService tenantAdminService;
private NodeService nodeService;
/** Notification role */
private String notificationRole;
@@ -113,15 +117,6 @@ public class RecordsManagementNotificationHelper
this.notificationRole = notificationRole;
}
/**
*
* @param repositoryHelper repository helper
*/
public void setRepositoryHelper(Repository repositoryHelper)
{
this.repositoryHelper = repositoryHelper;
}
/**
* @param searchService search service
*/
@@ -154,6 +149,22 @@ public class RecordsManagementNotificationHelper
this.authorityService = authorityService;
}
/**
* @param nodeService node service
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @param tenantAdminService tenant admin service
*/
public void setTenantAdminService(TenantAdminService tenantAdminService)
{
this.tenantAdminService = tenantAdminService;
}
/**
* @return superseded email template
*/
@@ -171,7 +182,7 @@ public class RecordsManagementNotificationHelper
{
List<NodeRef> nodeRefs =
searchService.selectNodes(
repositoryHelper.getRootHome(),
getRootNode(),
"app:company_home/app:dictionary/cm:records_management/cm:records_management_email_templates/cm:notify-records-due-for-review-email.ftl", null,
namespaceService,
false);
@@ -183,6 +194,23 @@ public class RecordsManagementNotificationHelper
return dueForReviewTemplate;
}
/**
* Helper method to get root node in a tenant safe way.
*
* @return NodeRef root node of spaces store
*/
private NodeRef getRootNode()
{
String tenantDomain = tenantAdminService.getCurrentUserDomain();
return TenantUtil.runAsSystemTenant(new TenantRunAsWork<NodeRef>()
{
public NodeRef doWork() throws Exception
{
return nodeService.getRootNode(StoreRef.STORE_REF_WORKSPACE_SPACESSTORE);
}
}, tenantDomain);
}
/**
* Sends records due for review email notification.
*

View File

@@ -104,6 +104,7 @@ public class CustomRefDelete extends AbstractRmWebScript
}
rmAdminService.removeCustomReference(fromNodeRef, toNodeRef, qn);
rmAdminService.removeCustomReference(toNodeRef, fromNodeRef, qn);
result.put("success", true);

View File

@@ -85,6 +85,11 @@ public class CustomReferenceDefinitionPost extends AbstractRmWebScript
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
"Could not parse JSON from req.", je);
}
catch (IllegalArgumentException iae)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
iae.getMessage(), iae);
}
return ftlModel;
}

View File

@@ -86,6 +86,11 @@ public class CustomReferenceDefinitionPut extends AbstractRmWebScript
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
"Could not parse JSON from req.", je);
}
catch (IllegalArgumentException iae)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
iae.getMessage(), iae);
}
return ftlModel;
}

View File

@@ -23,8 +23,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletResponse;
import org.alfresco.model.ContentModel;
import org.alfresco.module.org_alfresco_module_rm.disposition.DispositionAction;
import org.alfresco.module.org_alfresco_module_rm.event.EventCompletionDetails;
@@ -68,9 +66,12 @@ public class DispositionLifecycleGet extends DispositionAbstractBase
DispositionAction nextAction = this.dispositionService.getNextDispositionAction(nodeRef);
if (nextAction == null)
{
status.setCode(HttpServletResponse.SC_NOT_FOUND,
"Node " + nodeRef.toString() + " does not have a disposition lifecycle");
return null;
Map<String, Object> nextActionModel = new HashMap<String, Object>(2);
nextActionModel.put("notFound", true);
nextActionModel.put("message", "Node " + nodeRef.toString() + " does not have a disposition lifecycle");
Map<String, Object> model = new HashMap<String, Object>(1);
model.put("nextaction", nextActionModel);
return model;
}
else
{

View File

@@ -53,6 +53,16 @@ public class EmailMapPost extends DeclarativeWebScript
JSONObject json = null;
json = new JSONObject(new JSONTokener(req.getContent().getContent()));
if(json.has("delete"))
{
JSONArray toDelete = json.getJSONArray("delete");
for(int i = 0 ; i < toDelete.length(); i++)
{
JSONObject val = toDelete.getJSONObject(i);
customEmailMappingService.deleteCustomMapping(val.getString("from"), val.getString("to"));
}
}
if(json.has("add"))
{
JSONArray toAdd = json.getJSONArray("add");
@@ -64,17 +74,6 @@ public class EmailMapPost extends DeclarativeWebScript
}
}
if(json.has("delete"))
{
JSONArray toDelete = json.getJSONArray("delete");
for(int i = 0 ; i < toDelete.length(); i++)
{
JSONObject val = toDelete.getJSONObject(i);
customEmailMappingService.deleteCustomMapping(val.getString("from"), val.getString("to"));
}
}
// Set the return value.
Set<CustomMapping> emailMap = customEmailMappingService.getCustomMappings();
// create model object with the lists model

View File

@@ -50,175 +50,173 @@ import org.json.JSONTokener;
*/
public class RmActionPost extends DeclarativeWebScript
{
private static Log logger = LogFactory.getLog(RmActionPost.class);
private static final String PARAM_NAME = "name";
private static final String PARAM_NODE_REF = "nodeRef";
private static final String PARAM_NODE_REFS = "nodeRefs";
private static final String PARAM_PARAMS = "params";
private NodeService nodeService;
private RecordsManagementActionService rmActionService;
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
private static Log logger = LogFactory.getLog(RmActionPost.class);
public void setRecordsManagementActionService(RecordsManagementActionService rmActionService)
{
this.rmActionService = rmActionService;
}
private static final String PARAM_NAME = "name";
private static final String PARAM_NODE_REF = "nodeRef";
private static final String PARAM_NODE_REFS = "nodeRefs";
private static final String PARAM_PARAMS = "params";
@SuppressWarnings("unchecked")
@Override
public Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)
{
String reqContentAsString;
try
{
reqContentAsString = req.getContent().getContent();
}
catch (IOException iox)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
"Could not read content from req.", iox);
}
private NodeService nodeService;
private RecordsManagementActionService rmActionService;
String actionName = null;
List<NodeRef> targetNodeRefs = null;
Map<String, Serializable> actionParams = new HashMap<String, Serializable>(3);
try
{
JSONObject jsonObj = new JSONObject(new JSONTokener(reqContentAsString));
// Get the action name
if (jsonObj.has(PARAM_NAME) == true)
{
actionName = jsonObj.getString(PARAM_NAME);
}
// Get the target references
if (jsonObj.has(PARAM_NODE_REF) == true)
{
NodeRef nodeRef = new NodeRef(jsonObj.getString(PARAM_NODE_REF));
targetNodeRefs = new ArrayList<NodeRef>(1);
targetNodeRefs.add(nodeRef);
}
if (jsonObj.has(PARAM_NODE_REFS) == true)
{
JSONArray jsonArray = jsonObj.getJSONArray(PARAM_NODE_REFS);
if (jsonArray.length() != 0)
{
targetNodeRefs = new ArrayList<NodeRef>(jsonArray.length());
for (int i = 0; i < jsonArray.length(); i++)
{
NodeRef nodeRef = new NodeRef(jsonArray.getString(i));
targetNodeRefs.add(nodeRef);
}
}
}
// params are optional.
if (jsonObj.has(PARAM_PARAMS))
{
JSONObject paramsObj = jsonObj.getJSONObject(PARAM_PARAMS);
for (Iterator iter = paramsObj.keys(); iter.hasNext(); )
{
Object nextKey = iter.next();
String nextKeyString = (String)nextKey;
Object nextValue = paramsObj.get(nextKeyString);
// Check for date values
if (nextValue instanceof JSONObject)
{
if (((JSONObject)nextValue).has("iso8601") == true)
{
String dateStringValue = ((JSONObject)nextValue).getString("iso8601");
nextValue = ISO8601DateFormat.parse(dateStringValue);
}
}
actionParams.put(nextKeyString, (Serializable)nextValue);
}
}
}
catch (JSONException exception)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Unable to parse request JSON.");
}
// validate input: check for mandatory params.
// Some RM actions can be posted without a nodeRef.
if (actionName == null)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
"A mandatory parameter has not been provided in URL");
}
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
// Check that all the nodes provided exist and build report string
StringBuffer targetNodeRefsString = new StringBuffer(30);
boolean firstTime = true;
for (NodeRef targetNodeRef : targetNodeRefs)
{
if (nodeService.exists(targetNodeRef) == false)
{
throw new WebScriptException(Status.STATUS_NOT_FOUND,
"The targetNode does not exist (" + targetNodeRef.toString() + ")");
}
// Build the string
if (firstTime == true)
{
firstTime = false;
}
else
{
targetNodeRefsString.append(", ");
}
targetNodeRefsString.append(targetNodeRef.toString());
}
public void setRecordsManagementActionService(RecordsManagementActionService rmActionService)
{
this.rmActionService = rmActionService;
}
// Proceed to execute the specified action on the specified node.
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Executing Record Action ")
.append(actionName)
.append(", (")
.append(targetNodeRefsString.toString())
.append("), ")
.append(actionParams);
logger.debug(msg.toString());
}
Map<String, Object> model = new HashMap<String, Object>();
if (targetNodeRefs.isEmpty())
{
RecordsManagementActionResult result = this.rmActionService.executeRecordsManagementAction(actionName, actionParams);
if (result.getValue() != null)
{
model.put("result", result.getValue().toString());
}
}
else
{
Map<NodeRef, RecordsManagementActionResult> resultMap = this.rmActionService.executeRecordsManagementAction(targetNodeRefs, actionName, actionParams);
Map<String, String> results = new HashMap<String, String>(resultMap.size());
for (NodeRef nodeRef : resultMap.keySet())
{
Object value = resultMap.get(nodeRef).getValue();
if (value != null)
{
results.put(nodeRef.toString(), resultMap.get(nodeRef).getValue().toString());
}
}
model.put("results", results);
}
model.put("message", "Successfully queued action [" + actionName + "] on " + targetNodeRefsString.toString());
@SuppressWarnings("unchecked")
@Override
public Map<String, Object> executeImpl(WebScriptRequest req, Status status, Cache cache)
{
String reqContentAsString;
try
{
reqContentAsString = req.getContent().getContent();
}
catch (IOException iox)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
"Could not read content from req.", iox);
}
return model;
}
String actionName = null;
List<NodeRef> targetNodeRefs = new ArrayList<NodeRef>(1);
Map<String, Serializable> actionParams = new HashMap<String, Serializable>(3);
try
{
JSONObject jsonObj = new JSONObject(new JSONTokener(reqContentAsString));
// Get the action name
if (jsonObj.has(PARAM_NAME) == true)
{
actionName = jsonObj.getString(PARAM_NAME);
}
// Get the target references
if (jsonObj.has(PARAM_NODE_REF) == true)
{
NodeRef nodeRef = new NodeRef(jsonObj.getString(PARAM_NODE_REF));
targetNodeRefs.add(nodeRef);
}
if (jsonObj.has(PARAM_NODE_REFS) == true)
{
JSONArray jsonArray = jsonObj.getJSONArray(PARAM_NODE_REFS);
if (jsonArray.length() != 0)
{
targetNodeRefs = new ArrayList<NodeRef>(jsonArray.length());
for (int i = 0; i < jsonArray.length(); i++)
{
NodeRef nodeRef = new NodeRef(jsonArray.getString(i));
targetNodeRefs.add(nodeRef);
}
}
}
// params are optional.
if (jsonObj.has(PARAM_PARAMS))
{
JSONObject paramsObj = jsonObj.getJSONObject(PARAM_PARAMS);
for (Iterator<String> iter = paramsObj.keys(); iter.hasNext(); )
{
String nextKeyString = iter.next();
Object nextValue = paramsObj.get(nextKeyString);
// Check for date values
if (nextValue instanceof JSONObject)
{
if (((JSONObject)nextValue).has("iso8601") == true)
{
String dateStringValue = ((JSONObject)nextValue).getString("iso8601");
nextValue = ISO8601DateFormat.parse(dateStringValue);
}
}
actionParams.put(nextKeyString, (Serializable)nextValue);
}
}
}
catch (JSONException exception)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "Unable to parse request JSON.");
}
// validate input: check for mandatory params.
// Some RM actions can be posted without a nodeRef.
if (actionName == null)
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
"A mandatory parameter has not been provided in URL");
}
// Check that all the nodes provided exist and build report string
StringBuffer targetNodeRefsString = new StringBuffer(30);
boolean firstTime = true;
for (NodeRef targetNodeRef : targetNodeRefs)
{
if (nodeService.exists(targetNodeRef) == false)
{
throw new WebScriptException(Status.STATUS_NOT_FOUND,
"The targetNode does not exist (" + targetNodeRef.toString() + ")");
}
// Build the string
if (firstTime == true)
{
firstTime = false;
}
else
{
targetNodeRefsString.append(", ");
}
targetNodeRefsString.append(targetNodeRef.toString());
}
// Proceed to execute the specified action on the specified node.
if (logger.isDebugEnabled())
{
StringBuilder msg = new StringBuilder();
msg.append("Executing Record Action ")
.append(actionName)
.append(", (")
.append(targetNodeRefsString.toString())
.append("), ")
.append(actionParams);
logger.debug(msg.toString());
}
Map<String, Object> model = new HashMap<String, Object>();
if (targetNodeRefs.isEmpty())
{
RecordsManagementActionResult result = this.rmActionService.executeRecordsManagementAction(actionName, actionParams);
if (result.getValue() != null)
{
model.put("result", result.getValue().toString());
}
}
else
{
Map<NodeRef, RecordsManagementActionResult> resultMap = this.rmActionService.executeRecordsManagementAction(targetNodeRefs, actionName, actionParams);
Map<String, String> results = new HashMap<String, String>(resultMap.size());
for (NodeRef nodeRef : resultMap.keySet())
{
Object value = resultMap.get(nodeRef).getValue();
if (value != null)
{
results.put(nodeRef.toString(), resultMap.get(nodeRef).getValue().toString());
}
}
model.put("results", results);
}
model.put("message", "Successfully queued action [" + actionName + "] on " + targetNodeRefsString.toString());
return model;
}
}

View File

@@ -90,6 +90,12 @@ public class RmEventPut extends DeclarativeWebScript
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No event display label provided.");
}
if (rmEventService.existsEventDisplayLabel(eventDisplayLabel))
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
"Cannot edit event. The event display label '"
+ eventDisplayLabel + "' already exists.");
}
String eventType = null;
if (json.has("eventType") == true)

View File

@@ -90,6 +90,12 @@ public class RmEventsPost extends DeclarativeWebScript
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST, "No event display label provided.");
}
if (rmEventService.existsEventDisplayLabel(eventDisplayLabel))
{
throw new WebScriptException(Status.STATUS_BAD_REQUEST,
"Cannot create event. The event display label '"
+ eventDisplayLabel + "' already exists.");
}
String eventType = null;
if (json.has("eventType") == true)

View File

@@ -282,12 +282,12 @@ public class RMSearchGet extends DeclarativeWebScript
relPath.append(getName());
try
{
this.browseUrl = "documentlibrary?path=" + URLEncoder.encode(relPath.toString(), "UTF-8");
this.browseUrl = "documentlibrary?path=" + URLEncoder.encode(relPath.toString(), "UTF-8").replaceAll("\\+","%20");
}
catch (UnsupportedEncodingException e)
{
throw new AlfrescoRuntimeException("Could not process search results.", e);
}
}
}
}
else

View File

@@ -53,6 +53,9 @@ public class RMMetaDataGet extends DeclarativeWebScript
/** NodeRef pattern */
private static final Pattern nodeRefPattern = Pattern.compile(".+://.+/.+");
/** QName pattern */
private static final Pattern qnamePattern = Pattern.compile(".+:[^=,]+");
/** Records management service */
private RecordsManagementService rmService;
@@ -103,14 +106,18 @@ public class RMMetaDataGet extends DeclarativeWebScript
if (nodeRef == null || nodeRef.length() == 0)
{
String type = req.getParameter(PARAM_TYPE);
if (type != null && type.length() != 0)
if (type != null && type.length() != 0 && type.indexOf(':') != -1)
{
QName qname = QName.createQName(type, namespaceService);
FilePlanComponentKind kind = rmService.getFilePlanComponentKindFromType(qname);
if (kind != null)
{
result = kind.toString();
}
Matcher m = qnamePattern.matcher(type);
if (m.matches() == true)
{
QName qname = QName.createQName(type, namespaceService);
FilePlanComponentKind kind = rmService.getFilePlanComponentKindFromType(qname);
if (kind != null)
{
result = kind.toString();
}
}
}
}
else

View File

@@ -332,7 +332,48 @@ public class RecordsManagementSecurityServiceImpl implements RecordsManagementSe
*/
public void onCreateRMContainer(ChildAssociationRef childAssocRef)
{
setUpPermissions(childAssocRef.getChildRef());
final NodeRef recordCategory = childAssocRef.getChildRef();
setUpPermissions(recordCategory);
// Pull any permissions found on the parent (ie the record category)
final NodeRef parentNodeRef = childAssocRef.getParentRef();
if (parentNodeRef != null && nodeService.exists(parentNodeRef) == true)
{
AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Object>()
{
public Object doWork()
{
boolean fillingOnly = false;
if (recordsManagementService.isFilePlan(parentNodeRef) == true)
{
fillingOnly = true;
}
// since this is not a root category, inherit from parent
Set<AccessPermission> perms = permissionService.getAllSetPermissions(parentNodeRef);
for (AccessPermission perm : perms)
{
if (fillingOnly == false ||
RMPermissionModel.FILING.equals(perm.getPermission()) == true)
{
AccessStatus accessStatus = perm.getAccessStatus();
boolean allow = false;
if (AccessStatus.ALLOWED.equals(accessStatus) == true)
{
allow = true;
}
permissionService.setPermission(
recordCategory,
perm.getAuthority(),
perm.getPermission(),
allow);
}
}
return null;
}
}, AuthenticationUtil.getSystemUserName());
}
}
/**