Merged 1.4 to HEAD

svn merge svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4252 svn://svn.alfresco.com:3691/alfresco/BRANCHES/V1.4@4294 .
   svn revert root\common\common.xml
   svn resolved root\projects\repository\config\alfresco\script-services-context.xml


git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4634 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Derek Hulley
2006-12-18 13:45:11 +00:00
parent 8e0a637886
commit 72bb79696d
48 changed files with 1960 additions and 299 deletions

View File

@@ -0,0 +1,7 @@
# For Lifecycle Workflow Example
wfl_lifecycleapproval.workflow.title=Lifecycle Review & Approve
wfl_lifecycleapproval.workflow.description=Lifecycle Review & Approval workflow (Auto updates document status)
wfl_lifecycleapproval.node.review.transition.reject.title=Reject
wfl_lifecycleapproval.node.review.transition.reject.description=Reject
wfl_lifecycleapproval.node.review.transition.approve.title=Approve
wfl_lifecycleapproval.node.review.transition.approve.description=Approve

View File

@@ -0,0 +1,29 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<bean id="lifecycle.workflowBootstrap" parent="workflowDeployer">
<property name="workflowDefinitions">
<list>
<props>
<prop key="engineId">jbpm</prop>
<prop key="location">alfresco/extension/lifecycle_processdefinition.xml</prop>
<prop key="mimetype">text/xml</prop>
<prop key="redeploy">false</prop>
</props>
</list>
</property>
<property name="models">
<list>
<value>alfresco/extension/lifecycleModel.xml</value>
</list>
</property>
<property name="labels">
<list>
<value>alfresco/extension/lifecycle-messages</value>
</list>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
<model name="wfl:workflowlifecyclemodel" xmlns="http://www.alfresco.org/model/dictionary/1.0">
<!-- Optional meta-data about the model -->
<description>Workflow Lifecycle Model</description>
<author></author>
<version>1.0</version>
<!-- Imports are required to allow references to definitions in other models -->
<imports>
<!-- Import Alfresco Dictionary Definitions -->
<import uri="http://www.alfresco.org/model/dictionary/1.0" prefix="d" />
<!-- Import Alfresco Content Domain Model Definitions -->
<import uri="http://www.alfresco.org/model/content/1.0" prefix="cm" />
</imports>
<namespaces>
<namespace uri="wfl.model" prefix="wfl" />
</namespaces>
<constraints>
<constraint name="wfl:status" type="LIST">
<parameter name="allowedValues">
<list>
<value>Draft</value>
<value>In Review</value>
<value>Approved</value>
</list>
</parameter>
</constraint>
</constraints>
<aspects>
<!-- Status property is used to manage workflow approval -->
<aspect name="wfl:status">
<title>Status</title>
<properties>
<property name="wfl:status">
<title>Status</title>
<type>d:text</type>
<default>Draft</default>
<constraints>
<constraint ref="wfl:status" />
</constraints>
</property>
</properties>
</aspect>
</aspects>
</model>

View File

@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="urn:jbpm.org:jpdl-3.1" name="wfl:lifecycleapproval">
<swimlane name="initiator" />
<start-state name="start">
<task name="wf:submitReviewTask" swimlane="initiator" />
<event type="node-leave">
<!-- Call script once the workflow package exists i.e. on node-leave -->
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<!-- Apply the Workflow Lifecycle Aspect (wfl:status) if not set already. Note: The default wfl:status property is draft -->
<script>
for (var i = 0; i &lt; bpm_package.children.length; i++)
{
if (!bpm_package.children[i].hasAspect("wfl:status"))
{
bpm_package.children[i].addAspect("wfl:status");
}
}
</script>
</action>
</event>
<transition name="" to="review" />
</start-state>
<swimlane name="reviewer">
<assignment actor-id="#{bpm_assignee.properties['cm:userName']}" />
</swimlane>
<task-node name="review">
<event type="node-enter">
<!-- Update the status to In Review when we enter this task -->
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
for (var i = 0; i &lt; bpm_package.children.length; i++)
{
bpm_package.children[i].properties["wfl:status"] = "In Review";
bpm_package.children[i].save();
}
</script>
</action>
</event>
<task name="wf:reviewTask" swimlane="reviewer">
<event type="task-create">
<script>
if (bpm_workflowDueDate != void) taskInstance.dueDate = bpm_workflowDueDate;
if (bpm_workflowPriority != void) taskInstance.priority = bpm_workflowPriority;
</script>
</event>
</task>
<transition name="reject" to="rejected" />
<transition name="approve" to="approved" />
</task-node>
<task-node name="rejected">
<event type="node-enter">
<!-- Update the status to Draft when we enter this task -->
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
for (var i = 0; i &lt; bpm_package.children.length; i++)
{
bpm_package.children[i].properties["wfl:status"] = "Draft";
bpm_package.children[i].save();
}
</script>
</action>
</event>
<task name="wf:rejectedTask" swimlane="initiator" />
<transition name="" to="end" />
</task-node>
<task-node name="approved">
<event type="node-enter">
<!-- Update the status to Approved when we enter this task -->
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
for (var i = 0; i &lt; bpm_package.children.length; i++)
{
bpm_package.children[i].properties["wfl:status"] = "Approved";
bpm_package.children[i].save();
}
</script>
</action>
</event>
<task name="wf:approvedTask" swimlane="initiator" />
<transition name="" to="end" />
</task-node>
<end-state name="end" />
<event type="process-end">
<action class="org.alfresco.repo.workflow.jbpm.AlfrescoJavaScript">
<script>
if (cancelled)
{
for (var i = 0; i &lt; bpm_package.children.length; i++)
{
if (bpm_package.children[0].hasAspect("wfl:status"))
{
bpm_package.children[i].properties["wfl:status"] = "Draft";
bpm_package.children[i].save();
}
}
if (logger.isLoggingEnabled()) logger.log("Workflow cancelled, status reset to Draft");
}
else
{
if (logger.isLoggingEnabled()) logger.log("Workflow completed");
}
</script>
</action>
</event>
</process-definition>

View File

@@ -3,7 +3,7 @@
<beans> <beans>
<bean id="sample.workflowBootstrap" parent="workflowDeployer"> <bean id="parallel.workflowBootstrap" parent="workflowDeployer">
<property name="workflowDefinitions"> <property name="workflowDefinitions">
<list> <list>
<props> <props>
@@ -19,6 +19,8 @@
<!-- NOTE: The above process definition relies on the default workflowModel.xml --> <!-- NOTE: The above process definition relies on the default workflowModel.xml -->
<!-- which is already registered during Alfresco startup. --> <!-- which is already registered during Alfresco startup. -->
<!-- See bootstrap-context.xml (workflowBootstrap). --> <!-- See bootstrap-context.xml (workflowBootstrap). -->
<!-- <value>alfresco/workflow/workflowModel.xml</value> -->
</list> </list>
</property> </property>
<property name="labels"> <property name="labels">
@@ -26,6 +28,8 @@
<!-- NOTE: The above process definition relies on the default workflow-messages.properties --> <!-- NOTE: The above process definition relies on the default workflow-messages.properties -->
<!-- which is already registered during Alfresco startup --> <!-- which is already registered during Alfresco startup -->
<!-- See bootstrap-context.xml (workflowBootstrap). --> <!-- See bootstrap-context.xml (workflowBootstrap). -->
<!-- <value>alfresco/workflow/workflow-messages</value> -->
</list> </list>
</property> </property>
</bean> </bean>

View File

@@ -6,5 +6,5 @@ coci_service.err_not_owner=This user is not the owner of the working copy and ca
coci_service.err_workingcopy_checkout=A working copy can not be checked out. coci_service.err_workingcopy_checkout=A working copy can not be checked out.
coci_service.err_not_authenticated=Can not find the currently authenticated user details required by the CheckOutCheckIn service. coci_service.err_not_authenticated=Can not find the currently authenticated user details required by the CheckOutCheckIn service.
coci_service.err_workingcopy_has_no_mimetype=Working copy node ({0}) has no mimetype coci_service.err_workingcopy_has_no_mimetype=Working copy node ({0}) has no mimetype
coci_service.err_already_checkedout=This node is already checked out.
coci_service.discussion_for={0} discussion coci_service.discussion_for={0} discussion

View File

@@ -49,6 +49,15 @@ ok> use definition [<workflowDefId>]
<workflowDefId> is ommited, the currently selected workflow definition <workflowDefId> is ommited, the currently selected workflow definition
is shown. is shown.
ok> undeploy definition <workflowDefId>
Undeploy the latest version of the workflow definition identified by
<workflowDefId>. This will also terminate and remove all "in-flight"
workflows associated with the definition.
If multiple versions of the definition exist, you will have to undeploy
each in turn to remove the definition completely.
## ##
## Variable Commands ## Variable Commands
## ##
@@ -112,10 +121,11 @@ ok> start [<varName[=varValue>]]*
start bpm:assignee=david wf:predefined start bpm:assignee=david wf:predefined
ok> show workflows ok> show workflows [all]
Display the list of active workflows for the currently selected workflow Display the list of active workflows for the currently selected workflow
definition. definition. Or, display the list of all workflows when used with additional
keyword 'all'.
ok> use workflow <workflowId> ok> use workflow <workflowId>
@@ -149,6 +159,10 @@ ok> delete workflow <workflowId>
Force deletion of the specified <workflowId>. Force deletion of the specified <workflowId>.
ok> delete all workflows
Force deletion of all "in-flight" workflows. Use with care!
## ##
## Task Commands ## Task Commands
## ##

View File

@@ -37,4 +37,10 @@
</property> </property>
</bean> </bean>
<bean id="policyRegistration" abstract="true" init-method="register">
<property name="policyComponent">
<ref bean="policyComponent" />
</property>
</bean>
</beans> </beans>

View File

