mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-30 18:15:39 +00:00
42174: ALF-14721: Merged PATCHES/V4.0.2 to V4.1-BUG-FIX 41782: ALF-15751: Merged DEV to V4.0.2 (4.0.2.14) 41704: ALF-15751: CLONE - Version History presents versions in wrong order 'VersionHistoryImpl' now sorts versions by node DB id because version with greater version number can't have id which is lesser than id of version with lesser version number. Additionally, this approach should be quicker than sorting by 'Modification date' and 'Version number' label. << Did not merge unit test, which was doing things with version branches that we don't normally support >> 42179: ALF-16149: Merged PATCHES/V4.0.1 to V4.1-BUG-FIX 41995: 41911: ALF-14127 User search retrieves all users from the DB regardless of search criteria - PeopleServiceImpl.getPeople(...) now calls a new method nonCannedGetPeopleQuery(...) rather than using the canned query which is slow with large numbers of users. 42011: 41911: ALF-14127 User search retrieves all users from the DB regardless of search criteria - Avoid NPE on params 42059: 41911: ALF-14127 User search retrieves all users from the DB regardless of search criteria - Ignore case broke one of the unit tests (now excluded from nonCanned version) 42188: French installer corrections from Gloria 42192: ALF-15906 - Share UI does not show the 'edit online' button for Visio documents 42195: Refactor of imapSpacesTemplates.acp into imapSpacesTemplates.xml and exploded content. This work is a necessary precursor to the fix for ALF-15803, which will add new localisations. 42220: Fix for ALF-16138. AbstractLinksWebScript doesn't cope with Links from deleted users. 42233: Fix for ALF-16164 Cloud monitoring of SOLR is CPU intensive due to its repeated use of the SOLR stats page and related CLOUD-760 Cloud monitoring of SOLR is CPU intensive due to its repeated use of the SOLR stats page 42259: Fix to issue where multiple concurrent writes to same user preferences would cause exception to appear in Share when changing between old document library views and new views provided by a module. 42266: ALF-16154 - IE9: script error when click on workflow from document details page 42268: Fix for ALF-11152 - License Usage information always shows 0 users 42269: Fix for ALF-15211 - TinyMCE corrupting hyperlinks 42275: ALF-15993: alfresco log not removed if uninstalled on a different day - Fix from Bitrock - Also fixed for awe and share logs 42289: Merged DEV to V4.1-BUG-FIX 42276: ALF-1907: Check out rule is active for spaces - Unit test for checkout via action executer Fixed line endings and split asserts 42292: ALF-15937: updated the Javadoc of the checkin method to be in sync with what's in doc.alfresco.com 42307: Fix handling of syncmodeconfig=OFF when running 4.1.X locally without doing full enterprise build. 42308: Fix ALF-13968: Share DocLib sorting mixes files and folders - implicitly sort folders before files (~ pre 4.x) then selected sort option, such as name - also allow Alf-specific option with CMIS getChildren (eg. "orderBy=cmis:baseTypeId DESC,cmis:name ASC") 42310: Merged BRANCHES/DEV/BELARUS/V4.1-BUG-FIX-2012_09_24 to BRANCHES/DEV/V4.1-BUG-FIX: 42309: ALF-15707 (ALF-14691) - Any custom aspect or type (including ootb workflow) is not available for API calls like api/classes/<type or aspect> 42338: Merged BRANCHES/DEV/V3.4-BUG-FIX to BRANCHES/DEV/V4.1-BUG-FIX 42337: Fix for ALF-14764 - Moving a folder removes non-site Group permissions set, resets 'Inherit permissions flag' 42339: Fix for ALF-15151 - Selected group is illegible(black) in Admin console if High contrast theme is selected 42342: ALF-10362: Activities fail to log "name" changes with more than 1024 chars (eg. via Share "Create Content" form) - part I - fix Share config so that default "Create Content" form restricts to 255 chars as per other form config (eg. Edit Properties, inline rename, ...) 42353: Merged V3.4-BUG-FIX to V4.1-BUG-FIX (RECORD ONLY) 42281: Fix for ALF-9946 Need a supported solution for switching off content indexing (FTS) -> merge only to 4.1-BUG-FIX - remove references to isIndexed property which was removed in the back port 42360: ALF-16146: Fixed QName of the data list item type. 42361: ALF-10362: Activities fail to log "name" changes with more than 1024 chars (eg. via Share "Create Content" form) - part II - belts-and-braces (with unit test) 42362: Merged DEV to V4.1-BUG-FIX 42336: ALF-16160: office 2010 doesn't notify users of files being locked when using sharepoint protocol MS Office (if we enabled notification about document unlocking) periodically sends PROPFIND requests to get info about active locks. This code makes PROPFIND be able to send an info about locks for the MS Office 2010 client if a document was locked for edit offline. 42363: ALF-16213: renaming versioned file results in file being deleted. 42368: Record only merge V3.4-BUG-FIX (3.4.12) to V4.1-BUG-FIX (4.1.2) 42367: Merge V3.4 (3.4.11) to V3.4-BUG-FIX (3.4.12) 42366: ALF-16137: Merge V4.1 (4.1.1) to V3.4 (3.4.11) 42048: ALF-16005 Could not transform file size of 0 kb - Turns out that it was only doc ppt and xls zero byte files that had the problem. - Reverting part of revision 6473 (release 2.1 2007) AR-1251 (Version error when saving new content via CIFS) Dave W tells me that this is no longer an issue due to other changes 42381: Fixed ALF-16218: Solr GetNodes return status is 500 for Postgresql - Read-only webscript was calling through to "qnameDAO.getOrCreateQName", which could fail if the QName does not exist. Issue is not critical because it will start working once the QName gets created. 42384: ALF-15868 RepoTransferReceiverImplTest failing on MySQL Checked in a refactor of the transaction handling in the test to remove the suspicion that the current failure iis somehow a test error. (Still fails on MySQL) 42395: ALF-14353: Deploy pom files with dependencies to the Maven repo 42405: ALF-15986: Upgrade to Bitrock 8.5.0 in order to improve font scaling and adaptive layout with Gtk - Helps I18N 42407: Fixed 'state leak' from ActivityServiceImplTest 42408: Merged BRANCHES/DEV/FEATURES/CLOUD1_CLOUDSYNC to BRANCHES/DEV/V4.1-BUG-FIX: 42389: CLOUD-796: handle unknown custom content/folder type 42396: CLOUD-796: handle unknown custom content/folder type 42397: Merged BRANCHES/DEV/V4.1-BUG-FIX to BRANCHES/DEV/FEATURES/CLOUD1_CLOUDSYNC: 41858: ALF-14444 - CloudSync: Ensure unknown properties when synced to Cloud are ignored properly 42406: CLOUD-796: handle unknown custom content/folder type 42409: CloudSync: CLOUD-796 / ALF-16226 - hide sync props in forms (eg. edit props) for sync'ed custom content type 42419: Fixes: ALF-11096 - Ensures event edit button is disabled if the event came from Outlook (this is because VTI connector is one directional & changes can't be pushed back). 42420: Fix for ALF-16003 - Sync mode is incorrectly cached as off if repo hasn't started when the check is made. 42430: More refactoring of RepoTransferReceiverImplTest. 42441: Merged V4.1-BUG-FIX to HEAD 42440: ALF-16247: Thumbnails not rendering for PDFs with standard fonts - Because GS_LIB wasn't set on Linux and OSX 42452: Fix for ALF-15450 Share Lucene tool in admin console works incorrectly 42457: ALF-14347: Document workspace is incorrectly deleted - Check returned status code from delete method before continuing to delete components. 42458: ALF-15700: 'Imap Attachments' folder is not localized. - Added spaces.imap_attachments.childname property that allows the attachments folder to be localized 42459: ALF-16103: No easy way to specify a timeout for LDAP connections - Added ldap-authentication/ldap-ad-authentication property (ldap.authentication.java.naming.read.timeout) to configure the com.sun.jndi.ldap.read.timeout for the initialDirContextEnvironment. - ldap.authentication.java.naming.read.timeout property is configured in milliseconds. Defaults to zero (infinite) which is the current behavior. 42467: Fix for ALF-16275 SOLR include configuration to avoid indexing content - done and fixed all configuration to be treated as Java properties 42472: ALF-16175: Merged PATCHES/V4.0.1 to V4.1-BUG-FIX (Record Only) 42448: ALF-16096: Repo corruption in MT - clean-up assistance requested - Changed RepositoryAuthenticationDAO.getUserFolderLocation() to use getCurrentUserDomain() for its cache key. 42473: ALF-14838 ALF-14839 Deploy Maven artifacts containing the config and the test-resources, using these as classifiers 42475: ALF-14180 - CIFS - Cluster - doc and docx files are opened in read-only mode via MS Office 2003 and 2010 appropriately missed from check in 34544 42477: ALF-5051: Define ThumbnailDefinition Beans Outside of ThumbnailRegistry Bean - Reverted imgpreview to enterprise 4.1 size of 480 42504: Reverse Merge 42458 ALF-15700: 'Imap Attachments' folder is not localized. Causes unit test failures. 42517: ALF-15700: Restoring duff revision 42458 so that we can finish the job and fix it 42518: ALF-15700: Corrected internationalization of IMAP Attachments folder - RepositoryFolderConfigBean must look up paths by QName to be immune to localization and backward compatible - Must throw an error rather than using the store root if the path contains unresolved placeholders! - QName of attachments folder must remain "cm:Imap Attachments" because that's what it always was! 42528: ALF-16282: Hybrid Sync: folder unsync - sub-children still have sync indicators - fix typo fallout from ALF-15420 (r40782) + add unit/regress test 42529: ALF-16231: Corrected Imap Attachments English string 42530: ALF-14838 ALF-14839 Fix enterprise artifacts + deploy jars instead of zips 42531: ALF-14770 Cut / Paste triggers folder rules - Needed to disable rules on nodes being MOVED. - Added extra check to RuleTypeImpl when working out if a rule was disabled so that debug would not be misleading. No impact on logic, as RuleService does the same check later and discards the rules. 42546: ALF-15737 Audit trail does not show user login events - Also does not show any failed login events 42568: ALF-16077 CLONE: Incorrect activities if you try to add/edit/remove comment for document (if this document contains any title) The original activity feed comment code would include the title of a document, folder or blog rather than its name if it was available. - name is a mandatory field for a document and folder. - title is a mandatory field for a blog entry and its name may not be set via Share. Changing activity feed comment code so that the: - name is always used for documents and folders - title is always used for blogs 42571: ALF-14838 ALF-14839 Deploy config and test-resoruces artifacts in the same batch as the main artifact, otherwise they get different snapshot versions 42582: ALF-16255: CopiedFromAspectPatch fails on rules copied with a folder - Checked to make sure that cm:copiedfrom target is a cm:object before attempting a cm:original association. - Remove cm:copiedfrom aspect from source if cm:copiedfrom target is not a cm:object. 42593: ALF-16255: CopiedFromAspectPatch fails on rules copied with a folder - Corrections to log message and formatting. 42605: ALF-16231: Fixed broken IMAP unit tests 42612: Further fix for ALF-16164 Cloud monitoring of SOLR is CPU intensive due to its repeated use of the SOLR stats page - protect from dodgey JSON output 42624: ALF-14353: switch groupId to org.alfresco.enterprise, to be in sync with actual Maven deployment 42657: Fix for ALF-16359 Fix SOLR logging in production and other environments - configure in log4j-solr.properties anywhere on the solr web app classpath ... 42671: ALF-14353: fix facebook api dependency 42679: Merged V3.4-BUG-FIX to V4.1-BUG-FIX 42172: ALF-15262: Correct handling of linked rule deletion - When the last rule is removed from a folder and the ASPECT_RULES aspect is removed from its parent, we must cascade this removal to its secondary parents 42173: ALF-14400: Only site members can Edit Online (sharepoint) although the site is public and permissions allow editing for everybody - Rationalized the fix provided by Alex Malinovsky - Don't bother checking site memberships - let ACLs handle that and just check for permission to read the document 42182: Incremented version revision for 3.4.12 42243: ALF-15262: Further correction by Dmitry: use beforeRemoveAspect because beforeDeleteChildAssociation is not invoked on deletion of primary child associations 42278: ALF-12999: Correction by Alex M 42586: BDE-101: make .MD5 files suitable for easy check with md5sum -c 42627: Merged DEV to V3.4-BUG-FIX 42537: ALF-16139: Impossible to connect to CMIS via AtomPub and Web Services Activation libraries (including all Geronimo versions) have been removed because of a conflict with libraries in JBoss CXF WS installation. Also, 'javax.activation' is part of the JDK 1.6 (http://docs.oracle.com/javase/6/docs/api/javax/activation/DataHandler.html) 42677: Merged V3.4 to V3.4-BUG-FIX 42380: ALF-16220: Merged V4.1-BUG-FIX to V3.4 40590: ALF-15318: It was possible for a user with a disabled / expired account to log in via NTLM/SSO 40663: Merged DEV to V4.1-BUG-FIX 40661: ALF-15318 (part 2): It's possible to log in by disabled user (NTLM with SSO in a clustered env) The onValidateFailed() methods were moved to BaseSSOAuthenticationFilter to response with a 401 for a disabled user. 42556: ALF-15077: Site creation in Share is very very slow with over 15000 sites - Probably knock-on impact from us versioning secondary associations properly - Found old way of locating a leaf document to be ineffective as it would blow the caches (find all documents with the correct ID, then filter out the containers) - Effect was magnified when admin user was previously accessed via the explorer client and thus had an app:configurations child node, thus making admin a container and requiring its paths (e.g. zillions of nested group memberships) to be indexed - Instead, we have a new LEAFID field on leaves that we can use to efficiently locate a node to delete without hitting zillions of containers - Left backward compatible code to avoid requiring a full reindex 42557: ALF-16202: Merged V4.1-BUG-FIX to V3.4 40937: ALF-15702, ALF-15669: mmt-dependencies was messing up the SDK classpath 42566: ALF-15077: Correction to category-handling logic in container generation to fix failing unit tests 42608: Merged DEV to V3.4 42543: ALF-16248 : IE specific: It's impossible to create any event due to script error Correction for the fix for ALF-13623 to support IE8, also added clearing of 'allday' checkbox. 42622: ALF-16339: Group names incorrect in (non-site) "Manage Permissions" page - Site name was being used as the display name of all site groups! 42632: ALF-16354: Merged PATCHES/V3.4.6 to V3.4-BUG-FIX 42521: ALF-16231: Corrected LockUtils.isLockedOrReadOnly to properly handle the LOCK_EXPIRED status - Also fixed CheckOutCheckInService.checkout() to respect LOCK_EXPIRED but still disallow overwrite of unexpired WRITE_LOCKS 42522: ALF-16231: Further improvements - Renamed to isLockedAndReadOnly because that's what it means! 42644: ALF-16298: Cannot install RM amps on 4.1.1 - Passed command line arguments from shell script to mmt utility 42656: ALF-16298: Correction to DOS argument concatenation to allow multiple parameters separated by space 42664: ALF-16358: NPE detected during benchmark test. - Guarding against this in LeafScorer 42665: ALF-16360: Merged HEAD to V3.4 42440: ALF-16247: Thumbnails not rendering for PDFs with standard fonts - Because GS_LIB wasn't set on Linux and OSX 42447: ALF-16247: Thumbnails not rendering for PDFs with standard fonts - Fixes by Bitrock 42678: Merged V3.4 to V3.4-BUG-FIX (RECORD ONLY) 42244: Merged V3.4-BUG-FIX to V3.4 42172: ALF-15262: Correct handling of linked rule deletion - When the last rule is removed from a folder and the ASPECT_RULES aspect is removed from its parent, we must cascade this removal to its secondary parents 42243: ALF-15262: Further correction by Dmitry: use beforeRemoveAspect because beforeDeleteChildAssociation is not invoked on deletion of primary child associations 42279: Merged V3.4-BUG-FIX to V3.4 42278: ALF-12999: Correction by Alex M 42282: Merged V3.4-BUG-FIX to V3.4 42281: Fix for ALF-9946 Need a supported solution for switching off content indexing (FTS) -> merge only to 4.1-BUG-FIX - remove references to isIndexed property which was removed in the back port git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@42683 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
1548 lines
57 KiB
Java
1548 lines
57 KiB
Java
/*
|
|
* Copyright (C) 2005-2012 Alfresco Software Limited.
|
|
*
|
|
* This file is part of Alfresco
|
|
*
|
|
* 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/>.
|
|
*/
|
|
package org.alfresco.repo.rule;
|
|
|
|
import java.io.Serializable;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Set;
|
|
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.action.ActionModel;
|
|
import org.alfresco.repo.action.RuntimeActionService;
|
|
import org.alfresco.repo.action.executer.CompositeActionExecuter;
|
|
import org.alfresco.repo.action.executer.MailActionExecuter;
|
|
import org.alfresco.repo.cache.NullCache;
|
|
import org.alfresco.repo.cache.SimpleCache;
|
|
import org.alfresco.repo.node.NodeServicePolicies;
|
|
import org.alfresco.repo.policy.JavaBehaviour;
|
|
import org.alfresco.repo.policy.PolicyComponent;
|
|
import org.alfresco.repo.security.authentication.AuthenticationUtil;
|
|
import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
|
|
import org.alfresco.repo.transaction.TransactionListener;
|
|
import org.alfresco.repo.transaction.TransactionalResourceHelper;
|
|
import org.alfresco.service.cmr.action.Action;
|
|
import org.alfresco.service.cmr.action.ActionService;
|
|
import org.alfresco.service.cmr.action.ActionServiceException;
|
|
import org.alfresco.service.cmr.action.CompositeAction;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
|
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
|
import org.alfresco.service.cmr.repository.CopyService;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
|
|
import org.alfresco.service.cmr.rule.Rule;
|
|
import org.alfresco.service.cmr.rule.RuleService;
|
|
import org.alfresco.service.cmr.rule.RuleServiceException;
|
|
import org.alfresco.service.cmr.rule.RuleType;
|
|
import org.alfresco.service.cmr.security.AccessStatus;
|
|
import org.alfresco.service.cmr.security.PermissionService;
|
|
import org.alfresco.service.namespace.QName;
|
|
import org.alfresco.service.namespace.RegexQNamePattern;
|
|
import org.alfresco.util.GUID;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.springframework.extensions.surf.util.ParameterCheck;
|
|
|
|
/**
|
|
* Rule service implementation.
|
|
* <p>
|
|
* This service automatically binds to the transaction flush hooks. It will
|
|
* therefore participate in any flushes that occur during the transaction as
|
|
* well.
|
|
*
|
|
* @author Roy Wetherall
|
|
*/
|
|
public class RuleServiceImpl
|
|
implements RuleService, RuntimeRuleService,
|
|
NodeServicePolicies.OnCreateChildAssociationPolicy,
|
|
NodeServicePolicies.OnCreateNodePolicy,
|
|
NodeServicePolicies.OnUpdateNodePolicy,
|
|
NodeServicePolicies.OnAddAspectPolicy
|
|
{
|
|
/** key against which to store disabled rule types in the current txn */
|
|
private static final String KEY_DISABLED_RULE_TYPES = "RuleServiceImpl.disabledRuleTypes";
|
|
|
|
/** key against which to store disabled rule nodes in the current txn */
|
|
private static final String KEY_DISABLED_RULE_NODES = "RuleServiceImpl.disabledRuleNodes";
|
|
|
|
/** key against which to store rules pending on the current transaction */
|
|
private static final String KEY_RULES_PENDING = "RuleServiceImpl.PendingRules";
|
|
|
|
/** key against which to store executed rules on the current transaction */
|
|
private static final String KEY_RULES_EXECUTED = "RuleServiceImpl.ExecutedRules";
|
|
|
|
/** qname of assoc to rules */
|
|
private String ASSOC_NAME_RULES_PREFIX = "rules";
|
|
private RegexQNamePattern ASSOC_NAME_RULES_REGEX = new RegexQNamePattern(RuleModel.RULE_MODEL_URI, "^" + ASSOC_NAME_RULES_PREFIX + ".*");
|
|
|
|
private static final Set<QName> IGNORE_PARENT_ASSOC_TYPES = new HashSet<QName>(7);
|
|
static
|
|
{
|
|
IGNORE_PARENT_ASSOC_TYPES.add(ContentModel.ASSOC_MEMBER);
|
|
IGNORE_PARENT_ASSOC_TYPES.add(ContentModel.ASSOC_IN_ZONE);
|
|
}
|
|
|
|
private static Log logger = LogFactory.getLog(RuleServiceImpl.class);
|
|
|
|
private NodeService nodeService;
|
|
private NodeService runtimeNodeService;
|
|
private CopyService copyService;
|
|
private ActionService actionService;
|
|
private DictionaryService dictionaryService;
|
|
private PolicyComponent policyComponent;
|
|
private PermissionService permissionService;
|
|
|
|
/**
|
|
* The action service implementation which we need for some things.
|
|
*/
|
|
private RuntimeActionService runtimeActionService;
|
|
|
|
/**
|
|
* Cache of raw rules (not inherited or interpreted) for a given node
|
|
*/
|
|
private SimpleCache<NodeRef, List<Rule>> nodeRulesCache;
|
|
|
|
/**
|
|
* List of disabled rules. Any rules that appear in this list will not be added to the pending list and therefore
|
|
* not fired.
|
|
*/
|
|
private Set<Rule> disabledRules = new HashSet<Rule>(5);
|
|
|
|
/**
|
|
* All the rule type currently registered
|
|
*/
|
|
private Map<String, RuleType> ruleTypes = new HashMap<String, RuleType>();
|
|
|
|
/**
|
|
* The rule transaction listener
|
|
*/
|
|
private TransactionListener ruleTransactionListener = new RuleTransactionListener(this);
|
|
|
|
/**
|
|
* Indicates whether the rules are disabled for the current thread
|
|
*/
|
|
private ThreadLocal<Boolean> rulesDisabled = new ThreadLocal<Boolean>();
|
|
|
|
/**
|
|
* Global flag that indicates whether the
|
|
*/
|
|
private boolean globalRulesDisabled = false;
|
|
|
|
/**
|
|
* Set the permission-safe node service
|
|
*/
|
|
public void setNodeService(NodeService nodeService)
|
|
{
|
|
this.nodeService = nodeService;
|
|
}
|
|
|
|
/**
|
|
* Set the direct node service
|
|
*/
|
|
public void setRuntimeNodeService(NodeService runtimeNodeService)
|
|
{
|
|
this.runtimeNodeService = runtimeNodeService;
|
|
}
|
|
|
|
/**
|
|
* Set the service for locating copied nodes' originals
|
|
*/
|
|
public void setCopyService(CopyService copyService)
|
|
{
|
|
this.copyService = copyService;
|
|
}
|
|
|
|
/**
|
|
* Set the action service
|
|
*/
|
|
public void setActionService(ActionService actionService)
|
|
{
|
|
this.actionService = actionService;
|
|
}
|
|
|
|
/**
|
|
* Set the runtime action service
|
|
*/
|
|
public void setRuntimeActionService(RuntimeActionService runtimeActionService)
|
|
{
|
|
this.runtimeActionService = runtimeActionService;
|
|
}
|
|
|
|
/**
|
|
* Set the dictionary service
|
|
*/
|
|
public void setDictionaryService(DictionaryService dictionaryService)
|
|
{
|
|
this.dictionaryService = dictionaryService;
|
|
}
|
|
|
|
/**
|
|
* Set the policy component to listen for various events
|
|
*/
|
|
public void setPolicyComponent(PolicyComponent policyComponent)
|
|
{
|
|
this.policyComponent = policyComponent;
|
|
}
|
|
|
|
/**
|
|
* Set the permission service
|
|
*/
|
|
public void setPermissionService(PermissionService permissionService)
|
|
{
|
|
this.permissionService = permissionService;
|
|
}
|
|
|
|
/**
|
|
* Set the cache to hold node's individual rules. This cache <b>must not be shared</b>
|
|
* across transactions.
|
|
*
|
|
* @param nodeRulesCache a cache of raw rules contained on a node
|
|
*
|
|
* @see NullCache
|
|
*/
|
|
public void setNodeRulesCache(SimpleCache<NodeRef, List<Rule>> nodeRulesCache)
|
|
{
|
|
this.nodeRulesCache = nodeRulesCache;
|
|
}
|
|
|
|
/**
|
|
* Set the global rules disabled flag
|
|
*
|
|
* @param rulesDisabled true to disable allr ules, false otherwise
|
|
*/
|
|
public void setRulesDisabled(boolean rulesDisabled)
|
|
{
|
|
this.globalRulesDisabled = rulesDisabled;
|
|
}
|
|
|
|
/**
|
|
* Registers to listen for events of interest. For instance, the creation or deletion of a rule folder
|
|
* will affect the caching of rules.
|
|
*/
|
|
public void init()
|
|
{
|
|
policyComponent.bindAssociationBehaviour(
|
|
NodeServicePolicies.OnCreateChildAssociationPolicy.QNAME,
|
|
RuleModel.ASPECT_RULES,
|
|
RuleModel.ASSOC_RULE_FOLDER,
|
|
new JavaBehaviour(this, "onCreateChildAssociation"));
|
|
policyComponent.bindClassBehaviour(
|
|
NodeServicePolicies.OnAddAspectPolicy.QNAME,
|
|
RuleModel.ASPECT_RULES,
|
|
new JavaBehaviour(this, "onAddAspect"));
|
|
policyComponent.bindClassBehaviour(
|
|
NodeServicePolicies.OnUpdateNodePolicy.QNAME,
|
|
RuleModel.ASPECT_RULES,
|
|
new JavaBehaviour(this, "onUpdateNode"));
|
|
policyComponent.bindClassBehaviour(
|
|
NodeServicePolicies.OnCreateNodePolicy.QNAME,
|
|
RuleModel.TYPE_RULE,
|
|
new JavaBehaviour(this, "onCreateNode"));
|
|
policyComponent.bindClassBehaviour(
|
|
NodeServicePolicies.OnUpdateNodePolicy.QNAME,
|
|
RuleModel.TYPE_RULE,
|
|
new JavaBehaviour(this, "onUpdateNode"));
|
|
policyComponent.bindClassBehaviour(
|
|
NodeServicePolicies.OnCreateNodePolicy.QNAME,
|
|
ActionModel.TYPE_ACTION_BASE,
|
|
new JavaBehaviour(this, "onCreateNode"));
|
|
policyComponent.bindClassBehaviour(
|
|
NodeServicePolicies.OnUpdateNodePolicy.QNAME,
|
|
ActionModel.TYPE_ACTION_BASE,
|
|
new JavaBehaviour(this, "onUpdateNode"));
|
|
policyComponent.bindClassBehaviour(
|
|
NodeServicePolicies.OnCreateNodePolicy.QNAME,
|
|
ActionModel.TYPE_ACTION_PARAMETER,
|
|
new JavaBehaviour(this, "onCreateNode"));
|
|
policyComponent.bindClassBehaviour(
|
|
NodeServicePolicies.OnUpdateNodePolicy.QNAME,
|
|
ActionModel.TYPE_ACTION_PARAMETER,
|
|
new JavaBehaviour(this, "onUpdateNode"));
|
|
}
|
|
|
|
/**
|
|
* Cache invalidation
|
|
*/
|
|
@Override
|
|
public void onCreateChildAssociation(ChildAssociationRef childAssocRef, boolean isNewNode)
|
|
{
|
|
nodeRulesCache.clear();
|
|
}
|
|
|
|
/**
|
|
* Cache invalidation
|
|
*/
|
|
public void onUpdateNode(NodeRef nodeRef)
|
|
{
|
|
nodeRulesCache.clear();
|
|
}
|
|
|
|
/**
|
|
* Cache invalidation
|
|
*/
|
|
public void onCreateNode(ChildAssociationRef childAssocRef)
|
|
{
|
|
nodeRulesCache.clear();
|
|
}
|
|
|
|
/**
|
|
* Cache invalidation
|
|
*/
|
|
public void onAddAspect(NodeRef nodeRef, QName aspectTypeQName)
|
|
{
|
|
nodeRulesCache.clear();
|
|
}
|
|
|
|
protected NodeRef getSavedRuleFolderRef(NodeRef nodeRef)
|
|
{
|
|
NodeRef result = null;
|
|
ChildAssociationRef assoc = getSavedRuleFolderAssoc(nodeRef);
|
|
if (assoc != null)
|
|
{
|
|
result = assoc.getChildRef();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Gets the saved rule folder reference
|
|
*
|
|
* @param nodeRef the node reference
|
|
* @return the node reference
|
|
*/
|
|
public ChildAssociationRef getSavedRuleFolderAssoc(NodeRef nodeRef)
|
|
{
|
|
ChildAssociationRef result = null;
|
|
|
|
List<ChildAssociationRef> assocs = this.runtimeNodeService.getChildAssocs(
|
|
nodeRef,
|
|
RuleModel.ASSOC_RULE_FOLDER,
|
|
RuleModel.ASSOC_RULE_FOLDER);
|
|
if (assocs.size() > 1)
|
|
{
|
|
throw new ActionServiceException("There is more than one rule folder, which is invalid.");
|
|
}
|
|
else if (assocs.size() == 1)
|
|
{
|
|
result = assocs.get(0);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public List<RuleType> getRuleTypes()
|
|
{
|
|
return new ArrayList<RuleType>(this.ruleTypes.values());
|
|
}
|
|
|
|
@Override
|
|
public RuleType getRuleType(String name)
|
|
{
|
|
return this.ruleTypes.get(name);
|
|
}
|
|
|
|
@Override
|
|
public void enableRules()
|
|
{
|
|
this.rulesDisabled.remove();
|
|
}
|
|
|
|
@Override
|
|
public void disableRules()
|
|
{
|
|
this.rulesDisabled.set(Boolean.TRUE);
|
|
}
|
|
|
|
@Override
|
|
public boolean isEnabled()
|
|
{
|
|
return (this.globalRulesDisabled == false && this.rulesDisabled.get() == null);
|
|
}
|
|
|
|
@Override
|
|
public boolean rulesEnabled(NodeRef nodeRef)
|
|
{
|
|
Set<NodeRef> disabledRuleNodes = TransactionalResourceHelper.getSet(KEY_DISABLED_RULE_NODES);
|
|
return !disabledRuleNodes.contains(nodeRef);
|
|
}
|
|
|
|
@Override
|
|
public void disableRules(NodeRef nodeRef)
|
|
{
|
|
Set<NodeRef> disabledRuleNodes = TransactionalResourceHelper.getSet(KEY_DISABLED_RULE_NODES);
|
|
disabledRuleNodes.add(nodeRef);
|
|
}
|
|
|
|
@Override
|
|
public void enableRules(NodeRef nodeRef)
|
|
{
|
|
Set<NodeRef> disabledRuleNodes = TransactionalResourceHelper.getSet(KEY_DISABLED_RULE_NODES);
|
|
disabledRuleNodes.remove(nodeRef);
|
|
}
|
|
|
|
@Override
|
|
public void disableRule(Rule rule)
|
|
{
|
|
this.disabledRules.add(rule);
|
|
}
|
|
|
|
@Override
|
|
public void enableRule(Rule rule)
|
|
{
|
|
this.disabledRules.remove(rule);
|
|
}
|
|
|
|
@Override
|
|
public void disableRuleType(String ruleType)
|
|
{
|
|
Set<String> disabledRuleTypes = TransactionalResourceHelper.getSet(KEY_DISABLED_RULE_TYPES);
|
|
disabledRuleTypes.add(ruleType);
|
|
}
|
|
|
|
@Override
|
|
public void enableRuleType(String ruleType)
|
|
{
|
|
Set<String> disabledRuleTypes = TransactionalResourceHelper.getSet(KEY_DISABLED_RULE_TYPES);
|
|
disabledRuleTypes.remove(ruleType);
|
|
}
|
|
|
|
@Override
|
|
public boolean isRuleTypeEnabled(String ruleType)
|
|
{
|
|
Set<String> disabledRuleTypes = TransactionalResourceHelper.getSet(KEY_DISABLED_RULE_TYPES);
|
|
return !disabledRuleTypes.contains(ruleType);
|
|
}
|
|
|
|
@Override
|
|
public boolean hasRules(NodeRef nodeRef)
|
|
{
|
|
return getRules(nodeRef).size() != 0;
|
|
}
|
|
|
|
@Override
|
|
public List<Rule> getRules(NodeRef nodeRef)
|
|
{
|
|
return getRules(nodeRef, true, null);
|
|
}
|
|
|
|
@Override
|
|
public List<Rule> getRules(NodeRef nodeRef, boolean includeInherited)
|
|
{
|
|
return getRules(nodeRef, includeInherited, null);
|
|
}
|
|
|
|
@Override
|
|
public List<Rule> getRules(final NodeRef nodeRef, final boolean includeInherited, final String ruleTypeName)
|
|
{
|
|
//Run from system user: https://issues.alfresco.com/jira/browse/ALF-607
|
|
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<List<Rule>>()
|
|
{
|
|
|
|
public List<Rule> doWork() throws Exception
|
|
{
|
|
List<Rule> rules = new ArrayList<Rule>();
|
|
|
|
if (!runtimeNodeService.exists(nodeRef) || !checkNodeType(nodeRef))
|
|
{
|
|
// Node has gone or is not the correct type
|
|
return rules;
|
|
}
|
|
if (includeInherited == true && runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false)
|
|
{
|
|
// Get any inherited rules
|
|
for (Rule rule : getInheritedRules(nodeRef, ruleTypeName, null))
|
|
{
|
|
// Ensure rules are not duplicated in the list
|
|
if (rules.contains(rule) == false)
|
|
{
|
|
rules.add(rule);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Get the node's own rules and add them to the list
|
|
List<Rule> nodeRules = getRulesForNode(nodeRef);
|
|
for (Rule rule : nodeRules)
|
|
{
|
|
if ((rules.contains(rule) == false) && (ruleTypeName == null || rule.getRuleTypes().contains(ruleTypeName) == true))
|
|
{
|
|
rules.add(rule);
|
|
}
|
|
}
|
|
|
|
return rules;
|
|
}
|
|
}, AuthenticationUtil.getSystemUserName());
|
|
}
|
|
|
|
private List<Rule> getRulesForNode(NodeRef nodeRef)
|
|
{
|
|
// Extra check of CONSUMER permission was added to rule selection,
|
|
// to prevent Access Denied Exception due to the bug:
|
|
// https://issues.alfresco.com/browse/ETWOTWO-438
|
|
|
|
if (!runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) ||
|
|
permissionService.hasPermission(nodeRef, PermissionService.READ) != AccessStatus.ALLOWED)
|
|
{
|
|
// Doesn't have the aspect or the user doesn't have access
|
|
return Collections.emptyList();
|
|
}
|
|
List<Rule> nodeRules = nodeRulesCache.get(nodeRef);
|
|
if (nodeRules != null)
|
|
{
|
|
// We have already processed this node
|
|
return nodeRules;
|
|
}
|
|
// Not in the cache, so go and get the rules
|
|
nodeRules = new ArrayList<Rule>();
|
|
NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef);
|
|
if (ruleFolder != null)
|
|
{
|
|
// Get the rules for this node
|
|
List<ChildAssociationRef> ruleChildAssocRefs =
|
|
this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX);
|
|
for (ChildAssociationRef ruleChildAssocRef : ruleChildAssocRefs)
|
|
{
|
|
// Create the rule and add to the list
|
|
NodeRef ruleNodeRef = ruleChildAssocRef.getChildRef();
|
|
Rule rule = getRule(ruleNodeRef);
|
|
nodeRules.add(rule);
|
|
}
|
|
}
|
|
// Store this in the cache for later re-use
|
|
nodeRulesCache.put(nodeRef, nodeRules);
|
|
// Done
|
|
return nodeRules;
|
|
}
|
|
|
|
@Override
|
|
public int countRules(NodeRef nodeRef)
|
|
{
|
|
int ruleCount = 0;
|
|
|
|
if (this.runtimeNodeService.exists(nodeRef) == true && checkNodeType(nodeRef) == true)
|
|
{
|
|
if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true)
|
|
{
|
|
NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef);
|
|
if (ruleFolder != null)
|
|
{
|
|
// Get the rules for this node
|
|
List<ChildAssociationRef> ruleChildAssocRefs =
|
|
this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX);
|
|
|
|
ruleCount = ruleChildAssocRefs.size();
|
|
}
|
|
}
|
|
}
|
|
|
|
return ruleCount;
|
|
}
|
|
|
|
/**
|
|
* Looks at the type of the node and indicates whether the node can have rules associated with it
|
|
*
|
|
* @param nodeRef the node reference
|
|
* @return true if the node can have rule associated with it (inherited or otherwise)
|
|
*/
|
|
private boolean checkNodeType(NodeRef nodeRef)
|
|
{
|
|
boolean result = true;
|
|
|
|
QName nodeType = this.runtimeNodeService.getType(nodeRef);
|
|
if (this.dictionaryService.isSubClass(nodeType, ContentModel.TYPE_SYSTEM_FOLDER) == true ||
|
|
this.dictionaryService.isSubClass(nodeType, ActionModel.TYPE_ACTION) == true ||
|
|
this.dictionaryService.isSubClass(nodeType, ActionModel.TYPE_ACTION_CONDITION) == true ||
|
|
this.dictionaryService.isSubClass(nodeType, ActionModel.TYPE_ACTION_PARAMETER) == true)
|
|
{
|
|
result = false;
|
|
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("A node of type " + nodeType.toString() + " was checked and can not have rules.");
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Gets the inherited rules for a given node reference
|
|
*
|
|
* @param nodeRef the nodeRef
|
|
* @param ruleTypeName the rule type (null if all applicable)
|
|
* @return a list of inherited rules (empty if none)
|
|
*/
|
|
private List<Rule> getInheritedRules(NodeRef nodeRef, String ruleTypeName, Set<NodeRef> visitedNodeRefs)
|
|
{
|
|
List<Rule> inheritedRules = new ArrayList<Rule>();
|
|
|
|
if (this.runtimeNodeService.hasAspect(nodeRef, RuleModel.ASPECT_IGNORE_INHERITED_RULES) == false)
|
|
{
|
|
// Create the visited nodes set if it has not already been created
|
|
if (visitedNodeRefs == null)
|
|
{
|
|
visitedNodeRefs = new HashSet<NodeRef>();
|
|
}
|
|
|
|
// This check prevents stack over flow when we have a cyclic node graph
|
|
if (visitedNodeRefs.contains(nodeRef) == false)
|
|
{
|
|
visitedNodeRefs.add(nodeRef);
|
|
|
|
List<Rule> allInheritedRules = new ArrayList<Rule>();
|
|
List<ChildAssociationRef> parents = this.runtimeNodeService.getParentAssocs(nodeRef);
|
|
for (ChildAssociationRef parent : parents)
|
|
{
|
|
// We are not interested in following potentially massive person group membership trees!
|
|
if (IGNORE_PARENT_ASSOC_TYPES.contains(parent.getTypeQName()))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Add the inherited rule first
|
|
for (Rule rule : getInheritedRules(parent.getParentRef(), ruleTypeName, visitedNodeRefs))
|
|
{
|
|
// Ensure that we don't get any rule duplication (don't use a set cos we want to preserve order)
|
|
if (allInheritedRules.contains(rule) == false)
|
|
{
|
|
allInheritedRules.add(rule);
|
|
}
|
|
}
|
|
|
|
List<Rule> rules = getRules(parent.getParentRef(), false);
|
|
for (Rule rule : rules)
|
|
{
|
|
// Add is we hanvn't already added and it should be applied to the children
|
|
if (rule.isAppliedToChildren() == true && allInheritedRules.contains(rule) == false)
|
|
{
|
|
allInheritedRules.add(rule);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (ruleTypeName == null)
|
|
{
|
|
inheritedRules = allInheritedRules;
|
|
}
|
|
else
|
|
{
|
|
// Filter the rule list by rule type
|
|
for (Rule rule : allInheritedRules)
|
|
{
|
|
if (rule.getRuleTypes().contains(ruleTypeName) == true)
|
|
{
|
|
inheritedRules.add(rule);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return inheritedRules;
|
|
}
|
|
|
|
/**
|
|
* Create the rule object from the rule node reference
|
|
*
|
|
* @param ruleNodeRef the rule node reference
|
|
* @return the rule
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public Rule getRule(NodeRef ruleNodeRef)
|
|
{
|
|
// Get the rule properties
|
|
Map<QName, Serializable> props = this.runtimeNodeService.getProperties(ruleNodeRef);
|
|
|
|
// Create the rule
|
|
Rule rule = new Rule(ruleNodeRef);
|
|
|
|
// Set the title and description
|
|
String title = DefaultTypeConverter.INSTANCE.convert(String.class, props.get(ContentModel.PROP_TITLE));
|
|
String description = DefaultTypeConverter.INSTANCE.convert(String.class, props.get(ContentModel.PROP_DESCRIPTION));
|
|
rule.setTitle(title);
|
|
rule.setDescription(description);
|
|
|
|
// Set the rule types
|
|
rule.setRuleTypes((List<String>)props.get(RuleModel.PROP_RULE_TYPE));
|
|
|
|
// Set the applied to children value
|
|
boolean isAppliedToChildren = false;
|
|
Boolean value = (Boolean)props.get(RuleModel.PROP_APPLY_TO_CHILDREN);
|
|
if (value != null)
|
|
{
|
|
isAppliedToChildren = value.booleanValue();
|
|
}
|
|
rule.applyToChildren(isAppliedToChildren);
|
|
|
|
// Set the execute asynchronously value
|
|
boolean executeAsync = false;
|
|
Boolean value2 = (Boolean)props.get(RuleModel.PROP_EXECUTE_ASYNC);
|
|
if (value2 != null)
|
|
{
|
|
executeAsync = value2.booleanValue();
|
|
}
|
|
rule.setExecuteAsynchronously(executeAsync);
|
|
|
|
// Set the disabled value
|
|
boolean ruleDisabled = false;
|
|
Boolean value3 = (Boolean)props.get(RuleModel.PROP_DISABLED);
|
|
if (value3 != null)
|
|
{
|
|
ruleDisabled = value3.booleanValue();
|
|
}
|
|
rule.setRuleDisabled(ruleDisabled);
|
|
|
|
// Get the action node reference
|
|
List<ChildAssociationRef> actions = this.nodeService.getChildAssocs(ruleNodeRef, RuleModel.ASSOC_ACTION, RuleModel.ASSOC_ACTION);
|
|
if (actions.size() == 0)
|
|
{
|
|
throw new RuleServiceException("Rule exists without a specified action");
|
|
}
|
|
else if (actions.size() > 1)
|
|
{
|
|
throw new RuleServiceException("Rule exists with more than one specified action");
|
|
}
|
|
NodeRef actionNodeRef = actions.get(0).getChildRef();
|
|
|
|
// Here we need to create the action from the action node reference
|
|
Action action = runtimeActionService.createAction(actionNodeRef);
|
|
rule.setAction(action);
|
|
|
|
return rule;
|
|
}
|
|
|
|
@Override
|
|
public void saveRule(NodeRef nodeRef, Rule rule)
|
|
{
|
|
checkForLinkedRules(nodeRef);
|
|
|
|
if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED)
|
|
{
|
|
disableRules();
|
|
try
|
|
{
|
|
if (this.nodeService.exists(nodeRef) == false)
|
|
{
|
|
throw new RuleServiceException("The node does not exist.");
|
|
}
|
|
|
|
NodeRef ruleNodeRef = rule.getNodeRef();
|
|
if (ruleNodeRef == null)
|
|
{
|
|
if (this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == false)
|
|
{
|
|
// Add the actionable aspect
|
|
this.nodeService.addAspect(nodeRef, RuleModel.ASPECT_RULES, null);
|
|
}
|
|
|
|
// Create the action node
|
|
ruleNodeRef = this.nodeService.createNode(
|
|
getSavedRuleFolderRef(nodeRef),
|
|
ContentModel.ASSOC_CONTAINS,
|
|
QName.createQName(RuleModel.RULE_MODEL_URI, ASSOC_NAME_RULES_PREFIX + GUID.generate()),
|
|
RuleModel.TYPE_RULE).getChildRef();
|
|
|
|
// Set the rule node reference and the owning node reference
|
|
rule.setNodeRef(ruleNodeRef);
|
|
}
|
|
|
|
// Update the properties of the rule
|
|
this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_TITLE, rule.getTitle());
|
|
this.nodeService.setProperty(ruleNodeRef, ContentModel.PROP_DESCRIPTION, rule.getDescription());
|
|
this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_RULE_TYPE, (Serializable)rule.getRuleTypes());
|
|
this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_APPLY_TO_CHILDREN, rule.isAppliedToChildren());
|
|
this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_EXECUTE_ASYNC, rule.getExecuteAsynchronously());
|
|
this.nodeService.setProperty(ruleNodeRef, RuleModel.PROP_DISABLED, rule.getRuleDisabled());
|
|
|
|
// Save the rule's action
|
|
saveAction(ruleNodeRef, rule);
|
|
}
|
|
finally
|
|
{
|
|
enableRules();
|
|
// Drop the rules from the cache
|
|
nodeRulesCache.remove(nodeRef);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new RuleServiceException("Insufficient permissions to save a rule.");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void saveRule(NodeRef nodeRef, Rule rule, int index)
|
|
{
|
|
saveRule(nodeRef, rule);
|
|
setRulePosition(nodeRef, rule.getNodeRef(), index);
|
|
}
|
|
|
|
@Override
|
|
public void setRulePosition(NodeRef nodeRef, NodeRef ruleNodeRef, int index)
|
|
{
|
|
NodeRef ruleFolder = getSavedRuleFolderRef(nodeRef);
|
|
if (ruleFolder != null)
|
|
{
|
|
List<ChildAssociationRef> assocs = this.runtimeNodeService.getChildAssocs(ruleFolder, RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX);
|
|
List<ChildAssociationRef> orderedAssocs = new ArrayList<ChildAssociationRef>(assocs.size());
|
|
ChildAssociationRef movedAssoc = null;
|
|
for (ChildAssociationRef assoc : assocs)
|
|
{
|
|
NodeRef childNodeRef = assoc.getChildRef();
|
|
if (childNodeRef.equals(ruleNodeRef) == true)
|
|
{
|
|
movedAssoc = assoc;
|
|
}
|
|
else
|
|
{
|
|
orderedAssocs.add(assoc);
|
|
}
|
|
}
|
|
if (movedAssoc != null)
|
|
{
|
|
orderedAssocs.add(index, movedAssoc);
|
|
}
|
|
|
|
index = 0;
|
|
for (ChildAssociationRef orderedAssoc : orderedAssocs)
|
|
{
|
|
nodeService.setChildAssociationIndex(orderedAssoc, index);
|
|
index++;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void setRulePosition(NodeRef nodeRef, Rule rule, int index)
|
|
{
|
|
setRulePosition(nodeRef, rule.getNodeRef(), index);
|
|
}
|
|
|
|
/**
|
|
* Save the action related to the rule.
|
|
*
|
|
* @param ruleNodeRef the node reference representing the rule
|
|
* @param rule the rule
|
|
*/
|
|
private void saveAction(NodeRef ruleNodeRef, Rule rule)
|
|
{
|
|
// Get the action definition from the rule
|
|
Action action = rule.getAction();
|
|
if (action == null)
|
|
{
|
|
throw new RuleServiceException("An action must be specified when defining a rule.");
|
|
}
|
|
|
|
// Get the current action node reference
|
|
NodeRef actionNodeRef = null;
|
|
List<ChildAssociationRef> actions = this.nodeService.getChildAssocs(ruleNodeRef, RuleModel.ASSOC_ACTION, RuleModel.ASSOC_ACTION);
|
|
if (actions.size() == 1)
|
|
{
|
|
// We need to check that the action is the same
|
|
actionNodeRef = actions.get(0).getChildRef();
|
|
if (actionNodeRef.getId().equals(action.getId()) == false)
|
|
{
|
|
// Delete the old action
|
|
this.nodeService.deleteNode(actionNodeRef);
|
|
actionNodeRef = null;
|
|
}
|
|
}
|
|
else if (actions.size() > 1)
|
|
{
|
|
throw new RuleServiceException("The rule has become corrupt. More than one action is associated with the rule.");
|
|
}
|
|
|
|
// Create the new action node reference
|
|
if (actionNodeRef == null)
|
|
{
|
|
actionNodeRef = this.runtimeActionService.createActionNodeRef(action, ruleNodeRef, RuleModel.ASSOC_ACTION, RuleModel.ASSOC_ACTION);
|
|
}
|
|
|
|
// Update the action node
|
|
this.runtimeActionService.saveActionImpl(actionNodeRef, action);
|
|
|
|
}
|
|
|
|
@Override
|
|
public void removeRule(NodeRef nodeRef, Rule rule)
|
|
{
|
|
checkForLinkedRules(nodeRef);
|
|
|
|
if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED)
|
|
{
|
|
if (this.nodeService.exists(nodeRef) == true &&
|
|
this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true)
|
|
{
|
|
disableRules(nodeRef);
|
|
try
|
|
{
|
|
NodeRef ruleNodeRef = rule.getNodeRef();
|
|
if (ruleNodeRef != null)
|
|
{
|
|
this.nodeService.removeChild(getSavedRuleFolderRef(nodeRef), ruleNodeRef);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
enableRules(nodeRef);
|
|
}
|
|
|
|
// If this was the last rule on the node, remove the aspect
|
|
if(countRules(nodeRef) == 0)
|
|
{
|
|
// Since this is the last rule, unlink any linked rulesets
|
|
List<NodeRef> linkedFrom = getLinkedFromRuleNodes(nodeRef);
|
|
if (linkedFrom.isEmpty() == false)
|
|
{
|
|
for (NodeRef linkedFromNodeRef : linkedFrom)
|
|
{
|
|
NodeRef ruleFolder = getSavedRuleFolderAssoc(nodeRef).getChildRef();
|
|
nodeService.removeChild(linkedFromNodeRef, ruleFolder);
|
|
nodeService.removeAspect(linkedFromNodeRef, RuleModel.ASPECT_RULES);
|
|
}
|
|
}
|
|
|
|
this.nodeService.removeAspect(nodeRef, RuleModel.ASPECT_RULES);
|
|
}
|
|
}
|
|
// Drop the rules from the cache
|
|
nodeRulesCache.remove(nodeRef);
|
|
}
|
|
else
|
|
{
|
|
throw new RuleServiceException("Insufficient permissions to remove a rule.");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if rules are linked and throws an exception if they are.
|
|
*
|
|
* @param nodeRef node reference of rule node
|
|
*/
|
|
private void checkForLinkedRules(NodeRef nodeRef)
|
|
{
|
|
if (isLinkedToRuleNode(nodeRef)== true)
|
|
{
|
|
throw new RuleServiceException("Can not edit rules as they are linked to another rule set.");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void removeAllRules(NodeRef nodeRef)
|
|
{
|
|
checkForLinkedRules(nodeRef);
|
|
|
|
if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED)
|
|
{
|
|
if (this.nodeService.exists(nodeRef) == true &&
|
|
this.nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true)
|
|
{
|
|
NodeRef folder = getSavedRuleFolderRef(nodeRef);
|
|
if (folder != null)
|
|
{
|
|
List<ChildAssociationRef> ruleChildAssocs = this.nodeService.getChildAssocs(
|
|
folder,
|
|
RegexQNamePattern.MATCH_ALL, ASSOC_NAME_RULES_REGEX);
|
|
for (ChildAssociationRef ruleChildAssoc : ruleChildAssocs)
|
|
{
|
|
this.nodeService.removeChild(folder, ruleChildAssoc.getChildRef());
|
|
}
|
|
}
|
|
|
|
// As this was the last rule on the node, remove the aspect
|
|
this.nodeService.removeAspect(nodeRef, RuleModel.ASPECT_RULES);
|
|
}
|
|
// Drop the rules from the cache
|
|
nodeRulesCache.remove(nodeRef);
|
|
}
|
|
else
|
|
{
|
|
throw new RuleServiceException("Insufficient permissions to remove a rule.");
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule)
|
|
{
|
|
addRulePendingExecution(actionableNodeRef, actionedUponNodeRef, rule, false);
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("unchecked")
|
|
public void removeRulePendingExecution(NodeRef actionedUponNodeRef)
|
|
{
|
|
ParameterCheck.mandatory("actionedUponNodeRef", actionedUponNodeRef);
|
|
|
|
List<PendingRuleData> pendingRules = (List<PendingRuleData>) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING);
|
|
if (pendingRules != null)
|
|
{
|
|
boolean listUpdated = false;
|
|
List<PendingRuleData> temp = new ArrayList<PendingRuleData>(pendingRules);
|
|
for (PendingRuleData pendingRuleData : temp)
|
|
{
|
|
if (pendingRuleData.getActionedUponNodeRef().equals(actionedUponNodeRef) == true)
|
|
{
|
|
// Remove from the pending list
|
|
pendingRules.remove(pendingRuleData);
|
|
listUpdated = true;
|
|
}
|
|
}
|
|
|
|
if (listUpdated == true)
|
|
{
|
|
AlfrescoTransactionSupport.bindResource(KEY_RULES_PENDING, pendingRules);
|
|
AlfrescoTransactionSupport.bindListener(this.ruleTransactionListener);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
@SuppressWarnings("unchecked")
|
|
public void addRulePendingExecution(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule, boolean executeAtEnd)
|
|
{
|
|
ParameterCheck.mandatory("actionableNodeRef", actionableNodeRef);
|
|
ParameterCheck.mandatory("actionedUponNodeRef", actionedUponNodeRef);
|
|
|
|
// First check to see if the node has been disabled
|
|
if (this.isEnabled() == true &&
|
|
this.rulesEnabled(this.getOwningNodeRef(rule)) &&
|
|
this.disabledRules.contains(rule) == false)
|
|
{
|
|
PendingRuleData pendingRuleData = new PendingRuleData(actionableNodeRef, actionedUponNodeRef, rule, executeAtEnd);
|
|
|
|
List<PendingRuleData> pendingRules =
|
|
(List<PendingRuleData>) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING);
|
|
if (pendingRules == null)
|
|
{
|
|
// bind pending rules to the current transaction
|
|
pendingRules = new ArrayList<PendingRuleData>();
|
|
AlfrescoTransactionSupport.bindResource(KEY_RULES_PENDING, pendingRules);
|
|
// bind the rule transaction listener
|
|
AlfrescoTransactionSupport.bindListener(this.ruleTransactionListener);
|
|
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("Rule '" + rule.getTitle() + "' has been added pending execution to action upon node '" + actionedUponNodeRef.getId() + "'");
|
|
}
|
|
}
|
|
|
|
// Prevent the same rule being executed more than once in the same transaction
|
|
if (pendingRules.contains(pendingRuleData) == false)
|
|
{
|
|
pendingRules.add(pendingRuleData);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("The rule '" + rule.getTitle() + "' or the node '" + this.getOwningNodeRef(rule).getId() + "' has been disabled.");
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void executePendingRules()
|
|
{
|
|
if (AlfrescoTransactionSupport.getResource(KEY_RULES_EXECUTED) == null)
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("Creating the executed rules list");
|
|
}
|
|
AlfrescoTransactionSupport.bindResource(KEY_RULES_EXECUTED, new HashSet<ExecutedRuleData>());
|
|
}
|
|
else
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug("Executed rules list already exists");
|
|
}
|
|
}
|
|
|
|
List<PendingRuleData> executeAtEndRules = new ArrayList<PendingRuleData>();
|
|
executePendingRulesImpl(executeAtEndRules);
|
|
for (PendingRuleData data : executeAtEndRules)
|
|
{
|
|
executePendingRule(data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Executes the pending rules, iterating until all pending rules have been executed
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private void executePendingRulesImpl(List<PendingRuleData> executeAtEndRules)
|
|
{
|
|
// get the transaction-local rules to execute
|
|
List<PendingRuleData> pendingRules =
|
|
(List<PendingRuleData>) AlfrescoTransactionSupport.getResource(KEY_RULES_PENDING);
|
|
// only execute if there are rules present
|
|
if (pendingRules != null && !pendingRules.isEmpty())
|
|
{
|
|
PendingRuleData[] pendingRulesArr = pendingRules.toArray(new PendingRuleData[0]);
|
|
// remove all pending rules from the transaction
|
|
AlfrescoTransactionSupport.unbindResource(KEY_RULES_PENDING);
|
|
// execute each rule
|
|
for (PendingRuleData pendingRule : pendingRulesArr)
|
|
{
|
|
if (pendingRule.getExecuteAtEnd() == false)
|
|
{
|
|
executePendingRule(pendingRule);
|
|
}
|
|
else
|
|
{
|
|
executeAtEndRules.add(pendingRule);
|
|
}
|
|
}
|
|
|
|
// Run any rules that have been marked as pending during execution
|
|
executePendingRulesImpl(executeAtEndRules);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Executes a pending rule
|
|
*
|
|
* @param pendingRule the pending rule data object
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
private void executePendingRule(PendingRuleData pendingRule)
|
|
{
|
|
Set<ExecutedRuleData> executedRules =
|
|
(Set<ExecutedRuleData>) AlfrescoTransactionSupport.getResource(KEY_RULES_EXECUTED);
|
|
|
|
NodeRef actionedUponNodeRef = pendingRule.getActionedUponNodeRef();
|
|
Rule rule = pendingRule.getRule();
|
|
|
|
NodeRef ruleNodeRef = rule.getNodeRef();
|
|
if (!ruleNodeRef.getStoreRef().equals(actionedUponNodeRef.getStoreRef()) && !nodeService.exists(ruleNodeRef))
|
|
{
|
|
NodeRef newRuleNodeRef = new NodeRef(actionedUponNodeRef.getStoreRef(), ruleNodeRef.getId());
|
|
if (nodeService.exists(newRuleNodeRef))
|
|
{
|
|
ruleNodeRef = newRuleNodeRef;
|
|
}
|
|
|
|
}
|
|
final NodeRef finalRuleNodeRef = ruleNodeRef;
|
|
// update all associations and actions
|
|
rule = AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<Rule>()
|
|
{
|
|
public Rule doWork() throws Exception
|
|
{
|
|
return getRule(finalRuleNodeRef);
|
|
}
|
|
}, AuthenticationUtil.getSystemUserName());
|
|
|
|
if (executedRules == null || canExecuteRule(executedRules, actionedUponNodeRef, rule) == true)
|
|
{
|
|
executeRule(rule, actionedUponNodeRef, executedRules);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void executeRule(Rule rule, NodeRef actionedUponNodeRef, Set<ExecutedRuleData> executedRules)
|
|
{
|
|
// Get the action associated with the rule
|
|
Action action = rule.getAction();
|
|
if (action == null)
|
|
{
|
|
throw new RuleServiceException("Attempting to execute a rule that does not have a rule specified.");
|
|
}
|
|
|
|
// Evaluate the condition
|
|
if (this.actionService.evaluateAction(action, actionedUponNodeRef) == true)
|
|
{
|
|
if (executedRules != null)
|
|
{
|
|
// Add the rule to the executed rule list
|
|
// (do this before this is executed to prevent rules being added to the pending list)
|
|
executedRules.add(new ExecutedRuleData(actionedUponNodeRef, rule));
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug(" ... Adding rule (" + rule.getTitle() + ") and nodeRef (" + actionedUponNodeRef.getId() + ") to executed list");
|
|
}
|
|
}
|
|
|
|
// Execute the rule
|
|
boolean executeAsync = rule.getExecuteAsynchronously();
|
|
// ALF-718: Treats email actions as a special case where they may be performed after the
|
|
// current transaction is committed. This only deals with the bug fix and a more thorough approach
|
|
// (but one with potentially wide ranging consequences) is to replace the boolean executeAsynchronously
|
|
// property on Rules and Actions with an ExecutionTime property - which would
|
|
// be an enumerated type with members SYNCHRONOUSLY, SYNCRHONOUSLY_AFTER_COMMIT and ASYNCHRONOUSLY.
|
|
//
|
|
// NOTE: this code is not at the Action level (i.e. ActionService) since the logic of sending after
|
|
// successful commit works in the context of a Rule but not for the InvitationService.
|
|
if (action.getActionDefinitionName().equals(CompositeActionExecuter.NAME))
|
|
{
|
|
for (Action subAction : ((CompositeAction)action).getActions())
|
|
{
|
|
if (subAction.getActionDefinitionName().equals(MailActionExecuter.NAME))
|
|
{
|
|
subAction.setParameterValue(MailActionExecuter.PARAM_SEND_AFTER_COMMIT, true);
|
|
}
|
|
}
|
|
}
|
|
else if (action.getActionDefinitionName().equals(MailActionExecuter.NAME))
|
|
{
|
|
action.setParameterValue(MailActionExecuter.PARAM_SEND_AFTER_COMMIT, true);
|
|
}
|
|
|
|
executeAction(action, actionedUponNodeRef, executeAsync);
|
|
}
|
|
}
|
|
|
|
private void executeAction(Action action, NodeRef actionedUponNodeRef, boolean executeAsynchronously)
|
|
{
|
|
this.actionService.executeAction(action, actionedUponNodeRef, true, executeAsynchronously);
|
|
}
|
|
|
|
/**
|
|
* Determines whether the rule can be executed
|
|
*/
|
|
private boolean canExecuteRule(Set<ExecutedRuleData> executedRules, NodeRef actionedUponNodeRef, Rule rule)
|
|
{
|
|
boolean result = true;
|
|
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug(" >> Current executed items count = " + executedRules.size());
|
|
}
|
|
|
|
if (executedRules != null)
|
|
{
|
|
if (executedRules.contains(new ExecutedRuleData(actionedUponNodeRef, rule)) == true)
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug(" >> Already executed this rule (" + rule.getTitle()+ ") on this nodeRef (" + actionedUponNodeRef.getId() + ")");
|
|
}
|
|
result = false;
|
|
}
|
|
else
|
|
{
|
|
result = checkForCopy(executedRules, actionedUponNodeRef, rule);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug(" >> Executed this rule (" + rule.getTitle()+ ") on (" + actionedUponNodeRef.getId() + ") executed rule is null");
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Checks to see if a copy exists in the executed rules list
|
|
*/
|
|
private boolean checkForCopy(Set<ExecutedRuleData> executedRules, NodeRef actionedUponNodeRef, Rule rule)
|
|
{
|
|
boolean result = true;
|
|
if (this.nodeService.exists(actionedUponNodeRef)
|
|
&& this.permissionService.hasPermission(actionedUponNodeRef, PermissionService.READ).equals(AccessStatus.ALLOWED))
|
|
{
|
|
NodeRef copiedFrom = copyService.getOriginal(actionedUponNodeRef);
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug(" >> Got the copiedFrom nodeRef (" + copiedFrom + ")");
|
|
}
|
|
|
|
if (copiedFrom != null)
|
|
{
|
|
if (executedRules.contains(new ExecutedRuleData(copiedFrom, rule)) == true)
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug(" >> Already executed this rule (" + rule.getTitle()+ ") on this the copied from nodeRef (" + copiedFrom.getId() + ")");
|
|
}
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug(" >> Executed this rule (" + rule.getTitle()+ ") on (" + actionedUponNodeRef.getId() + ") copiedFrom is not is list");
|
|
logger.debug(" > Checking copy");
|
|
}
|
|
result = checkForCopy(executedRules, copiedFrom, rule);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (logger.isDebugEnabled() == true)
|
|
{
|
|
logger.debug(" >> Executed this rule (" + rule.getTitle()+ ") on (" + actionedUponNodeRef.getId() + ") no copied from aspect");
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Register the rule type
|
|
*
|
|
* @param ruleTypeAdapter the rule type adapter
|
|
*/
|
|
public void registerRuleType(RuleType ruleType)
|
|
{
|
|
this.ruleTypes.put(ruleType.getName(), ruleType);
|
|
}
|
|
|
|
/**
|
|
* Helper class to contain the information about a rule that is executed
|
|
*
|
|
* @author Roy Wetherall
|
|
*/
|
|
public class ExecutedRuleData
|
|
{
|
|
|
|
protected NodeRef actionableNodeRef;
|
|
protected Rule rule;
|
|
|
|
public ExecutedRuleData(NodeRef actionableNodeRef, Rule rule)
|
|
{
|
|
this.actionableNodeRef = actionableNodeRef;
|
|
this.rule = rule;
|
|
}
|
|
|
|
public NodeRef getActionableNodeRef()
|
|
{
|
|
return actionableNodeRef;
|
|
}
|
|
|
|
public Rule getRule()
|
|
{
|
|
return rule;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode()
|
|
{
|
|
int i = actionableNodeRef.hashCode();
|
|
i = (i*37) + rule.hashCode();
|
|
return i;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj)
|
|
{
|
|
if (this == obj)
|
|
{
|
|
return true;
|
|
}
|
|
if (obj instanceof ExecutedRuleData)
|
|
{
|
|
ExecutedRuleData that = (ExecutedRuleData) obj;
|
|
return (this.actionableNodeRef.equals(that.actionableNodeRef) &&
|
|
this.rule.equals(that.rule));
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper class to contain the information about a rule that is pending execution
|
|
*
|
|
* @author Roy Wetherall
|
|
*/
|
|
private class PendingRuleData extends ExecutedRuleData
|
|
{
|
|
private NodeRef actionedUponNodeRef;
|
|
private boolean executeAtEnd = false;
|
|
|
|
public PendingRuleData(NodeRef actionableNodeRef, NodeRef actionedUponNodeRef, Rule rule, boolean executeAtEnd)
|
|
{
|
|
super(actionableNodeRef, rule);
|
|
this.actionedUponNodeRef = actionedUponNodeRef;
|
|
this.executeAtEnd = executeAtEnd;
|
|
}
|
|
|
|
public NodeRef getActionedUponNodeRef()
|
|
{
|
|
return actionedUponNodeRef;
|
|
}
|
|
|
|
public boolean getExecuteAtEnd()
|
|
{
|
|
return this.executeAtEnd;
|
|
}
|
|
|
|
@Override
|
|
public int hashCode()
|
|
{
|
|
int i = super.hashCode();
|
|
i = (i*37) + actionedUponNodeRef.hashCode();
|
|
return i;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object obj)
|
|
{
|
|
if (this == obj)
|
|
{
|
|
return true;
|
|
}
|
|
if (obj instanceof PendingRuleData)
|
|
{
|
|
PendingRuleData that = (PendingRuleData) obj;
|
|
return (this.actionableNodeRef.equals(that.actionableNodeRef) &&
|
|
this.actionedUponNodeRef.equals(that.actionedUponNodeRef) &&
|
|
this.rule.equals(that.rule));
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public NodeRef getOwningNodeRef(final Rule rule)
|
|
{
|
|
// Run from system user: https://issues.alfresco.com/jira/browse/ALF-607
|
|
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<NodeRef>()
|
|
{
|
|
public NodeRef doWork() throws Exception
|
|
{
|
|
NodeRef result = null;
|
|
|
|
NodeRef ruleNodeRef = rule.getNodeRef();
|
|
if (ruleNodeRef != null)
|
|
{
|
|
result = getOwningNodeRefRuleImpl(ruleNodeRef);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}, AuthenticationUtil.getSystemUserName());
|
|
}
|
|
|
|
private NodeRef getOwningNodeRefRuleImpl(NodeRef ruleNodeRef)
|
|
{
|
|
// Get the system folder parent
|
|
NodeRef systemFolder = this.nodeService.getPrimaryParent(ruleNodeRef).getParentRef();
|
|
|
|
// Get the owning node ref
|
|
return this.nodeService.getPrimaryParent(systemFolder).getParentRef();
|
|
}
|
|
|
|
@Override
|
|
public NodeRef getOwningNodeRef(final Action action)
|
|
{
|
|
// Run from system user: https://issues.alfresco.com/jira/browse/ALF-607
|
|
return AuthenticationUtil.runAs(new AuthenticationUtil.RunAsWork<NodeRef>()
|
|
{
|
|
|
|
public NodeRef doWork() throws Exception
|
|
{
|
|
|
|
NodeRef result = null;
|
|
NodeRef actionNodeRef = action.getNodeRef();
|
|
if (actionNodeRef != null)
|
|
{
|
|
result = getOwningNodeRefActionImpl(actionNodeRef);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
}, AuthenticationUtil.getSystemUserName());
|
|
}
|
|
|
|
private NodeRef getOwningNodeRefActionImpl(NodeRef actionNodeRef)
|
|
{
|
|
NodeRef result = null;
|
|
NodeRef parentNodeRef = this.nodeService.getPrimaryParent(actionNodeRef).getParentRef();
|
|
if (parentNodeRef != null)
|
|
{
|
|
QName parentType = this.nodeService.getType(parentNodeRef);
|
|
if (RuleModel.TYPE_RULE.equals(parentType) == true)
|
|
{
|
|
result = getOwningNodeRefRuleImpl(parentNodeRef);
|
|
}
|
|
else if (ActionModel.TYPE_COMPOSITE_ACTION.equals(parentType) == true)
|
|
{
|
|
result = getOwningNodeRefActionImpl(parentNodeRef);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean isLinkedToRuleNode(NodeRef nodeRef)
|
|
{
|
|
return (getLinkedToRuleNode(nodeRef) != null);
|
|
}
|
|
|
|
@Override
|
|
public NodeRef getLinkedToRuleNode(NodeRef nodeRef)
|
|
{
|
|
NodeRef result = null;
|
|
|
|
// Check whether the node reference has the rule aspect
|
|
if (nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true)
|
|
{
|
|
ChildAssociationRef assoc = getSavedRuleFolderAssoc(nodeRef);
|
|
if (assoc.isPrimary() == false)
|
|
{
|
|
result = nodeService.getPrimaryParent(assoc.getChildRef()).getParentRef();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public List<NodeRef> getLinkedFromRuleNodes(NodeRef nodeRef)
|
|
{
|
|
List<NodeRef> result = new ArrayList<NodeRef>();
|
|
|
|
if (nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true)
|
|
{
|
|
ChildAssociationRef assoc = getSavedRuleFolderAssoc(nodeRef);
|
|
if (assoc.isPrimary() == true)
|
|
{
|
|
List<ChildAssociationRef> linkedAssocs = nodeService.getParentAssocs(assoc.getChildRef());
|
|
for (ChildAssociationRef linkAssoc : linkedAssocs)
|
|
{
|
|
if (linkAssoc.isPrimary() == false)
|
|
{
|
|
result.add(linkAssoc.getParentRef());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
}
|