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)
{