@@ -82,26 +82,6 @@
<alias name="namespaceService" alias="NamespaceService"/> <alias name="namespaceService" alias="NamespaceService"/>
<!--
<bean id="NamespaceService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.service.namespace.NamespaceService</value>
</property>
<property name="target">
<ref bean="namespaceService"/>
</property>
<property name="interceptorNames">
<list>
<idref local="NamespaceService_transaction"/>
<idref local="AuditMethodInterceptor"/>
<idref local="exceptionTranslator"/>
<idref bean="NamespaceService_security"/>
<idref local="NamespaceService_descriptor"/>
</list>
</property>
</bean>
-->
<bean id="NamespaceService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <bean id="NamespaceService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager"> <property name="transactionManager">
<ref bean="transactionManager"/> <ref bean="transactionManager"/>
@@ -130,26 +110,6 @@
<alias name="dictionaryService" alias="DictionaryService"/> <alias name="dictionaryService" alias="DictionaryService"/>
<!--
<bean id="DictionaryService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.service.cmr.dictionary.DictionaryService</value>
</property>
<property name="target">
<ref bean="dictionaryService"/>
</property>
<property name="interceptorNames">
<list>
<idref local="DictionaryService_transaction"/>
<idref local="AuditMethodInterceptor"/>
<idref local="exceptionTranslator"/>
<idref bean="DictionaryService_security"/>
<idref local="DictionaryService_descriptor"/>
</list>
</property>
</bean>
-->
<bean id="DictionaryService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor"> <bean id="DictionaryService_transaction" class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager"> <property name="transactionManager">
<ref bean="transactionManager"/> <ref bean="transactionManager"/>
@@ -260,25 +220,6 @@
<alias name="mimetypeService" alias="MimetypeService"/> <alias name="mimetypeService" alias="MimetypeService"/>
<!--
<bean id="MimetypeService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>org.alfresco.service.cmr.repository.MimetypeService</value>
</property>
<property name="target">
<ref bean="mimetypeService"/>
</property>
<property name="interceptorNames">
<list>
<idref local="AuditMethodInterceptor"/>
<idref local="exceptionTranslator"/>
<idref bean="MimetypeService_security"/>
<idref local="MimetypeService_descriptor"/>
</list>
</property>
</bean>
-->
<bean id="MimetypeService_descriptor" parent="AlfrescoServiceDescriptor"> <bean id="MimetypeService_descriptor" parent="AlfrescoServiceDescriptor">
<property name="interface"> <property name="interface">
<value>org.alfresco.service.cmr.repository.MimetypeService</value> <value>org.alfresco.service.cmr.repository.MimetypeService</value>

View File

@@ -211,6 +211,12 @@
<property name="authenticationService"> <property name="authenticationService">
<ref bean="authenticationServiceImpl" /> <ref bean="authenticationServiceImpl" />
</property> </property>
<property name="maxPermissionCheckTimeMillis">
<value>${lucene.query.maxPermissionCheckTimeMillis}</value>
</property>
<property name="maxPermissionChecks">
<value>${lucene.query.maxPermissionChecks}</value>
</property>
</bean> </bean>
<!-- Link up after method call security --> <!-- Link up after method call security -->

View File

@@ -20,6 +20,14 @@ index.recovery.mode=VALIDATE
# Lucene configuration # # Lucene configuration #
# #################### # # #################### #
# #
# The maximum time spent pruning query results
#
lucene.query.maxPermissionCheckTimeMillis=10000
#
# The maximum number of results to perform permission checks against
#
lucene.query.maxPermissionChecks=1000
#
# Millisecond threshold for text transformations # Millisecond threshold for text transformations
# Slower transformers will force the text extraction to be asynchronous # Slower transformers will force the text extraction to be asynchronous
# #

View File

@@ -1,90 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<server>
<!-- ==================================================================== -->
<!-- Defines TreeCache configuration -->
<!-- ==================================================================== -->
<mbean code="org.jboss.cache.TreeCache" name="jboss.cache:service=hibernateTreeCache">
<!--
Node locking level : SERIALIZABLE
REPEATABLE_READ (default)
READ_COMMITTED
READ_UNCOMMITTED
NONE
-->
<attribute name="IsolationLevel">READ_COMMITTED</attribute>
<!--
Valid modes are LOCAL
REPL_ASYNC
REPL_SYNC
-->
<attribute name="CacheMode">REPL_SYNC</attribute>
<!-- Name of cluster. Needs to be the same for all clusters, in order
to find each other
-->
<attribute name="ClusterName">Alfresco-Hibernate-Cluster</attribute>
<!-- JGroups protocol stack properties. Can also be a URL,
e.g. file:/home/bela/default.xml
<attribute name="ClusterProperties"></attribute>
-->
<attribute name="ClusterConfig">
<config>
<!-- UDP: if you have a multihomed machine,
set the bind_addr attribute to the appropriate NIC IP address -->
<!-- UDP: On Windows machines, because of the media sense feature
being broken with multicast (even after disabling media sense)
set the loopback attribute to true -->
<UDP mcast_addr="228.1.2.5" mcast_port="45577"
ip_ttl="64" ip_mcast="true"
mcast_send_buf_size="150000" mcast_recv_buf_size="80000"
ucast_send_buf_size="150000" ucast_recv_buf_size="80000"
loopback="false"/>
<PING timeout="2000" num_initial_members="3"
up_thread="false" down_thread="false"/>
<MERGE2 min_interval="10000" max_interval="20000"/>
<FD shun="true" up_thread="true" down_thread="true"/>#
<VERIFY_SUSPECT timeout="1500"
up_thread="false" down_thread="false"/>
<pbcast.NAKACK gc_lag="50" retransmit_timeout="600,1200,2400,4800"
up_thread="false" down_thread="false"/>
<pbcast.STABLE desired_avg_gossip="20000"
up_thread="false" down_thread="false"/>
<UNICAST timeout="600,1200,2400" window_size="100" min_threshold="10"
down_thread="false"/>
<FRAG frag_size="8192"
down_thread="false" up_thread="false"/>
<pbcast.GMS join_timeout="5000" join_retry_timeout="2000"
shun="true" print_local_addr="true"/>
<pbcast.STATE_TRANSFER up_thread="false" down_thread="false"/>
</config>
</attribute>
<!--
The max amount of time (in milliseconds) we wait until the
initial state (ie. the contents of the cache) are retrieved from
existing members in a clustered environment
-->
<attribute name="InitialStateRetrievalTimeout">20000</attribute>
<!--
Number of milliseconds to wait until all responses for a
synchronous call have been received.
-->
<attribute name="SyncReplTimeout">10000</attribute>
<!-- Max number of milliseconds to wait for a lock acquisition -->
<attribute name="LockAcquisitionTimeout">15000</attribute>
<!-- Name of the eviction policy class. Not supported now. -->
<attribute name="EvictionPolicyClass"></attribute>
</mbean>
</server>

View File

