diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index f43df485a0..9a63697a50 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -665,6 +665,30 @@ + + + + + + + + + false + + + + + + + + + + + + false + + + diff --git a/source/java/org/alfresco/repo/rule/LinkRules.java b/source/java/org/alfresco/repo/rule/LinkRules.java new file mode 100644 index 0000000000..65881d20d6 --- /dev/null +++ b/source/java/org/alfresco/repo/rule/LinkRules.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2009-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.repo.rule; + +import java.util.List; + +import javax.swing.text.html.parser.ContentModel; + +import org.alfresco.error.AlfrescoRuntimeException; +import org.alfresco.repo.action.ParameterDefinitionImpl; +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.dictionary.DataTypeDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; + +/** + * Action implementation to link the rules from one folder to another + * + * @author Roy Wetherall + */ +public class LinkRules extends ActionExecuterAbstractBase +{ + public static final String NAME = "link-rules"; + public static final String PARAM_LINK_FROM_NODE = "link_from_node"; + + private NodeService nodeService; + + private RuntimeRuleService ruleService; + + public void setRuleService(RuntimeRuleService ruleService) + { + this.ruleService = ruleService; + } + + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // The actioned upon node is the rule folder we are interested in + // this should not already have rules associated with it + if (nodeService.hasAspect(actionedUponNodeRef, RuleModel.ASPECT_RULES) == true) + { + throw new AlfrescoRuntimeException("The link to node already has rules."); + } + + // Link to folder is passed as a parameter + // this should have rules already specified + NodeRef linkedFromNodeRef = (NodeRef)action.getParameterValue(PARAM_LINK_FROM_NODE); + if (nodeService.hasAspect(linkedFromNodeRef, RuleModel.ASPECT_RULES) == false) + { + throw new AlfrescoRuntimeException("The link from node has no rules to link."); + } + + // Create the destination folder as a secondary child of the first + NodeRef ruleSetNodeRef = ruleService.getSavedRuleFolderAssoc(linkedFromNodeRef).getChildRef(); + nodeService.addChild(actionedUponNodeRef, ruleSetNodeRef, RuleModel.ASSOC_RULE_FOLDER, RuleModel.ASSOC_RULE_FOLDER); + nodeService.addAspect(actionedUponNodeRef, RuleModel.ASPECT_RULES, null); + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + paramList.add( + new ParameterDefinitionImpl(PARAM_LINK_FROM_NODE, + DataTypeDefinition.NODE_REF, + true, + getParamDisplayLabel(PARAM_LINK_FROM_NODE))); + } +} diff --git a/source/java/org/alfresco/repo/rule/RuleLinkTest.java b/source/java/org/alfresco/repo/rule/RuleLinkTest.java new file mode 100644 index 0000000000..abbcdf2503 --- /dev/null +++ b/source/java/org/alfresco/repo/rule/RuleLinkTest.java @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2005-2007 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have recieved a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ +package org.alfresco.repo.rule; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.alfresco.model.ContentModel; +import org.alfresco.repo.action.evaluator.ComparePropertyValueEvaluator; +import org.alfresco.repo.action.executer.AddFeaturesActionExecuter; +import org.alfresco.repo.security.authentication.AuthenticationComponent; +import org.alfresco.repo.security.authentication.AuthenticationUtil; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionCondition; +import org.alfresco.service.cmr.action.ActionService; +import org.alfresco.service.cmr.model.FileFolderService; +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.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.namespace.QName; +import org.alfresco.util.BaseSpringTest; + +/** + * Parameter definition implementation unit test. + * + * @author Roy Wetherall + */ +public class RuleLinkTest extends BaseSpringTest +{ + protected static final String RULE_TYPE_NAME = RuleType.INBOUND; + protected static final String ACTION_DEF_NAME = AddFeaturesActionExecuter.NAME; + protected static final String ACTION_PROP_NAME_1 = AddFeaturesActionExecuter.PARAM_ASPECT_NAME; + protected static final QName ACTION_PROP_VALUE_1 = ContentModel.ASPECT_LOCKABLE; + protected static final String CONDITION_DEF_NAME = ComparePropertyValueEvaluator.NAME; + protected static final String COND_PROP_NAME_1 = ComparePropertyValueEvaluator.PARAM_VALUE; + protected static final String COND_PROP_VALUE_1 = ".doc"; + + + private NodeService nodeService; + private RuleService ruleService; + private ActionService actionService; + private AuthenticationComponent authenticationComponent; + private FileFolderService fileFolderService; + + private StoreRef testStoreRef; + private NodeRef rootNodeRef; + private NodeRef folderOne; + private NodeRef folderTwo; + + @SuppressWarnings("deprecation") + @Override + protected void onSetUpInTransaction() throws Exception + { + // Get the services + nodeService = (NodeService)getApplicationContext().getBean("nodeService"); + ruleService = (RuleService)getApplicationContext().getBean("ruleService"); + actionService = (ActionService)getApplicationContext().getBean("actionService"); + authenticationComponent = (AuthenticationComponent)getApplicationContext().getBean("authenticationComponent"); + fileFolderService = (FileFolderService)getApplicationContext().getBean("fileFolderService"); + + //authenticationComponent.setSystemUserAsCurrentUser(); + authenticationComponent.setCurrentUser(AuthenticationUtil.getAdminUserName()); + + + // Create the store and get the root node + testStoreRef = nodeService.createStore( + StoreRef.PROTOCOL_WORKSPACE, "Test_" + + System.currentTimeMillis()); + rootNodeRef = nodeService.getRootNode(testStoreRef); + + // Create the node used for tests + NodeRef folder = nodeService.createNode( + rootNodeRef, + ContentModel.ASSOC_CHILDREN, + QName.createQName("{test}testnode"), + ContentModel.TYPE_FOLDER).getChildRef(); + + folderOne = fileFolderService.create(folder, "folderOne", ContentModel.TYPE_FOLDER).getNodeRef(); + folderTwo = fileFolderService.create(folder, "folderTwo", ContentModel.TYPE_FOLDER).getNodeRef(); + } + + public void testLinkRule() + { + // Create a rule + Rule rule = createTestRule(false, "bobs rule"); + this.ruleService.saveRule(folderOne, rule); + + assertTrue(this.ruleService.hasRules(folderOne)); + assertEquals(1, ruleService.getRules(folderOne, false).size()); + assertFalse(ruleService.isLinkedToRuleNode(folderOne)); + + assertFalse(this.ruleService.hasRules(folderTwo)); + assertEquals(0, ruleService.getRules(folderTwo, false).size()); + assertFalse(ruleService.isLinkedToRuleNode(folderTwo)); + + Action linkAction = actionService.createAction(LinkRules.NAME); + linkAction.setParameterValue(LinkRules.PARAM_LINK_FROM_NODE, folderOne); + actionService.executeAction(linkAction, folderTwo); + + assertTrue(this.ruleService.hasRules(folderOne)); + assertEquals(1, ruleService.getRules(folderOne, false).size()); + assertFalse(ruleService.isLinkedToRuleNode(folderOne)); + + assertTrue(this.ruleService.hasRules(folderTwo)); + assertEquals(1, ruleService.getRules(folderTwo, false).size()); + boolean value = ruleService.isLinkedToRuleNode(folderTwo); + assertTrue(value); + assertEquals(folderOne, ruleService.getLinkedToRuleNode(folderTwo)); + + List linkedFrom = ruleService.getLinkedFromRuleNodes(folderTwo); + assertNotNull(linkedFrom); + assertTrue(linkedFrom.isEmpty()); + + linkedFrom = ruleService.getLinkedFromRuleNodes(folderOne); + assertNotNull(linkedFrom); + assertEquals(1, linkedFrom.size()); + assertEquals(folderTwo, linkedFrom.get(0)); + + // Check that you can't modify the rules on a linked rule node + try + { + rule = createTestRule(false, "bobs rule 2"); + this.ruleService.saveRule(folderTwo, rule); + fail("Shouldn't be able to add a new rule to a linked rule set"); + } + catch (RuleServiceException e) + { + // Expected + } + + // Add another rule to folder one + rule = createTestRule(false, "bobs other rule"); + this.ruleService.saveRule(folderOne, rule); + + assertTrue(this.ruleService.hasRules(folderOne)); + assertEquals(2, ruleService.getRules(folderOne, false).size()); + assertFalse(ruleService.isLinkedToRuleNode(folderOne)); + + assertTrue(this.ruleService.hasRules(folderTwo)); + assertEquals(2, ruleService.getRules(folderTwo, false).size()); + value = ruleService.isLinkedToRuleNode(folderTwo); + assertTrue(value); + assertEquals(folderOne, ruleService.getLinkedToRuleNode(folderTwo)); + + linkedFrom = ruleService.getLinkedFromRuleNodes(folderTwo); + assertNotNull(linkedFrom); + assertTrue(linkedFrom.isEmpty()); + + linkedFrom = ruleService.getLinkedFromRuleNodes(folderOne); + assertNotNull(linkedFrom); + assertEquals(1, linkedFrom.size()); + assertEquals(folderTwo, linkedFrom.get(0)); + + // Unlink + Action unlinkAction = actionService.createAction(UnlinkRules.NAME); + actionService.executeAction(unlinkAction, folderTwo); + + assertTrue(this.ruleService.hasRules(folderOne)); + assertEquals(2, ruleService.getRules(folderOne, false).size()); + assertFalse(ruleService.isLinkedToRuleNode(folderOne)); + + assertFalse(this.ruleService.hasRules(folderTwo)); + assertEquals(0, ruleService.getRules(folderTwo, false).size()); + assertFalse(ruleService.isLinkedToRuleNode(folderTwo)); + + } + + protected Rule createTestRule(boolean isAppliedToChildren, String title) + { + // Rule properties + Map conditionProps = new HashMap(); + conditionProps.put(COND_PROP_NAME_1, COND_PROP_VALUE_1); + + Map actionProps = new HashMap(); + actionProps.put(ACTION_PROP_NAME_1, ACTION_PROP_VALUE_1); + + List ruleTypes = new ArrayList(1); + ruleTypes.add(RULE_TYPE_NAME); + + // Create the action + Action action = this.actionService.createAction(CONDITION_DEF_NAME); + action.setParameterValues(conditionProps); + + ActionCondition actionCondition = this.actionService.createActionCondition(CONDITION_DEF_NAME); + actionCondition.setParameterValues(conditionProps); + action.addActionCondition(actionCondition); + + // Create the rule + Rule rule = new Rule(); + rule.setRuleTypes(ruleTypes); + rule.setTitle(title); + rule.setDescription("bob"); + rule.applyToChildren(isAppliedToChildren); + rule.setAction(action); + + return rule; + } + +} diff --git a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java index 3afd90db0e..78a4c615b4 100644 --- a/source/java/org/alfresco/repo/rule/RuleServiceImpl.java +++ b/source/java/org/alfresco/repo/rule/RuleServiceImpl.java @@ -304,6 +304,17 @@ public class RuleServiceImpl { 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 @@ -311,9 +322,9 @@ public class RuleServiceImpl * @param nodeRef the node reference * @return the node reference */ - private NodeRef getSavedRuleFolderRef(NodeRef nodeRef) + public ChildAssociationRef getSavedRuleFolderAssoc(NodeRef nodeRef) { - NodeRef result = null; + ChildAssociationRef result = null; List assocs = this.runtimeNodeService.getChildAssocs( nodeRef, @@ -325,7 +336,7 @@ public class RuleServiceImpl } else if (assocs.size() == 1) { - result = assocs.get(0).getChildRef(); + result = assocs.get(0); } return result; @@ -714,6 +725,8 @@ public class RuleServiceImpl */ public void saveRule(NodeRef nodeRef, Rule rule) { + checkForLinkedRules(nodeRef); + if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) { disableRules(); @@ -818,6 +831,8 @@ public class RuleServiceImpl */ 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 && @@ -846,11 +861,26 @@ public class RuleServiceImpl } } + /** + * 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."); + } + } + /** * @see org.alfresco.repo.rule.RuleService#removeAllRules(NodeRef) */ public void removeAllRules(NodeRef nodeRef) { + checkForLinkedRules(nodeRef); + if (this.permissionService.hasPermission(nodeRef, PermissionService.CHANGE_PERMISSIONS) == AccessStatus.ALLOWED) { if (this.nodeService.exists(nodeRef) == true && @@ -1324,4 +1354,59 @@ public class RuleServiceImpl } return result; } + + /** + * @see org.alfresco.service.cmr.rule.RuleService#isLinkedToRuleNode(org.alfresco.service.cmr.repository.NodeRef) + */ + public boolean isLinkedToRuleNode(NodeRef nodeRef) + { + return (getLinkedToRuleNode(nodeRef) != null); + } + + /** + * @see org.alfresco.service.cmr.rule.RuleService#getLinkedToRuleNode(org.alfresco.service.cmr.repository.NodeRef) + */ + 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; + } + + /** + * @see org.alfresco.service.cmr.rule.RuleService#getLinkedFromRuleNodes(org.alfresco.service.cmr.repository.NodeRef) + */ + public List getLinkedFromRuleNodes(NodeRef nodeRef) + { + List result = new ArrayList(); + + if (nodeService.hasAspect(nodeRef, RuleModel.ASPECT_RULES) == true) + { + ChildAssociationRef assoc = getSavedRuleFolderAssoc(nodeRef); + if (assoc.isPrimary() == true) + { + List linkedAssocs = nodeService.getParentAssocs(assoc.getChildRef()); + for (ChildAssociationRef linkAssoc : linkedAssocs) + { + if (linkAssoc.isPrimary() == false) + { + result.add(linkAssoc.getParentRef()); + } + } + } + } + + return result; + } + } diff --git a/source/java/org/alfresco/repo/rule/RuntimeRuleService.java b/source/java/org/alfresco/repo/rule/RuntimeRuleService.java index 038d7a424f..8dc8b9501d 100644 --- a/source/java/org/alfresco/repo/rule/RuntimeRuleService.java +++ b/source/java/org/alfresco/repo/rule/RuntimeRuleService.java @@ -27,6 +27,7 @@ package org.alfresco.repo.rule; import java.util.Set; import org.alfresco.repo.rule.RuleServiceImpl.ExecutedRuleData; +import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.rule.Rule; import org.alfresco.service.cmr.rule.RuleType; @@ -45,4 +46,6 @@ public interface RuntimeRuleService void executePendingRules(); void registerRuleType(RuleType ruleType); + + ChildAssociationRef getSavedRuleFolderAssoc(NodeRef nodeRef); } diff --git a/source/java/org/alfresco/repo/rule/UnlinkRules.java b/source/java/org/alfresco/repo/rule/UnlinkRules.java new file mode 100644 index 0000000000..bb8e60cc14 --- /dev/null +++ b/source/java/org/alfresco/repo/rule/UnlinkRules.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2009-2009 Alfresco Software Limited. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + + * This program 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 General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + * As a special exception to the terms and conditions of version 2.0 of + * the GPL, you may redistribute this Program in connection with Free/Libre + * and Open Source Software ("FLOSS") applications as described in Alfresco's + * FLOSS exception. You should have received a copy of the text describing + * the FLOSS exception, and it is also available here: + * http://www.alfresco.com/legal/licensing" + */ + +package org.alfresco.repo.rule; + +import java.util.List; + +import org.alfresco.repo.action.executer.ActionExecuterAbstractBase; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ParameterDefinition; +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.service.cmr.rule.RuleService; + +/** + * Action implementation to unlink the rules from one folder to another + * + * @author Roy Wetherall + */ +public class UnlinkRules extends ActionExecuterAbstractBase +{ + /** Constants */ + public static final String NAME = "unlink-rules"; + + /** Node service */ + private NodeService nodeService; + + /** Runtime rule service */ + private RuntimeRuleService ruleService; + + /** + * Set rule service + * + * @param ruleService rule service + */ + public void setRuleService(RuntimeRuleService ruleService) + { + this.ruleService = ruleService; + } + + /** + * Set node service + * + * @param nodeService node service + */ + public void setNodeService(NodeService nodeService) + { + this.nodeService = nodeService; + } + + /** + * @see org.alfresco.repo.action.executer.ActionExecuterAbstractBase#executeImpl(org.alfresco.service.cmr.action.Action, org.alfresco.service.cmr.repository.NodeRef) + */ + @Override + protected void executeImpl(Action action, NodeRef actionedUponNodeRef) + { + // Check that the actioned upon node has the rules aspect applied + if (nodeService.hasAspect(actionedUponNodeRef, RuleModel.ASPECT_RULES) == true) + { + // Get the rule node the actioned upon node is linked to + NodeRef linkedToNode = ((RuleService)ruleService).getLinkedToRuleNode(actionedUponNodeRef); + if (linkedToNode != null) + { + NodeRef ruleFolder = ruleService.getSavedRuleFolderAssoc(linkedToNode).getChildRef(); + nodeService.removeChild(actionedUponNodeRef, ruleFolder); + nodeService.removeAspect(actionedUponNodeRef, RuleModel.ASPECT_RULES); + } + } + } + + /** + * @see org.alfresco.repo.action.ParameterizedItemAbstractBase#addParameterDefinitions(java.util.List) + */ + @Override + protected void addParameterDefinitions(List paramList) + { + } +} diff --git a/source/java/org/alfresco/service/cmr/rule/RuleService.java b/source/java/org/alfresco/service/cmr/rule/RuleService.java index 4f83d1ad76..cc893767f9 100644 --- a/source/java/org/alfresco/service/cmr/rule/RuleService.java +++ b/source/java/org/alfresco/service/cmr/rule/RuleService.java @@ -237,4 +237,34 @@ public interface RuleService */ @Auditable(key = Auditable.Key.RETURN, parameters = {"action"}) public NodeRef getOwningNodeRef(Action action); + + /** + * Indicates whether the passed rule node reference is linked to another + * rule node. + * + * @param nodeRef rule node reference + * @return boolean true if linked, false otherwise + */ + @Auditable(key = Auditable.Key.RETURN, parameters = {"nodeRef"}) + public boolean isLinkedToRuleNode(NodeRef nodeRef); + + /** + * Get the node reference to the rule node which the rule node links to. Returns null + * if rules are not linked. + * + * @param nodeRef node reference of a rule node + * @return NodeRef reference to the + */ + @Auditable(key = Auditable.Key.RETURN, parameters = {"nodeRef"}) + public NodeRef getLinkedToRuleNode(NodeRef nodeRef); + + /** + * Get a list of the all the rule nodes that link to the passed rule node. + * Returns an empty list if none link. + * + * @param nodeRef node reference of a rule node + * @return List list of rule nodes that link to this passed rule node, empty if none + */ + @Auditable(key = Auditable.Key.RETURN, parameters = {"nodeRef"}) + public List getLinkedFromRuleNodes(NodeRef nodeRef); }