diff --git a/config/alfresco/action-services-context.xml b/config/alfresco/action-services-context.xml index 9835531a1f..1125fa4c24 100644 --- a/config/alfresco/action-services-context.xml +++ b/config/alfresco/action-services-context.xml @@ -291,6 +291,9 @@ ${mail.header} + + ${mail.from.default} + diff --git a/config/alfresco/messages/content-service.properties b/config/alfresco/messages/content-service.properties index ed894d3a73..19b562d56c 100644 --- a/config/alfresco/messages/content-service.properties +++ b/config/alfresco/messages/content-service.properties @@ -3,7 +3,7 @@ content.content_missing=The node''s content is missing: \n node: {0} \n reader: {1} \n Please contact your system administrator. content.runtime_exec.property_moved=The property ''errorCodes'' has moved down onto the RuntimeExec class -index.recovery.store_not_up_to_date=The indexes for store ''{0}'' are not synchronized with the database. +index.recovery.out_of_date=The indexes for are not synchronized with the database. index.recovery.starting=Index recovery started: {0} transactions. index.recovery.complete=Index recovery completed. index.recovery.progress=\t{0} % complete. diff --git a/config/alfresco/repository.properties b/config/alfresco/repository.properties index 6842a43698..8f4d0b213c 100644 --- a/config/alfresco/repository.properties +++ b/config/alfresco/repository.properties @@ -85,6 +85,7 @@ mail.password= mail.encoding=UTF-8 # Set this value to 7bit or similar for Asian encoding of email headers as required mail.header= +mail.from.default=alfresco@alfresco.org # System Configuration system.store=system://system diff --git a/config/alfresco/script-services-context.xml b/config/alfresco/script-services-context.xml index 995294635d..391f625782 100644 --- a/config/alfresco/script-services-context.xml +++ b/config/alfresco/script-services-context.xml @@ -35,6 +35,15 @@ + + + + people + + + + + diff --git a/config/alfresco/workflow/adhoc_processdefinition.xml b/config/alfresco/workflow/adhoc_processdefinition.xml index 101ceaf168..2c359035f7 100644 --- a/config/alfresco/workflow/adhoc_processdefinition.xml +++ b/config/alfresco/workflow/adhoc_processdefinition.xml @@ -28,9 +28,9 @@ if (wf_notifyMe) { var mail = actions.create("mail"); - mail.parameters.to = initiator.properties["cm:email"]; + mail.parameters.to = initiator.properties.email; mail.parameters.subject = "Adhoc Task " + bpm_workflowDescription; - mail.parameters.from = bpm_assignee.properties["cm:email"]; + mail.parameters.from = bpm_assignee.properties.email; mail.parameters.text = "It's done"; mail.execute(bpm_package); } diff --git a/config/alfresco/workflow/parallelreview_processdefinition.xml b/config/alfresco/workflow/parallelreview_processdefinition.xml index 13b80f9955..e84952af3a 100644 --- a/config/alfresco/workflow/parallelreview_processdefinition.xml +++ b/config/alfresco/workflow/parallelreview_processdefinition.xml @@ -30,7 +30,7 @@ diff --git a/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java b/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java index 61a592c240..02df2f1670 100644 --- a/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java +++ b/source/java/org/alfresco/repo/action/executer/MailActionExecuter.java @@ -45,6 +45,7 @@ import org.alfresco.service.cmr.security.AuthorityType; import org.alfresco.service.cmr.security.PersonService; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.mail.javamail.MimeMessagePreparator; @@ -54,7 +55,8 @@ import org.springframework.mail.javamail.MimeMessagePreparator; * * @author Roy Wetherall */ -public class MailActionExecuter extends ActionExecuterAbstractBase +public class MailActionExecuter extends ActionExecuterAbstractBase + implements InitializingBean { private static Log logger = LogFactory.getLog(MailActionExecuter.class); @@ -72,7 +74,7 @@ public class MailActionExecuter extends ActionExecuterAbstractBase /** * From address */ - public static final String FROM_ADDRESS = "alfresco_repository@alfresco.org"; + private static final String FROM_ADDRESS = "alfresco@alfresco.org"; /** * The java mail sender @@ -114,6 +116,11 @@ public class MailActionExecuter extends ActionExecuterAbstractBase */ private String headerEncoding = null; + /** + * Default from address + */ + private String fromAddress = null; + /** * @param javaMailSender the java mail sender */ @@ -178,6 +185,25 @@ public class MailActionExecuter extends ActionExecuterAbstractBase this.headerEncoding = headerEncoding; } + /** + * @param fromAddress The default mail address. + */ + public void setFromAddress(String fromAddress) + { + this.fromAddress = fromAddress; + } + + /** + * Initialise bean + */ + public void afterPropertiesSet() throws Exception + { + if (fromAddress == null || fromAddress.length() == 0) + { + fromAddress = FROM_ADDRESS; + } + } + /** * Execute the rule action */ @@ -274,13 +300,13 @@ public class MailActionExecuter extends ActionExecuterAbstractBase // set the from address - use the default if not set String from = (String)ruleAction.getParameterValue(PARAM_FROM); - if (from != null) + if (from == null || from.length() == 0) { - message.setFrom(from); + message.setFrom(fromAddress); } else { - message.setFrom(FROM_ADDRESS); + message.setFrom(from); } } }; @@ -336,4 +362,5 @@ public class MailActionExecuter extends ActionExecuterAbstractBase paramList.add(new ParameterDefinitionImpl(PARAM_FROM, DataTypeDefinition.TEXT, false, getParamDisplayLabel(PARAM_FROM))); paramList.add(new ParameterDefinitionImpl(PARAM_TEMPLATE, DataTypeDefinition.NODE_REF, false, getParamDisplayLabel(PARAM_TEMPLATE))); } + } diff --git a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml index 0946ad2c1e..ac921c7d7e 100644 --- a/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml +++ b/source/java/org/alfresco/repo/domain/hibernate/Node.hbm.xml @@ -224,6 +224,26 @@ assoc.id = :childAssocId + + select + status + from + org.alfresco.repo.domain.hibernate.NodeStatusImpl as status + join status.node as node + where + node.id in + ( + select + child.id + from + org.alfresco.repo.domain.hibernate.ChildAssocImpl as assoc + join assoc.child as child + where + assoc.parent.id = :parentId and + assoc.isPrimary = 1 + ) + + select assoc diff --git a/source/java/org/alfresco/repo/jscript/People.java b/source/java/org/alfresco/repo/jscript/People.java new file mode 100644 index 0000000000..6f85360604 --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/People.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2005 Alfresco, Inc. + * + * Licensed under the Mozilla Public License version 1.1 + * with a permitted attribution clause. You may obtain a + * copy of the License at + * + * http://www.alfresco.org/legal/license.txt + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, + * either express or implied. See the License for the specific + * language governing permissions and limitations under the + * License. + */ +package org.alfresco.repo.jscript; + +import java.io.Serializable; +import java.util.List; +import java.util.Map; + +import org.alfresco.service.ServiceRegistry; +import org.alfresco.service.cmr.action.Action; +import org.alfresco.service.cmr.action.ActionDefinition; +import org.alfresco.service.cmr.action.ActionService; +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.security.PersonService; +import org.alfresco.service.namespace.QName; +import org.mozilla.javascript.Scriptable; +import org.mozilla.javascript.Wrapper; + +/** + * Scripted People service for describing and executing actions against People & Groups. + * + * @author davidc + */ +public final class People extends BaseScriptImplementation implements Scopeable +{ + /** Repository Service Registry */ + private ServiceRegistry services; + + /** Root scope for this object */ + private Scriptable scope; + + /** + * Set the service registry + * + * @param serviceRegistry the service registry + */ + public void setServiceRegistry(ServiceRegistry serviceRegistry) + { + this.services = serviceRegistry; + } + + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + + /** + * Gets the Person given the username + * + * @return the person node (type cm:person) or null if no such person exists + */ + public Node getPerson(String username) + { + Node person = null; + PersonService personService = services.getPersonService(); + if (personService.personExists(username)) + { + NodeRef personRef = personService.getPerson(username); + person = new Node(personRef, services, null, scope); + } + return person; + } + +} diff --git a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java index f197c67832..d12f7153cc 100644 --- a/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/DbNodeServiceImpl.java @@ -123,6 +123,22 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl return unchecked; } + /** + * Gets the node status for a live node. + * @param nodeRef the node reference + * @return Returns the node status, which will not be null and will have a live node attached. + * @throws InvalidNodeRefException if the node is deleted or never existed + */ + public NodeStatus getNodeStatusNotNull(NodeRef nodeRef) throws InvalidNodeRefException + { + NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false); + if (nodeStatus == null || nodeStatus.getNode() == null) + { + throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef); + } + return nodeStatus; + } + public boolean exists(StoreRef storeRef) { Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier()); @@ -1411,7 +1427,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl private void archiveNode(NodeRef nodeRef, StoreRef archiveStoreRef) { - Node node = getNodeNotNull(nodeRef); + NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, false); + Node node = nodeStatus.getNode(); ChildAssoc primaryParentAssoc = nodeDaoService.getPrimaryParentAssoc(node); // add the aspect @@ -1454,17 +1471,24 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl ContentModel.ASSOC_CHILDREN, QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedItem")); - // get the IDs of all the node's primary children, including its own - Map nodesById = getNodeHierarchy(node, null); - - // Archive all the associations between the archived nodes and non-archived nodes - for (Node nodeToArchive : nodesById.values()) - { - archiveAssocs(nodeToArchive, nodesById); - } - // the node reference has changed due to the store move nodeRef = node.getNodeRef(); + // as has the node status + nodeStatus = nodeDaoService.getNodeStatus(nodeRef, true); + + // get the IDs of all the node's primary children, including its own + Map nodeStatusesById = getNodeHierarchy(nodeStatus, null); + + // Archive all the associations between the archived nodes and non-archived nodes + for (NodeStatus nodeStatusToArchive : nodeStatusesById.values()) + { + Node nodeToArchive = nodeStatusToArchive.getNode(); + if (nodeToArchive == null) + { + continue; + } + archiveAssocs(nodeToArchive, nodeStatusesById); + } } /** @@ -1477,22 +1501,25 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl */ private void moveNodeToStore(Node node, Store store) { + NodeRef nodeRef = node.getNodeRef(); + NodeStatus nodeStatus = nodeDaoService.getNodeStatus(nodeRef, true); // get the IDs of all the node's primary children, including its own - Map nodesById = getNodeHierarchy(node, null); + Map nodeStatusesById = getNodeHierarchy(nodeStatus, null); // move each node into the archive store - for (Node nodeToMove : nodesById.values()) + for (NodeStatus oldNodeStatus : nodeStatusesById.values()) { - NodeRef oldNodeRef = nodeToMove.getNodeRef(); + Node nodeToMove = oldNodeStatus.getNode(); nodeToMove.setStore(store); NodeRef newNodeRef = nodeToMove.getNodeRef(); // update old status - NodeStatus oldNodeStatus = nodeDaoService.getNodeStatus(oldNodeRef, true); oldNodeStatus.setNode(null); // create the new status NodeStatus newNodeStatus = nodeDaoService.getNodeStatus(newNodeRef, true); newNodeStatus.setNode(nodeToMove); + + invokeOnUpdateNode(newNodeRef); } } @@ -1501,38 +1528,42 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl * The given node will be added to the map and the method is recursive * to all primary children. * - * @param node the start of the hierarchy - * @param nodesById a map of nodes that will be reused as the return value + * @param nodeStatus the status of the node at the top of the hierarchy + * @param nodeStatusesById a map of node statuses that will be reused as the return value * @return Returns a map of nodes in the hierarchy keyed by their IDs */ - private Map getNodeHierarchy(Node node, Map nodesById) + private Map getNodeHierarchy(NodeStatus nodeStatus, Map nodeStatusesById) { - if (nodesById == null) + if (nodeStatusesById == null) { - nodesById = new HashMap(23); + nodeStatusesById = new HashMap(23); + // this is the entry into the hierarchy - flush to ensure we are not stale + nodeDaoService.flush(); } - Long id = node.getId(); - if (nodesById.containsKey(id)) + Node node = nodeStatus.getNode(); + if (node == null) + { + // the node has already been deleted + return nodeStatusesById; + } + Long nodeId = node.getId(); + if (nodeStatusesById.containsKey(nodeId)) { // this ID was already added - circular reference - logger.warn("Circular hierarchy found including node " + id); - return nodesById; + logger.warn("Circular hierarchy found including node " + nodeId); + return nodeStatusesById; } // add the node to the map - nodesById.put(id, node); + nodeStatusesById.put(nodeId, nodeStatus); // recurse into the primary children - Collection childAssocs = nodeDaoService.getChildAssocs(node); - for (ChildAssoc childAssoc : childAssocs) + Collection primaryChildNodeStatuses = nodeDaoService.getPrimaryChildNodeStatuses(node); + for (NodeStatus primaryChildNodeStatus : primaryChildNodeStatuses) { // cascade into primary associations - if (childAssoc.getIsPrimary()) - { - Node primaryChild = childAssoc.getChild(); - nodesById = getNodeHierarchy(primaryChild, nodesById); - } + nodeStatusesById = getNodeHierarchy(primaryChildNodeStatus, nodeStatusesById); } - return nodesById; + return nodeStatusesById; } /** @@ -1544,7 +1575,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl * @param node the node whose associations must be archived * @param nodesById a map of nodes partaking in the archival process */ - private void archiveAssocs(Node node, Map nodesById) + private void archiveAssocs(Node node, Map nodeStatusesById) { List childAssocsToDelete = new ArrayList(5); // child associations @@ -1553,7 +1584,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl for (ChildAssoc assoc : childAssocs) { Long relatedNodeId = assoc.getChild().getId(); - if (nodesById.containsKey(relatedNodeId)) + if (nodeStatusesById.containsKey(relatedNodeId)) { // a sibling in the archive process continue; @@ -1566,7 +1597,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl for (ChildAssoc assoc : node.getParentAssocs()) { Long relatedNodeId = assoc.getParent().getId(); - if (nodesById.containsKey(relatedNodeId)) + if (nodeStatusesById.containsKey(relatedNodeId)) { // a sibling in the archive process continue; @@ -1586,7 +1617,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl for (NodeAssoc assoc : nodeDaoService.getSourceNodeAssocs(node)) { Long relatedNodeId = assoc.getSource().getId(); - if (nodesById.containsKey(relatedNodeId)) + if (nodeStatusesById.containsKey(relatedNodeId)) { // a sibling in the archive process continue; @@ -1599,7 +1630,7 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl for (NodeAssoc assoc : nodeDaoService.getTargetNodeAssocs(node)) { Long relatedNodeId = assoc.getTarget().getId(); - if (nodesById.containsKey(relatedNodeId)) + if (nodeStatusesById.containsKey(relatedNodeId)) { // a sibling in the archive process continue; @@ -1665,7 +1696,8 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName) { - Node archivedNode = getNodeNotNull(archivedNodeRef); + NodeStatus archivedNodeStatus = getNodeStatusNotNull(archivedNodeRef); + Node archivedNode = archivedNodeStatus.getNode(); Set aspects = archivedNode.getAspects(); Map properties = archivedNode.getProperties(); // the node must be a top-level archive node @@ -1707,18 +1739,21 @@ public class DbNodeServiceImpl extends AbstractNodeServiceImpl } // move the node to the target parent, which may or may not be the original parent - moveNode( + ChildAssociationRef newChildAssocRef = moveNode( archivedNodeRef, destinationParentNodeRef, assocTypeQName, assocQName); + archivedNodeRef = newChildAssocRef.getChildRef(); + archivedNodeStatus = nodeDaoService.getNodeStatus(archivedNodeRef, false); // get the IDs of all the node's primary children, including its own - Map restoredNodesById = getNodeHierarchy(archivedNode, null); + Map restoreNodeStatusesById = getNodeHierarchy(archivedNodeStatus, null); // Restore the archived associations, if required - for (Node restoredNode : restoredNodesById.values()) + for (NodeStatus restoreNodeStatus : restoreNodeStatusesById.values()) { - restoreAssocs(restoredNode); + Node restoreNode = restoreNodeStatus.getNode(); + restoreAssocs(restoreNode); } // the node reference has changed due to the store move diff --git a/source/java/org/alfresco/repo/node/db/NodeDaoService.java b/source/java/org/alfresco/repo/node/db/NodeDaoService.java index d4f9bd5b36..1984eb1a15 100644 --- a/source/java/org/alfresco/repo/node/db/NodeDaoService.java +++ b/source/java/org/alfresco/repo/node/db/NodeDaoService.java @@ -143,6 +143,11 @@ public interface NodeDaoService */ public void setChildNameUnique(ChildAssoc childAssoc, String childName); + /** + * Get the statuses of all the child primary child nodes of the given parent + */ + public Collection getPrimaryChildNodeStatuses(final Node parentNode); + /** * Get all child associations for a given node * diff --git a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java index 652c000e72..0a57c5514b 100644 --- a/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java +++ b/source/java/org/alfresco/repo/node/db/hibernate/HibernateNodeDaoServiceImpl.java @@ -63,6 +63,7 @@ import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.hibernate.FlushMode; import org.hibernate.ObjectDeletedException; import org.hibernate.Query; import org.hibernate.ScrollMode; @@ -82,6 +83,7 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements { private static final String QUERY_GET_ALL_STORES = "store.GetAllStores"; private static final String UPDATE_SET_CHILD_ASSOC_NAME = "node.updateChildAssocName"; + private static final String QUERY_GET_PRIMARY_CHILD_NODE_STATUSES = "node.GetPrimaryChildNodeStatuses"; private static final String QUERY_GET_CHILD_ASSOCS = "node.GetChildAssocs"; private static final String QUERY_GET_CHILD_ASSOC_BY_TYPE_AND_NAME = "node.GetChildAssocByTypeAndName"; private static final String QUERY_GET_CHILD_ASSOC_REFS = "node.GetChildAssocRefs"; @@ -647,6 +649,24 @@ public class HibernateNodeDaoServiceImpl extends HibernateDaoSupport implements } } + @SuppressWarnings("unchecked") + public Collection getPrimaryChildNodeStatuses(final Node parentNode) + { + HibernateCallback callback = new HibernateCallback() + { + public Object doInHibernate(Session session) + { + Query query = session + .getNamedQuery(HibernateNodeDaoServiceImpl.QUERY_GET_PRIMARY_CHILD_NODE_STATUSES) + .setLong("parentId", parentNode.getId()) + .setFlushMode(FlushMode.NEVER); + return query.list(); + } + }; + List queryResults = (List) getHibernateTemplate().execute(callback); + return queryResults; + } + @SuppressWarnings("unchecked") public Collection getChildAssocs(final Node parentNode) {