@@ -541,6 +541,65 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
if ( hasPseudoFileInterface(ctx)) if ( hasPseudoFileInterface(ctx))
{ {
// Make sure the parent folder has a file state, and the path exists
String[] paths = FileName.splitPath( path);
FileState fstate = ctx.getStateTable().findFileState( paths[0]);
if ( fstate == null)
{
NodeRef nodeRef = getNodeForPath(tree, paths[0]);
if ( nodeRef != null)
{
// Get the file information for the node
session.beginTransaction(transactionService, true);
finfo = cifsHelper.getFileInformation(nodeRef);
}
// Create the file state
fstate = ctx.getStateTable().findFileState( paths[0], true, true);
fstate.setFileStatus( FileStatus.DirectoryExists);
fstate.setNodeRef( nodeRef);
// Add pseudo files to the folder
getPseudoFileInterface( ctx).addPseudoFilesToFolder( session, tree, paths[0]);
// Debug
if ( logger.isInfoEnabled())
logger.info( "Added file state for pseudo files folder (getinfo) - " + paths[0]);
}
else if ( fstate.hasPseudoFiles() == false)
{
// Make sure the file state has the node ref
if ( fstate.hasNodeRef() == false)
{
// Create the transaction
session.beginTransaction(transactionService, true);
// Get the node for the folder path
fstate.setNodeRef( getNodeForPath( tree, paths[0]));
}
// Add pseudo files for the parent folder
getPseudoFileInterface( ctx).addPseudoFilesToFolder( session, tree, paths[0]);
// Debug
if ( logger.isInfoEnabled())
logger.info( "Added pseudo files for folder (exists) - " + paths[0]);
}
// Get the pseudo file // Get the pseudo file
PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( session, tree, path); PseudoFile pfile = getPseudoFileInterface(ctx).getPseudoFile( session, tree, path);
@@ -868,6 +927,7 @@ public class ContentDiskDriver extends AlfrescoDiskDriver implements DiskInterfa
// Check if the file name is a pseudo file name // Check if the file name is a pseudo file name
if ( getPseudoFileInterface( ctx).isPseudoFile(sess, tree, name)) { if ( getPseudoFileInterface( ctx).isPseudoFile(sess, tree, name)) {
// Make sure the parent folder has a file state, and the path exists // Make sure the parent folder has a file state, and the path exists
String[] paths = FileName.splitPath( name); String[] paths = FileName.splitPath( name);

View File

@@ -57,6 +57,7 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService
private static final String MSG_ERR_ALREADY_WORKING_COPY = "coci_service.err_workingcopy_checkout"; private static final String MSG_ERR_ALREADY_WORKING_COPY = "coci_service.err_workingcopy_checkout";
private static final String MSG_ERR_NOT_AUTHENTICATED = "coci_service.err_not_authenticated"; private static final String MSG_ERR_NOT_AUTHENTICATED = "coci_service.err_not_authenticated";
private static final String MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE = "coci_service.err_workingcopy_has_no_mimetype"; private static final String MSG_ERR_WORKINGCOPY_HAS_NO_MIMETYPE = "coci_service.err_workingcopy_has_no_mimetype";
private static final String MSG_ALREADY_CHECKEDOUT = "coci_service.err_already_checkedout";
/** /**
* Extension character, used to recalculate the working copy names * Extension character, used to recalculate the working copy names
@@ -189,6 +190,12 @@ public class CheckOutCheckInServiceImpl implements CheckOutCheckInService
QName destinationAssocTypeQName, QName destinationAssocTypeQName,
QName destinationAssocQName) QName destinationAssocQName)
{ {
LockType lockType = this.lockService.getLockType(nodeRef);
if (LockType.READ_ONLY_LOCK.equals(lockType) == true)
{
throw new CheckOutCheckInServiceException(MSG_ALREADY_CHECKEDOUT);
}
// Make sure we are no checking out a working copy node // Make sure we are no checking out a working copy node
if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == true) if (this.nodeService.hasAspect(nodeRef, ContentModel.ASPECT_WORKING_COPY) == true)
{ {

View File

@@ -431,4 +431,31 @@ public class CheckOutCheckInServiceImplTest extends BaseSpringTest
assertNull(wk3); assertNull(wk3);
} }
public void testAR1056()
{
// Check out the node
NodeRef workingCopy = this.cociService.checkout(
this.nodeRef,
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}workingCopy"));
assertNotNull(workingCopy);
// Try and check the same node out again
try
{
this.cociService.checkout(
this.nodeRef,
this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}workingCopy2"));
fail("This document has been checked out twice.");
}
catch (Exception exception)
{
// Good because we shouldnt be able to checkout a document twice
}
}
} }

View File

@@ -0,0 +1,91 @@
/**
*
*/
package org.alfresco.repo.jscript;
import java.io.Serializable;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.util.ParameterCheck;
import org.mozilla.javascript.Scriptable;
/**
* Object representing an association
*
* @author Roy Wetherall
*/
public class Association implements Scopeable, Serializable
{
/** Serial version UUID*/
private static final long serialVersionUID = 897788515655487131L;
/** Service registry **/
private ServiceRegistry services;
/** Script scope **/
private Scriptable scope;
/** Association reference **/
private AssociationRef assocRef;
public Association(ServiceRegistry services, AssociationRef assocRef)
{
this(services, assocRef, null);
}
public Association(ServiceRegistry services, AssociationRef assocRef, Scriptable scope)
{
ParameterCheck.mandatory("Service registry", services);
ParameterCheck.mandatory("Association reference", assocRef);
this.services = services;
this.assocRef = assocRef;
if (scope != null)
{
this.scope = scope;
}
}
/**
* @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable)
*/
public void setScope(Scriptable scope)
{
this.scope = scope;
}
public AssociationRef getAssociationRef()
{
return this.assocRef;
}
public String getType()
{
return assocRef.getTypeQName().toString();
}
public String jsGet_type()
{
return getType();
}
public Node getSource()
{
return (Node)new ValueConverter().convertValueForScript(this.services, this.scope, null, assocRef.getSourceRef());
}
public Node jsGet_source()
{
return getSource();
}
public Node getTarget()
{
return (Node)new ValueConverter().convertValueForScript(this.services, this.scope, null, assocRef.getTargetRef());
}
public Node jsGet_target()
{
return getTarget();
}
}

View File

@@ -0,0 +1,108 @@
/**
*
*/
package org.alfresco.repo.jscript;
import java.io.Serializable;
import org.alfresco.service.ServiceRegistry;
import org.mozilla.javascript.Scriptable;
/**
* Object representing the behaviour information
*
* @author Roy Wetherall
*/
public class Behaviour implements Scopeable, Serializable
{
/** Serial version UID **/
private static final long serialVersionUID = 1936017361886646100L;
/** Service registry **/
private ServiceRegistry services;
/** Script scope **/
private Scriptable scope;
/** The name of the policy that this behaviour is linked to **/
private String name;
/** The behaviour argument values **/
private Object[] args;
/** Cached js converted argument values **/
private Serializable[] jsArgs;
/**
* Constructor
*
* @param services the service registry
* @param name the name of the policy associated with this behaviour
* @param args the argument values
*/
public Behaviour(ServiceRegistry services, String name, Object[] args)
{
this.services = services;
this.name = name;
this.args = args;
}
/**
* @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable)
*/
public void setScope(Scriptable scope)
{
this.scope = scope;
}
/**
* Get the policy name
*
* @return the name of the policy
*/
public String getName()
{
return this.name;
}
/**
* JS accessor method
*
* @return the name of the policy
*/
public String jsGet_name()
{
return getName();
}
/**
* The argument values
*
* @return array containing the argument values
*/
public Serializable[] getArgs()
{
if (this.jsArgs == null)
{
ValueConverter valueConverter = new ValueConverter();
this.jsArgs = new Serializable[args.length];
int index = 0;
for (Object arg : this.args)
{
this.jsArgs[index] = valueConverter.convertValueForScript(services, this.scope, null, (Serializable)arg);
index ++;
}
}
return this.jsArgs;
}
/**
* JS accessor method
*
* @return array containing the argument values
*/
public Serializable[] jsGet_args()
{
return getArgs();
}
}

View File

@@ -0,0 +1,122 @@
/**
*
*/
package org.alfresco.repo.jscript;
import java.io.Serializable;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.util.ParameterCheck;
import org.mozilla.javascript.Scriptable;
/**
* Object representing a child association
*
* @author Roy Wetherall
*/
public class ChildAssociation implements Scopeable, Serializable
{
/** Serial version UUID **/
private static final long serialVersionUID = -2122640697340663213L;
/** Service registry **/
private ServiceRegistry services;
/** Script scope **/
private Scriptable scope;
/** Child association reference **/
private ChildAssociationRef childAssocRef;
public ChildAssociation(ServiceRegistry services, ChildAssociationRef childAssocRef)
{
this(services, childAssocRef, null);
}
/**
* Constructor
*
* @param services
* @param childAssocRef
*/
public ChildAssociation(ServiceRegistry services, ChildAssociationRef childAssocRef, Scriptable scope)
{
ParameterCheck.mandatory("Service registry", services);
ParameterCheck.mandatory("Child association reference", childAssocRef);
this.services = services;
this.childAssocRef = childAssocRef;
if (scope != null)
{
this.scope = scope;
}
}
/**
* @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable)
*/
public void setScope(Scriptable scope)
{
this.scope = scope;
}
public ChildAssociationRef getChildAssociationRef()
{
return this.childAssocRef;
}
public String getType()
{
return childAssocRef.getTypeQName().toString();
}
public String jsGet_type()
{
return getType();
}
public String getName()
{
return childAssocRef.getQName().toString();
}
public String jsGet_name()
{
return getName();
}
public Node getParent()
{
return (Node)new ValueConverter().convertValueForScript(this.services, this.scope, null, childAssocRef.getParentRef());
}
public Node jsGet_parent()
{
return getParent();
}
public Node getChild()
{
return (Node)new ValueConverter().convertValueForScript(this.services, this.scope, null, childAssocRef.getChildRef());
}
public Node jsGet_child()
{
return getChild();
}
public boolean isPrimary()
{
return this.childAssocRef.isPrimary();
}
public int getNthSibling()
{
return this.childAssocRef.getNthSibling();
}
public int jsGet_nthSibling()
{
return getNthSibling();
}
}

View File

@@ -0,0 +1,87 @@
/**
*
*/
package org.alfresco.repo.jscript;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.service.cmr.repository.ScriptException;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.util.ParameterCheck;
/**
* Classpath script location object.
*
* @author Roy Wetherall
*
*/
public class ClasspathScriptLocation implements ScriptLocation
{
/** Classpath location **/
private String location;
/**
* Constructor
*
* @param location the classpath location
*/
public ClasspathScriptLocation(String location)
{
ParameterCheck.mandatory("Location", location);
this.location = location;
}
/**
* @see org.alfresco.service.cmr.repository.ScriptLocation#getReader()
*/
public Reader getReader()
{
Reader reader = null;
try
{
InputStream stream = getClass().getClassLoader().getResourceAsStream(location);
if (stream == null)
{
throw new AlfrescoRuntimeException("Unable to load classpath resource: " + location);
}
reader = new InputStreamReader(stream);
}
catch (Throwable err)
{
throw new ScriptException("Failed to load classpath resource '" + location + "': " + err.getMessage(), err);
}
return reader;
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
else if (obj == null || !(obj instanceof ClasspathScriptLocation))
{
return false;
}
ClasspathScriptLocation other = (ClasspathScriptLocation)obj;
return this.location.equals(other.location);
}
@Override
public int hashCode()
{
return 37 * this.location.hashCode();
}
@Override
public String toString()
{
return this.location.toString();
}
}

View File

@@ -33,9 +33,11 @@ import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.ScriptException; import org.alfresco.service.cmr.repository.ScriptException;
import org.alfresco.service.cmr.repository.ScriptImplementation; import org.alfresco.service.cmr.repository.ScriptImplementation;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.repository.TemplateImageResolver; import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.util.ParameterCheck;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.Scriptable;
@@ -166,6 +168,37 @@ public class RhinoScriptService implements ScriptService
} }
} }
/**
* @see org.alfresco.service.cmr.repository.ScriptService#executeScript(org.alfresco.service.cmr.repository.ScriptLocation, java.util.Map)
*/
public Object executeScript(ScriptLocation location, Map<String, Object> model)
throws ScriptException
{
ParameterCheck.mandatory("Location", location);
if (logger.isDebugEnabled())
{
logger.debug("Executing script: " + location.toString());
}
Reader reader = null;
try
{
return executeScriptImpl(location.getReader(), model);
}
catch (Throwable err)
{
throw new ScriptException("Failed to execute script '" + location.toString() + "': " + err.getMessage(), err);
}
finally
{
if (reader != null)
{
try {reader.close();} catch (IOException ioErr) {}
}
}
}
/** /**
* @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map) * @see org.alfresco.service.cmr.repository.ScriptService#executeScriptString(java.lang.String, java.util.Map)
*/ */

View File

@@ -0,0 +1,189 @@
/*
* 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.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import org.alfresco.repo.policy.BaseBehaviour;
import org.alfresco.repo.policy.PolicyException;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.util.ParameterCheck;
/**
* JavaScript behaviour implementation
*
* @author Roy Wetherall
*/
public class ScriptBehaviour extends BaseBehaviour
{
private ServiceRegistry serviceRegistry;
private ScriptLocation location;
public ScriptBehaviour()
{
super();
}
public ScriptBehaviour(ServiceRegistry serviceRegistry, ScriptLocation location)
{
this(serviceRegistry, location, NotificationFrequency.EVERY_EVENT);
}
public ScriptBehaviour(ServiceRegistry serviceRegistry, ScriptLocation location, NotificationFrequency frequency)
{
super(frequency);
ParameterCheck.mandatory("Location", location);
ParameterCheck.mandatory("ServiceRegistry", serviceRegistry);
this.location = location;
this.serviceRegistry = serviceRegistry;
}
public void setServiceRegistry(ServiceRegistry serviceRegistry)
{
this.serviceRegistry = serviceRegistry;
}
public void setLocation(ScriptLocation location)
{
this.location = location;
}
@Override
public String toString()
{
return "JavaScript behaviour[location = " + this.location.toString() + "]";
}
@SuppressWarnings("unchecked")
public synchronized <T> T getInterface(Class<T> policy)
{
ParameterCheck.mandatory("Policy class", policy);
Object proxy = proxies.get(policy);
if (proxy == null)
{
Method[] policyIFMethods = policy.getMethods();
if (policyIFMethods.length != 1)
{
throw new PolicyException("Policy interface " + policy.getCanonicalName() + " must have only one method");
}
InvocationHandler handler = new JavaScriptInvocationHandler(this);
proxy = Proxy.newProxyInstance(policy.getClassLoader(), new Class[]{policy}, handler);
proxies.put(policy, proxy);
}
return (T)proxy;
}
/**
* JavaScript Invocation Handler
*
* @author Roy Wetherall
*/
private static class JavaScriptInvocationHandler implements InvocationHandler
{
private ScriptBehaviour behaviour;
private JavaScriptInvocationHandler(ScriptBehaviour behaviour)
{
this.behaviour = behaviour;
}
/**
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
// Handle Object level methods
if (method.getName().equals("toString"))
{
return toString();
}
else if (method.getName().equals("hashCode"))
{
return hashCode();
}
else if (method.getName().equals("equals"))
{
if (Proxy.isProxyClass(args[0].getClass()))
{
return equals(Proxy.getInvocationHandler(args[0]));
}
return false;
}
// Delegate to designated method pointer
if (behaviour.isEnabled())
{
try
{
behaviour.disable();
return invokeScript(method, args);
}
finally
{
behaviour.enable();
}
}
return null;
}
private Object invokeScript(Method method, Object[] args)
{
// Build the model
Map<String, Object> model = new HashMap<String, Object>(1);
model.put("behaviour", new org.alfresco.repo.jscript.Behaviour(this.behaviour.serviceRegistry, method.getName(), args));
// Execute the script
return this.behaviour.serviceRegistry.getScriptService().executeScript(this.behaviour.location, model);
}
@Override
public boolean equals(Object obj)
{
if (obj == this)
{
return true;
}
else if (obj == null || !(obj instanceof JavaScriptInvocationHandler))
{
return false;
}
JavaScriptInvocationHandler other = (JavaScriptInvocationHandler)obj;
return behaviour.location.equals(other.behaviour.location);
}
@Override
public int hashCode()
{
return 37 * behaviour.location.hashCode();
}
@Override
public String toString()
{
return "JavaScriptBehaviour[location=" + behaviour.location.toString() + "]";
}
}
}

View File

@@ -0,0 +1,175 @@
/*
* 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.HashMap;
import java.util.Map;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.node.NodeServicePolicies;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.repo.security.authentication.AuthenticationComponent;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.ScriptLocation;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.util.BaseSpringTest;
/**
*
*
* @author Roy Wetherall
*/
public class ScriptBehaviourTest extends BaseSpringTest
{
private ServiceRegistry serviceRegistry;
private NodeService nodeService;
private PolicyComponent policyComponent;
private StoreRef storeRef;
private NodeRef folderNodeRef;
protected String[] getConfigLocations()
{
return new String[] { "classpath:org/alfresco/repo/jscript/test-context.xml" };
}
/**
* On setup in transaction implementation
*/
@Override
protected void onSetUpInTransaction()
throws Exception
{
// Get the required services
this.nodeService = (NodeService)this.applicationContext.getBean("nodeService");
this.policyComponent = (PolicyComponent)this.applicationContext.getBean("policyComponent");
this.serviceRegistry = (ServiceRegistry)this.applicationContext.getBean("ServiceRegistry");
AuthenticationComponent authenticationComponent = (AuthenticationComponent)this.applicationContext.getBean("authenticationComponent");
authenticationComponent.setCurrentUser("admin");
// Create the store and get the root node reference
this.storeRef = this.nodeService.createStore(StoreRef.PROTOCOL_WORKSPACE, "Test_" + System.currentTimeMillis());
NodeRef rootNodeRef = this.nodeService.getRootNode(storeRef);
// Create folder node
Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
props.put(ContentModel.PROP_NAME, "TestFolder");
ChildAssociationRef childAssocRef = this.nodeService.createNode(
rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("{test}TestFolder"),
ContentModel.TYPE_FOLDER,
props);
this.folderNodeRef = childAssocRef.getChildRef();
}
public void testEnableDiableBehaviour()
{
// Register the onCreateNode behaviour script
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/jscript/test_onCreateNode_cmContent.js");
ScriptBehaviour behaviour = new ScriptBehaviour(this.serviceRegistry, location);
this.policyComponent.bindClassBehaviour(
QName.createQName(NodeServicePolicies.OnCreateNodePolicy.NAMESPACE, "onCreateNode"),
ContentModel.TYPE_CONTENT,
behaviour);
behaviour.disable();
// Create a content node
Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
props.put(ContentModel.PROP_NAME, "myDoc.txt");
ChildAssociationRef childAssoc = this.nodeService.createNode(
this.folderNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myDoc.txt"),
ContentModel.TYPE_CONTENT,
props);
assertFalse(this.nodeService.hasAspect(childAssoc.getChildRef(), ContentModel.ASPECT_TITLED));
behaviour.enable();
Map<QName, Serializable> props2 = new HashMap<QName, Serializable>(1);
props2.put(ContentModel.PROP_NAME, "myDoc1.txt");
ChildAssociationRef childAssoc2 = this.nodeService.createNode(
this.folderNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myDoc1.txt"),
ContentModel.TYPE_CONTENT,
props2);
assertTrue(this.nodeService.hasAspect(childAssoc2.getChildRef(), ContentModel.ASPECT_TITLED));
}
public void testClasspathLocationBehaviour()
{
// Register the onCreateNode behaviour script
ScriptLocation location = new ClasspathScriptLocation("org/alfresco/repo/jscript/test_onCreateNode_cmContent.js");
ScriptBehaviour behaviour = new ScriptBehaviour(this.serviceRegistry, location);
this.policyComponent.bindClassBehaviour(
QName.createQName(NodeServicePolicies.OnCreateNodePolicy.NAMESPACE, "onCreateNode"),
ContentModel.TYPE_CONTENT,
behaviour);
// Create a content node
Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
props.put(ContentModel.PROP_NAME, "myDoc.txt");
ChildAssociationRef childAssoc = this.nodeService.createNode(
this.folderNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myDoc.txt"),
ContentModel.TYPE_CONTENT,
props);
// Since the behavoiour will have been run check that the titled aspect has been applied
assertTrue(this.nodeService.hasAspect(childAssoc.getChildRef(), ContentModel.ASPECT_TITLED));
}
public void testSpringConfiguredBehaviour()
{
this.nodeService.addAspect(this.folderNodeRef, ContentModel.ASPECT_COUNTABLE, null);
assertTrue(this.nodeService.hasAspect(this.folderNodeRef, ContentModel.ASPECT_TITLED));
// Create a couple of nodes
Map<QName, Serializable> props = new HashMap<QName, Serializable>(1);
props.put(ContentModel.PROP_NAME, "myDoc.txt");
ChildAssociationRef childAssoc = this.nodeService.createNode(
this.folderNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "myDoc.txt"),
ContentModel.TYPE_CONTENT,
props);
Map<QName, Serializable> props2 = new HashMap<QName, Serializable>(1);
props2.put(ContentModel.PROP_NAME, "folder2");
ChildAssociationRef childAssoc2 = this.nodeService.createNode(
this.folderNodeRef,
ContentModel.ASSOC_CONTAINS,
QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "folder2"),
ContentModel.TYPE_FOLDER,
props2);
this.nodeService.addChild(childAssoc2.getChildRef(), childAssoc.getChildRef(), ContentModel.ASSOC_CONTAINS, QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "linked"));
assertTrue(this.nodeService.hasAspect(childAssoc.getChildRef(), ContentModel.ASPECT_VERSIONABLE));
}
}

View File

@@ -20,6 +20,7 @@ import java.io.StringReader;
import org.alfresco.error.AlfrescoRuntimeException; import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.search.impl.lucene.LuceneQueryParser;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -101,9 +102,8 @@ public final class Search implements Scopeable
*/ */
public Node findNode(String ref) public Node findNode(String ref)
{ {
String query = ref.replace(":", "\\:"); String query = "ID:" + LuceneQueryParser.escape(ref);
query = query.replace("/", "\\/"); Node[] result = query(query);
Node[] result = query("ID:" + query);
if (result.length == 1) if (result.length == 1)
{ {
return result[0]; return result[0];

View File

@@ -23,7 +23,10 @@ import java.util.Date;
import java.util.List; import java.util.List;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.mozilla.javascript.Context; import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.NativeArray;
@@ -38,7 +41,6 @@ import org.mozilla.javascript.Wrapper;
*/ */
public class ValueConverter public class ValueConverter
{ {
/** /**
* Convert an object from any repository serialized value to a valid script object. * Convert an object from any repository serialized value to a valid script object.
* This includes converting Collection multi-value properties into JavaScript Array objects. * This includes converting Collection multi-value properties into JavaScript Array objects.
@@ -63,6 +65,18 @@ public class ValueConverter
// so they can be used as objects within a template // so they can be used as objects within a template
value = new Node(((NodeRef)value), services, null, scope); value = new Node(((NodeRef)value), services, null, scope);
} }
else if (value instanceof QName || value instanceof StoreRef)
{
value = value.toString();
}
else if (value instanceof ChildAssociationRef)
{
value = new ChildAssociation(services, (ChildAssociationRef)value, scope);
}
else if (value instanceof AssociationRef)
{
value = new Association(services, (AssociationRef)value, scope);
}
else if (value instanceof Date) else if (value instanceof Date)
{ {
// convert Date to JavaScript native Date object // convert Date to JavaScript native Date object
@@ -110,6 +124,14 @@ public class ValueConverter
// convert back to NodeRef // convert back to NodeRef
value = ((Node)value).getNodeRef(); value = ((Node)value).getNodeRef();
} }
else if (value instanceof ChildAssociation)
{
value = ((ChildAssociation)value).getChildAssociationRef();
}
else if (value instanceof Association)
{
value = ((Association)value).getAssociationRef();
}
else if (value instanceof Wrapper) else if (value instanceof Wrapper)
{ {
// unwrap a Java object from a JavaScript wrapper // unwrap a Java object from a JavaScript wrapper

View File

@@ -0,0 +1,51 @@
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE beans PUBLIC '-//SPRING//DTD BEAN//EN' 'http://www.springframework.org/dtd/spring-beans.dtd'>
<beans>
<import resource="classpath:alfresco/application-context.xml" />
<bean id="onAddAspect" class="org.alfresco.repo.policy.registration.ClassPolicyRegistration" parent="policyRegistration">
<property name="policyName">
<value>{http://www.alfresco.org}onAddAspect</value>
</property>
<property name="className">
<value>{http://www.alfresco.org/model/content/1.0}countable</value>
</property>
<property name="behaviour">
<bean class="org.alfresco.repo.jscript.ScriptBehaviour" parent="scriptBehaviour">
<property name="location">
<bean class="org.alfresco.repo.jscript.ClasspathScriptLocation">
<constructor-arg>
<value>org/alfresco/repo/jscript/test_onAddAspect_cmCountable.js</value>
</constructor-arg>
</bean>
</property>
</bean>
</property>
</bean>
<bean id="onCreateChildAssociation" class="org.alfresco.repo.policy.registration.AssociationPolicyRegistration" parent="policyRegistration">
<property name="policyName">
<value>{http://www.alfresco.org}onCreateChildAssociation</value>
</property>
<property name="className">
<value>{http://www.alfresco.org/model/content/1.0}folder</value>
</property>
<property name="associationType">
<value>{http://www.alfresco.org/model/content/1.0}contains</value>
</property>
<property name="behaviour">
<bean class="org.alfresco.repo.jscript.ScriptBehaviour" parent="scriptBehaviour">
<property name="location">
<bean class="org.alfresco.repo.jscript.ClasspathScriptLocation">
<constructor-arg>
<value>org/alfresco/repo/jscript/test_onCreateChildAssociation.js</value>
</constructor-arg>
</bean>
</property>
</bean>
</property>
</bean>
</beans>

View File

@@ -0,0 +1,55 @@
logger.log("The counatble aspect has been added");
var scriptFailed = false;
// Have a look at the behaviour object that should have been passed
if (behaviour == null)
{
logger.log("The behaviour object has not been set.");
scriptFailed = true;
}
// Check the name of the behaviour
if (behaviour.name == null && behaviour.name != "onAddAspect")
{
logger.log("The behaviour name has not been set correctly.");
scriptFailed = true;
}
else
{
logger.log("Behaviour name: " + behaviour.name);
}
// Check the arguments
if (behaviour.args == null)
{
logger.log("The args have not been set.")
scriptFailed = true;
}
else
{
if (behaviour.args.length == 2)
{
var nodeRef = behaviour.args[0];
var aspectType = behaviour.args[1];
logger.log("NodeRef: " + nodeRef.id);
logger.log("Type: " + aspectType);
if (aspectType != "{http://www.alfresco.org/model/content/1.0}countable")
{
logger.log("Aspect type is incorrect");
scriptFailed = true;
}
}
else
{
logger.log("The number of arguments is incorrect.")
scriptFailed = true;
}
}
if (scriptFailed == false)
{
nodeRef.addAspect("cm:titled");
nodeRef.save();
}

View File

@@ -0,0 +1,51 @@
var scriptFailed = false;
// Have a look at the behaviour object that should have been passed
if (behaviour == null)
{
logger.log("The behaviour object has not been set.");
scriptFailed = true;
}
// Check the name of the behaviour
if (behaviour.name == null && behaviour.name != "onCreateChildAssociation")
{
logger.log("The behaviour name has not been set correctly.");
scriptFailed = true;
}
else
{
logger.log("Behaviour name: " + behaviour.name);
}
// Check the arguments
if (behaviour.args == null)
{
logger.log("The args have not been set.")
scriptFailed = true;
}
else
{
if (behaviour.args.length == 1)
{
var childAssoc = behaviour.args[0];
logger.log("Assoc type: " + childAssoc.type);
logger.log("Assoc name: " + childAssoc.name);
logger.log("Parent node: " + childAssoc.parent.id);
logger.log("Child node: " + childAssoc.child.id);
logger.log("Is primary: " + childAssoc.isPrimary());
logger.log("Nth sibling: " + childAssoc.nthSibling);
}
else
{
logger.log("The number of arguments is incorrect.")
scriptFailed = true;
}
}
if (scriptFailed == false)
{
childAssoc.child.addAspect("cm:versionable");
childAssoc.child.save();
}

View File

@@ -0,0 +1,51 @@
var scriptFailed = false;
// Have a look at the behaviour object that should have been passed
if (behaviour == null)
{
logger.log("The behaviour object has not been set.");
scriptFailed = true;
}
// Check the name of the behaviour
if (behaviour.name == null && behaviour.name != "onCreateNode")
{
logger.log("The behaviour name has not been set correctly.");
scriptFailed = true;
}
else
{
logger.log("Behaviour name: " + behaviour.name);
}
// Check the arguments
if (behaviour.args == null)
{
logger.log("The args have not been set.")
scriptFailed = true;
}
else
{
if (behaviour.args.length == 1)
{
var childAssoc = behaviour.args[0];
logger.log("Assoc type: " + childAssoc.type);
logger.log("Assoc name: " + childAssoc.name);
logger.log("Parent node: " + childAssoc.parent.id);
logger.log("Child node: " + childAssoc.child.id);
logger.log("Is primary: " + childAssoc.isPrimary());
logger.log("Nth sibling: " + childAssoc.nthSibling);
}
else
{
logger.log("The number of arguments is incorrect.")
scriptFailed = true;
}
}
if (scriptFailed == false)
{
childAssoc.child.addAspect("cm:titled");
childAssoc.child.save();
}

View File

@@ -0,0 +1,109 @@
/**
*
*/
package org.alfresco.repo.policy;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.alfresco.repo.policy.Behaviour.NotificationFrequency;
import org.alfresco.util.ParameterCheck;
/**
* Base behaviour implementation
*
* @author Roy Wetherall
*/
public abstract class BaseBehaviour implements Behaviour
{
/** The notification frequency */
protected NotificationFrequency frequency = NotificationFrequency.EVERY_EVENT;
/** Disabled stack **/
private StackThreadLocal disabled = new StackThreadLocal();
/** Proxies **/
protected Map<Class, Object> proxies = new HashMap<Class, Object>();
/**
* Default constructor
*/
public BaseBehaviour()
{
// Default constructor
}
/**
* Constructor
*
* @param frequency the notification frequency
*/
public BaseBehaviour(NotificationFrequency frequency)
{
ParameterCheck.mandatory("Frequency", frequency);
this.frequency = frequency;
}
public void setNotificationFrequency(NotificationFrequency frequency)
{
this.frequency = frequency;
}
/**
* Disable this behaviour for the curent thread
*/
public void disable()
{
Stack<Integer> stack = disabled.get();
stack.push(hashCode());
}
/**
* Enable this behaviour for the current thread
*/
public void enable()
{
Stack<Integer> stack = disabled.get();
if (stack.peek().equals(hashCode()) == false)
{
throw new PolicyException("Cannot enable " + this.toString() + " at this time - mismatched with disable calls");
}
stack.pop();
}
/**
* Indicates whether the this behaviour is current enabled or not
*
* @return true if the behaviour is enabled, false otherwise
*/
public boolean isEnabled()
{
Stack<Integer> stack = disabled.get();
return stack.search(hashCode()) == -1;
}
/**
* Get the notification frequency
*
* @return the notification frequency
*/
public NotificationFrequency getNotificationFrequency()
{
return frequency;
}
/**
* Stack specific Thread Local
*
* @author David Caruana
*/
class StackThreadLocal extends ThreadLocal<Stack<Integer>>
{
@Override
protected Stack<Integer> initialValue()
{
return new Stack<Integer>();
}
}
}

View File

@@ -20,9 +20,6 @@ import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
import org.alfresco.util.ParameterCheck; import org.alfresco.util.ParameterCheck;
@@ -36,23 +33,13 @@ import org.alfresco.util.ParameterCheck;
* @author David Caruana * @author David Caruana
* *
*/ */
public class JavaBehaviour implements Behaviour public class JavaBehaviour extends BaseBehaviour
{ {
// The object instance holding the method // The object instance holding the method
private Object instance; Object instance;
// The method name // The method name
private String method; String method;
// Notification Frequency
private NotificationFrequency frequency;
// Cache of interface proxies (by interface class)
private Map<Class, Object> proxies = new HashMap<Class, Object>();
// Enable / Disable invocation of behaviour
private StackThreadLocal disabled = new StackThreadLocal();
/** /**
* Construct. * Construct.
@@ -73,17 +60,20 @@ public class JavaBehaviour implements Behaviour
*/ */
public JavaBehaviour(Object instance, String method, NotificationFrequency frequency) public JavaBehaviour(Object instance, String method, NotificationFrequency frequency)
{ {
super(frequency);
ParameterCheck.mandatory("Instance", instance); ParameterCheck.mandatory("Instance", instance);
ParameterCheck.mandatory("Method", method); ParameterCheck.mandatory("Method", method);
this.instance = instance;
this.method = method; this.method = method;
this.frequency = frequency; this.instance = instance;
} }
/* (non-Javadoc) @Override
* @see org.alfresco.repo.policy.Behaviour#getInterface(java.lang.Class) public String toString()
*/ {
return "Java method[class=" + instance.getClass().getName() + ", method=" + method + "]";
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public synchronized <T> T getInterface(Class<T> policy) public synchronized <T> T getInterface(Class<T> policy)
{ {
@@ -98,52 +88,6 @@ public class JavaBehaviour implements Behaviour
return (T)proxy; return (T)proxy;
} }
/* (non-Javadoc)
* @see org.alfresco.repo.policy.Behaviour#disable()
*/
public void disable()
{
Stack<Integer> stack = disabled.get();
stack.push(hashCode());
}
/* (non-Javadoc)
* @see org.alfresco.repo.policy.Behaviour#enable()
*/
public void enable()
{
Stack<Integer> stack = disabled.get();
if (stack.peek().equals(hashCode()) == false)
{
throw new PolicyException("Cannot enable " + this.toString() + " at this time - mismatched with disable calls");
}
stack.pop();
}
/* (non-Javadoc)
* @see org.alfresco.repo.policy.Behaviour#isEnabled()
*/
public boolean isEnabled()
{
Stack<Integer> stack = disabled.get();
return stack.search(hashCode()) == -1;
}
/* (non-Javadoc)
* @see org.alfresco.repo.policy.Behaviour#getNotificationFrequency()
*/
public NotificationFrequency getNotificationFrequency()
{
return frequency;
}
@Override
public String toString()
{
return "Java method[class=" + instance.getClass().getName() + ", method=" + method + "]";
}
/** /**
* Gets the Invocation Handler. * Gets the Invocation Handler.
* *
@@ -153,7 +97,7 @@ public class JavaBehaviour implements Behaviour
* @param policyIF the policy interface class * @param policyIF the policy interface class
* @return the invocation handler * @return the invocation handler
*/ */
private <T> InvocationHandler getInvocationHandler(Object instance, String method, Class<T> policyIF) <T> InvocationHandler getInvocationHandler(Object instance, String method, Class<T> policyIF)
{ {
Method[] policyIFMethods = policyIF.getMethods(); Method[] policyIFMethods = policyIF.getMethods();
if (policyIFMethods.length != 1) if (policyIFMethods.length != 1)
@@ -173,22 +117,6 @@ public class JavaBehaviour implements Behaviour
} }
} }
/**
* Stack specific Thread Local
*
* @author David Caruana
*/
private class StackThreadLocal extends ThreadLocal<Stack<Integer>>
{
@Override
protected Stack<Integer> initialValue()
{
return new Stack<Integer>();
}
}
/** /**
* Java Method Invocation Handler * Java Method Invocation Handler
* *

View File

@@ -0,0 +1,46 @@
/**
*
*/
package org.alfresco.repo.policy.registration;
import org.alfresco.service.namespace.QName;
/**
* Deals with the registration of an association policy
*
* @author Roy Wetherall
*
*/
public class AssociationPolicyRegistration extends PolicyRegistration
{
/** The association type **/
private QName associationType;
/**
* Set the association type
*
* @param associationType the association type
*/
public void setAssociationType(String associationType)
{
this.associationType = QName.createQName(associationType);
}
/**
* @see org.alfresco.repo.policy.registration.PolicyRegistration#register()
*/
@Override
public void register()
{
// Register the association behaviour
if (this.associationType == null)
{
this.policyComponent.bindAssociationBehaviour(this.policyName, this.className, this.behaviour);
}
else
{
this.policyComponent.bindAssociationBehaviour(this.policyName, this.className, this.associationType, this.behaviour);
}
}
}

View File

@@ -0,0 +1,24 @@
/**
*
*/
package org.alfresco.repo.policy.registration;
/**
* Deal with the registration of a class policy
*
* @author Roy Wetherall
*
*/
public class ClassPolicyRegistration extends PolicyRegistration
{
/**
* @see org.alfresco.repo.policy.registration.PolicyRegistration#register()
*/
@Override
public void register()
{
// Register the class behaviour
this.policyComponent.bindClassBehaviour(this.policyName, this.className, this.behaviour);
}
}

View File

@@ -0,0 +1,76 @@
/**
*
*/
package org.alfresco.repo.policy.registration;
import org.alfresco.repo.policy.Behaviour;
import org.alfresco.repo.policy.PolicyComponent;
import org.alfresco.service.namespace.QName;
/**
* Bean that can be configured in spring to register a policy bahaviour
*
* @author Roy Wetherall
*/
public abstract class PolicyRegistration
{
/** The policy componenet **/
protected PolicyComponent policyComponent;
/** The policy name **/
protected QName policyName;
/** The class name **/
protected QName className;
/** The behaviour **/
protected Behaviour behaviour;
/**
* Set the policy component
*
* @param policyComponent the policy componenet
*/
public void setPolicyComponent(PolicyComponent policyComponent)
{
this.policyComponent = policyComponent;
}
/**
* Set the policy name
*
* @param policyName the policy name
*/
public void setPolicyName(String policyName)
{
this.policyName = QName.createQName(policyName);
}
/**
* Set the class name
*
* @param className the class name
*/
public void setClassName(String className)
{
this.className = QName.createQName(className);
}
/**
* Set the behaviour
*
* @param behaviour the behaviour
*/
public void setBehaviour(Behaviour behaviour)
{
this.behaviour = behaviour;
}
/**
* Registers the behaviour with the policy component for the policy and type specified. Called
* as the init method of the bean.
*
* TODO supoort service registration?
*/
public abstract void register();
}

View File

@@ -259,8 +259,13 @@ public class LuceneSearcherImpl2 extends LuceneBase2 implements LuceneSearcher2
hits = searcher.search(query); hits = searcher.search(query);
} }
return new LuceneResultSet(hits, searcher, nodeService, searchParameters.getAttributePaths().toArray( Path[] paths = searchParameters.getAttributePaths().toArray(new Path[0]);
new Path[0]), searchParameters); return new LuceneResultSet(
hits,
searcher,
nodeService,
paths,
searchParameters);
} }
catch (ParseException e) catch (ParseException e)

View File

@@ -60,16 +60,17 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider,
private static final String AFTER_ACL_PARENT = "AFTER_ACL_PARENT"; private static final String AFTER_ACL_PARENT = "AFTER_ACL_PARENT";
private PermissionService permissionService; private PermissionService permissionService;
private NamespacePrefixResolver nspr; private NamespacePrefixResolver nspr;
private NodeService nodeService; private NodeService nodeService;
private AuthenticationService authenticationService; private AuthenticationService authenticationService;
private int maxPermissionChecks;
private long maxPermissionCheckTimeMillis;
public ACLEntryAfterInvocationProvider() public ACLEntryAfterInvocationProvider()
{ {
super(); super();
maxPermissionChecks = Integer.MAX_VALUE;
maxPermissionCheckTimeMillis = Long.MAX_VALUE;
} }
public void setPermissionService(PermissionService permissionService) public void setPermissionService(PermissionService permissionService)
@@ -112,6 +113,16 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider,
this.authenticationService = authenticationService; this.authenticationService = authenticationService;
} }
public void setMaxPermissionChecks(int maxPermissionChecks)
{
this.maxPermissionChecks = maxPermissionChecks;
}
public void setMaxPermissionCheckTimeMillis(long maxPermissionCheckTimeMillis)
{
this.maxPermissionCheckTimeMillis = maxPermissionCheckTimeMillis;
}
public void afterPropertiesSet() throws Exception public void afterPropertiesSet() throws Exception
{ {
if (permissionService == null) if (permissionService == null)
@@ -401,10 +412,24 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider,
} }
} }
// record the start time
long startTimeMillis = System.currentTimeMillis();
// set the default, unlimited resultset type
filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters()));
for (int i = 0; i < returnedObject.length(); i++) for (int i = 0; i < returnedObject.length(); i++)
{ {
long currentTimeMillis = System.currentTimeMillis();
if ( i >= maxPermissionChecks || (currentTimeMillis - startTimeMillis) > maxPermissionCheckTimeMillis)
{
filteringResultSet.setResultSetMetaData(
new SimpleResultSetMetaData(
LimitBy.NUMBER_OF_PERMISSION_EVALUATIONS,
PermissionEvaluationMode.EAGER,
returnedObject.getResultSetMetaData().getSearchParameters()));
break;
}
// All permission checks must pass // All permission checks must pass
filteringResultSet.setIncluded(i, true); filteringResultSet.setIncluded(i, true);
@@ -429,16 +454,14 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider,
} }
// Bug out if we are limiting by size // Bug out if we are limiting by size
if ((maxSize != null) && (filteringResultSet.length() > maxSize.intValue())) if ((maxSize != null) && (filteringResultSet.length() > maxSize.intValue()))
{ {
// Renove the last match to fix the correct size // Renove the last match to fix the correct size
filteringResultSet.setIncluded(i, false); filteringResultSet.setIncluded(i, false);
filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.FINAL_SIZE, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters())); filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.FINAL_SIZE, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters()));
return filteringResultSet; break;
} }
} }
filteringResultSet.setResultSetMetaData(new SimpleResultSetMetaData(LimitBy.UNLIMITED, PermissionEvaluationMode.EAGER, returnedObject.getResultSetMetaData().getSearchParameters()));
return filteringResultSet; return filteringResultSet;
} }
@@ -465,8 +488,24 @@ public class ACLEntryAfterInvocationProvider implements AfterInvocationProvider,
log.debug("Entries are " + supportedDefinitions); log.debug("Entries are " + supportedDefinitions);
} }
for (Object nextObject : returnedObject) // record search start time
long startTimeMillis = System.currentTimeMillis();
int count = 0;
Iterator iterator = returnedObject.iterator();
while (iterator.hasNext())
{ {
Object nextObject = iterator.next();
// if the maximum result size or time has been exceeded, then we have to remove only
long currentTimeMillis = System.currentTimeMillis();
if ( count >= maxPermissionChecks || (currentTimeMillis - startTimeMillis) > maxPermissionCheckTimeMillis)
{
// just remove it
iterator.remove();
continue;
}
boolean allowed = true; boolean allowed = true;
for (ConfigAttributeDefintion cad : supportedDefinitions) for (ConfigAttributeDefintion cad : supportedDefinitions)
{ {

View File

@@ -318,6 +318,11 @@ public class PermissionModel implements ModelDAO, InitializingBean
private void addTypePermissions(QName type, Set<PermissionReference> permissions, boolean exposedOnly) private void addTypePermissions(QName type, Set<PermissionReference> permissions, boolean exposedOnly)
{ {
TypeDefinition typeDef = dictionaryService.getType(type); TypeDefinition typeDef = dictionaryService.getType(type);
if (typeDef == null)
{
// the type definition is no longer in the dictionary - ignore
return;
}
if (typeDef.getParentName() != null) if (typeDef.getParentName() != null)
{ {
PermissionSet permissionSet = permissionSets.get(type); PermissionSet permissionSet = permissionSets.get(type);
@@ -342,6 +347,11 @@ public class PermissionModel implements ModelDAO, InitializingBean
private void addAspectPermissions(QName type, Set<PermissionReference> permissions, boolean exposedOnly) private void addAspectPermissions(QName type, Set<PermissionReference> permissions, boolean exposedOnly)
{ {
AspectDefinition aspectDef = dictionaryService.getAspect(type); AspectDefinition aspectDef = dictionaryService.getAspect(type);
if (aspectDef == null)
{
// the aspect definition is no longer in the dictionary - ignore
return;
}
if (aspectDef.getParentName() != null) if (aspectDef.getParentName() != null)
{ {
PermissionSet permissionSet = permissionSets.get(type); PermissionSet permissionSet = permissionSets.get(type);

View File

@@ -18,6 +18,7 @@ package org.alfresco.repo.template;
import java.util.List; import java.util.List;
import org.alfresco.repo.search.impl.lucene.LuceneQueryParser;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.TemplateNode; import org.alfresco.service.cmr.repository.TemplateNode;
@@ -47,8 +48,7 @@ public class NodeSearchResultsMap extends BaseSearchResultsMap
TemplateNode result = null; TemplateNode result = null;
if (key != null) if (key != null)
{ {
String ref = key.toString().replace(":", "\\:"); String ref = "ID:" + LuceneQueryParser.escape(key.toString());
ref = "ID:" + ref.replace("/", "\\/");
List<TemplateNode> results = query(ref); List<TemplateNode> results = query(ref);

View File

@@ -58,6 +58,7 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest
protected TransactionService transactionService; protected TransactionService transactionService;
protected MutableAuthenticationDao authenticationDAO; protected MutableAuthenticationDao authenticationDAO;
protected NodeArchiveService nodeArchiveService; protected NodeArchiveService nodeArchiveService;
protected NodeService nodeService;
/* /*
* Data used by tests * Data used by tests
@@ -139,6 +140,7 @@ public abstract class BaseVersionStoreTest extends BaseSpringTest
this.transactionService = (TransactionService)this.applicationContext.getBean("transactionComponent"); this.transactionService = (TransactionService)this.applicationContext.getBean("transactionComponent");
this.authenticationDAO = (MutableAuthenticationDao) applicationContext.getBean("alfDaoImpl"); this.authenticationDAO = (MutableAuthenticationDao) applicationContext.getBean("alfDaoImpl");
this.nodeArchiveService = (NodeArchiveService) applicationContext.getBean("nodeArchiveService"); this.nodeArchiveService = (NodeArchiveService) applicationContext.getBean("nodeArchiveService");
this.nodeService = (NodeService)applicationContext.getBean("nodeService");
authenticationService.clearCurrentSecurityContext(); authenticationService.clearCurrentSecurityContext();

View File

@@ -29,6 +29,7 @@ import java.util.Set;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.service.cmr.dictionary.DictionaryService; import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.dictionary.InvalidAspectException; import org.alfresco.service.cmr.dictionary.InvalidAspectException;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.service.cmr.repository.AssociationExistsException; import org.alfresco.service.cmr.repository.AssociationExistsException;
import org.alfresco.service.cmr.repository.AssociationRef; import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.ChildAssociationRef;
@@ -39,6 +40,7 @@ import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.repository.Path; import org.alfresco.service.cmr.repository.Path;
import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.repository.NodeRef.Status; import org.alfresco.service.cmr.repository.NodeRef.Status;
import org.alfresco.service.cmr.repository.datatype.DefaultTypeConverter;
import org.alfresco.service.cmr.search.SearchService; import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
import org.alfresco.service.namespace.QNamePattern; import org.alfresco.service.namespace.QNamePattern;
@@ -302,22 +304,21 @@ public class NodeServiceImpl implements NodeService, VersionModel
{ {
Map<QName, Serializable> result = new HashMap<QName, Serializable>(); Map<QName, Serializable> result = new HashMap<QName, Serializable>();
// TODO should be doing this using a path query .. Collection<ChildAssociationRef> children = this.dbNodeService.getChildAssocs(convertNodeRef(nodeRef), CHILD_QNAME_VERSIONED_ATTRIBUTES, RegexQNamePattern.MATCH_ALL);
Collection<ChildAssociationRef> children = this.dbNodeService.getChildAssocs(convertNodeRef(nodeRef));
for (ChildAssociationRef child : children) for (ChildAssociationRef child : children)
{
if (child.getQName().equals(CHILD_QNAME_VERSIONED_ATTRIBUTES))
{ {
NodeRef versionedAttribute = child.getChildRef(); NodeRef versionedAttribute = child.getChildRef();
// Get the QName and the value // Get the QName and the value
Serializable value = null; Serializable value = null;
QName qName = (QName)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_QNAME); QName qName = (QName)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_QNAME);
PropertyDefinition propDef = this.dicitionaryService.getProperty(qName);
Boolean isMultiValue = (Boolean)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_IS_MULTI_VALUE); Boolean isMultiValue = (Boolean)this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_IS_MULTI_VALUE);
if (isMultiValue.booleanValue() == false) if (isMultiValue.booleanValue() == false)
{ {
value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_VALUE); value = this.dbNodeService.getProperty(versionedAttribute, PROP_QNAME_VALUE);
value = (Serializable)DefaultTypeConverter.INSTANCE.convert(propDef.getDataType(), value);
} }
else else
{ {
@@ -325,7 +326,6 @@ public class NodeServiceImpl implements NodeService, VersionModel
} }
result.put(qName, value); result.put(qName, value);
}
} }
return result; return result;

View File

@@ -19,11 +19,13 @@ package org.alfresco.repo.version;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map;
import java.util.Set; import java.util.Set;
import org.alfresco.model.ApplicationModel; import org.alfresco.model.ApplicationModel;
import org.alfresco.model.ContentModel; import org.alfresco.model.ContentModel;
import org.alfresco.repo.transaction.TransactionUtil; import org.alfresco.repo.transaction.TransactionUtil;
import org.alfresco.service.cmr.repository.ChildAssociationRef;
import org.alfresco.service.cmr.repository.ContentReader; import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentWriter; import org.alfresco.service.cmr.repository.ContentWriter;
import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeRef;
@@ -31,6 +33,7 @@ import org.alfresco.service.cmr.repository.StoreRef;
import org.alfresco.service.cmr.version.Version; import org.alfresco.service.cmr.version.Version;
import org.alfresco.service.cmr.version.VersionHistory; import org.alfresco.service.cmr.version.VersionHistory;
import org.alfresco.service.cmr.version.VersionServiceException; import org.alfresco.service.cmr.version.VersionServiceException;
import org.alfresco.service.cmr.version.VersionType;
import org.alfresco.service.namespace.QName; import org.alfresco.service.namespace.QName;
/** /**
@@ -681,4 +684,30 @@ public class VersionServiceImplTest extends BaseVersionStoreTest
} }
}); });
} }
public void testAR807()
{
QName prop = QName.createQName("http://www.alfresco.org/test/versionstorebasetest/1.0", "intProp");
ChildAssociationRef childAssociation =
nodeService.createNode(this.rootNodeRef,
ContentModel.ASSOC_CHILDREN,
QName.createQName("http://www.alfresco.org/test/versionstorebasetest/1.0", "integerTest"),
TEST_TYPE_QNAME);
NodeRef newNode = childAssociation.getChildRef();
nodeService.setProperty(newNode, prop, 1);
Object editionCode = nodeService.getProperty(newNode, prop);
assertEquals(editionCode.getClass(), Integer.class);
Map<String, Serializable> versionProps = new HashMap<String, Serializable>(1);
versionProps.put(VersionModel.PROP_VERSION_TYPE, VersionType.MAJOR);
Version version = versionService.createVersion(newNode, versionProps);
NodeRef versionNodeRef = version.getFrozenStateNodeRef();
assertNotNull(versionNodeRef);
Object editionCodeArchive = nodeService.getProperty(versionNodeRef, prop);
assertEquals(editionCodeArchive.getClass(), Integer.class);
}
} }

View File

@@ -41,6 +41,9 @@
<type>d:text</type> <type>d:text</type>
<multiple>true</multiple> <multiple>true</multiple>
</property> </property>
<property name="test:intProp">
<type>d:int</type>
</property>
</properties> </properties>
<associations> <associations>

View File

@@ -100,8 +100,6 @@ public class WorkflowInterpreter
/** /**
* Main entry point. * Main entry point.
*
* Syntax: AVMInteractiveConsole storage (new|old).
*/ */
public static void main(String[] args) public static void main(String[] args)
{ {
@@ -206,6 +204,8 @@ public class WorkflowInterpreter
/** /**
* Interpret a single command using the BufferedReader passed in for any data needed. * Interpret a single command using the BufferedReader passed in for any data needed.
* *
* TODO: Use decent parser!
*
* @param line The unparsed command * @param line The unparsed command
* @return The textual output of the command. * @return The textual output of the command.
*/ */
@@ -300,16 +300,43 @@ public class WorkflowInterpreter
else if (command[1].equals("workflows")) else if (command[1].equals("workflows"))
{ {
if (currentWorkflowDef == null) String id = (currentWorkflowDef != null) ? currentWorkflowDef.id : null;
if (id == null && command.length == 2)
{ {
return "workflow definition not in use. Enter command use <workflowDefId>.\n"; return "workflow definition not in use. Enter command 'show workflows all' or 'use <workflowDefId>'.\n";
} }
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(currentWorkflowDef.id); if (command.length == 3)
{
if (command[2].equals("all"))
{
id = "all";
}
else
{
return "Syntax Error.\n";
}
}
if (id.equals("all"))
{
for (WorkflowDefinition def : workflowService.getDefinitions())
{
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(def.id);
for (WorkflowInstance workflow : workflows) for (WorkflowInstance workflow : workflows)
{ {
out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title); out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title);
} }
} }
}
else
{
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(id);
for (WorkflowInstance workflow : workflows)
{
out.println("id: " + workflow.id + " , desc: " + workflow.description + " , start date: " + workflow.startDate + " , def: " + workflow.definition.title);
}
}
}
else if (command[1].equals("paths")) else if (command[1].equals("paths"))
{ {
@@ -485,6 +512,25 @@ public class WorkflowInterpreter
out.print(interpretCommand("deploy " + currentDeploy)); out.print(interpretCommand("deploy " + currentDeploy));
} }
else if (command[0].equals("undeploy"))
{
if (command.length < 2)
{
return "Syntax Error.\n";
}
if (command[1].equals("definition"))
{
if (command.length != 3)
{
return "Syntax Error.\n";
}
workflowService.undeployDefinition(command[2]);
currentWorkflowDef = null;
currentPath = null;
out.print(interpretCommand("show definitions"));
}
}
else if (command[0].equals("use")) else if (command[0].equals("use"))
{ {
if (command.length == 1) if (command.length == 1)
@@ -527,7 +573,6 @@ public class WorkflowInterpreter
return "Syntax Error.\n"; return "Syntax Error.\n";
} }
} }
} }
else if (command[0].equals("user")) else if (command[0].equals("user"))
@@ -659,11 +704,11 @@ public class WorkflowInterpreter
else if (command[0].equals("delete")) else if (command[0].equals("delete"))
{ {
if (command.length < 3) if (command.length < 2)
{ {
return "Syntax Error.\n"; return "Syntax Error.\n";
} }
else if (command[1].equals("workflow")) if (command[1].equals("workflow"))
{ {
String workflowId = (command.length == 3) ? command[2] : (currentPath == null) ? null : currentPath.instance.id; String workflowId = (command.length == 3) ? command[2] : (currentPath == null) ? null : currentPath.instance.id;
if (workflowId == null) if (workflowId == null)
@@ -673,6 +718,40 @@ public class WorkflowInterpreter
workflowService.deleteWorkflow(workflowId); workflowService.deleteWorkflow(workflowId);
out.println("workflow " + workflowId + " deleted."); out.println("workflow " + workflowId + " deleted.");
} }
else if (command[1].equals("all"))
{
if (command.length < 3)
{
return "Syntax Error.\n";
}
if (command[2].equals("workflows"))
{
if (command.length < 4)
{
return "Enter the command 'delete all workflows imeanit' to really delete all workflows\n";
}
if (command[3].equals("imeanit"))
{
for (WorkflowDefinition def : workflowService.getDefinitions())
{
List<WorkflowInstance> workflows = workflowService.getActiveWorkflows(def.id);
for (WorkflowInstance workflow : workflows)
{
workflowService.deleteWorkflow(workflow.id);
out.println("workflow " + workflow.id + " deleted.");
}
}
}
else
{
return "Syntax Error.\n";
}
}
else
{
return "Syntax Error.\n";
}
}
else else
{ {
return "Syntax Error.\n"; return "Syntax Error.\n";

View File

@@ -21,7 +21,11 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.alfresco.repo.jscript.Classification;
import org.alfresco.repo.jscript.Search;
import org.alfresco.repo.jscript.Session;
import org.alfresco.service.ServiceRegistry; import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.ScriptService; import org.alfresco.service.cmr.repository.ScriptService;
import org.alfresco.service.cmr.workflow.WorkflowException; import org.alfresco.service.cmr.workflow.WorkflowException;
import org.dom4j.Element; import org.dom4j.Element;
@@ -54,6 +58,7 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
private static JpdlXmlReader jpdlReader = new JpdlXmlReader((InputSource)null); private static JpdlXmlReader jpdlReader = new JpdlXmlReader((InputSource)null);
private ScriptService scriptService; private ScriptService scriptService;
private ServiceRegistry services;
private Element script; private Element script;
@@ -64,6 +69,7 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
protected void initialiseHandler(BeanFactory factory) protected void initialiseHandler(BeanFactory factory)
{ {
scriptService = (ScriptService)factory.getBean(ServiceRegistry.SCRIPT_SERVICE.getLocalName()); scriptService = (ScriptService)factory.getBean(ServiceRegistry.SCRIPT_SERVICE.getLocalName());
services = (ServiceRegistry)factory.getBean(ServiceRegistry.SERVICE_REGISTRY);
} }
@@ -140,6 +146,16 @@ public class AlfrescoJavaScript extends JBPMSpringActionHandler
{ {
Map<String, Object> inputMap = new HashMap<String, Object>(); Map<String, Object> inputMap = new HashMap<String, Object>();
// initialise global script variables
JBPMNode companyHome = (JBPMNode)executionContext.getContextInstance().getVariable("companyhome");
if (companyHome != null)
{
NodeRef companyHomeRef = companyHome.getNodeRef();
inputMap.put("search", new Search(services, companyHomeRef.getStoreRef(), null));
inputMap.put("session", new Session(services, null));
inputMap.put("classification", new Classification(services, companyHomeRef.getStoreRef(), null));
}
// initialise process variables // initialise process variables
Token token = executionContext.getToken(); Token token = executionContext.getToken();
inputMap.put("executionContext", executionContext); inputMap.put("executionContext", executionContext);

View File

@@ -30,6 +30,24 @@ import org.alfresco.service.namespace.QNamePattern;
/** /**
* Interface for public and internal <b>node</b> and <b>store</b> operations. * Interface for public and internal <b>node</b> and <b>store</b> operations.
* <p>
* Amongst other things, this service must enforce the unique name check as mandated
* by the <b>duplicate</b> entity in the model.
* <pre></code>
* <type name="cm:folder">
* ...
* <associations>
* <child-association name="cm:contains">
* ...
* <duplicate>false</duplicate>
* </child-association>
* </associations>
* </type>
* </code></pre>
* When duplicates are not allowed, and the <b>cm:name</b> property of a node changes,
* then the {@link org.alfresco.service.cmr.repository.DuplicateChildNodeNameException}
* exception must be thrown. Client code can catch this exception and deal with it
* appropriately.
* *
* @author Derek Hulley * @author Derek Hulley
*/ */

View File

@@ -0,0 +1,22 @@
/**
*
*/
package org.alfresco.service.cmr.repository;
import java.io.Reader;
/**
* Interface encapsulating the location of a script and provding access to it.
*
* @author Roy Wetherall
*
*/
public interface ScriptLocation
{
/**
* Returns a reader to the contents of the script
*
* @return the reader
*/
Reader getReader();
}

View File

@@ -70,6 +70,20 @@ public interface ScriptService
public Object executeScript(NodeRef scriptRef, QName contentProp, Map<String, Object> model) public Object executeScript(NodeRef scriptRef, QName contentProp, Map<String, Object> model)
throws ScriptException; throws ScriptException;
/**
* Process a script against the supplied data model.
*
* @param scriptLocation object representing the script location
* @param model Object model to process script against
*
* @return output of the script (may be null or any other valid wrapped JavaScript object)
*
* @throws ScriptException
*/
@Auditable(parameters = {"scriptLocation", "model"})
public Object executeScript(ScriptLocation scriptLocation, Map<String, Object> model)
throws ScriptException;
/** /**
* Process a script against the supplied data model. * Process a script against the supplied data model.
* *

View File

@@ -7,5 +7,18 @@ package org.alfresco.service.cmr.search;
*/ */
public enum LimitBy public enum LimitBy
{ {
UNLIMITED, FINAL_SIZE; // NUMBER_OF_PERMISSION_EVALUATIONS /**
* The final number of search results is not important.
*/
UNLIMITED,
/**
* Limit the total number of search results returned after pruning by permissions.
*/
FINAL_SIZE,
/**
* Limit the number of results that will be passed through for permission checks.<br/>
* Used internally to prevent excessive permission checking
* (see property <b>lucene.query.maxInitialSearchResults</b>).
*/
NUMBER_OF_PERMISSION_EVALUATIONS;
} }