diff --git a/source/java/org/alfresco/repo/jscript/Node.java b/source/java/org/alfresco/repo/jscript/Node.java
new file mode 100644
index 0000000000..a338e4571c
--- /dev/null
+++ b/source/java/org/alfresco/repo/jscript/Node.java
@@ -0,0 +1,671 @@
+/*
+ * 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.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.text.MessageFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.model.ContentModel;
+import org.alfresco.repo.security.permissions.AccessDeniedException;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.dictionary.DictionaryService;
+import org.alfresco.service.cmr.lock.LockStatus;
+import org.alfresco.service.cmr.repository.AssociationRef;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.ContentData;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.TemplateImageResolver;
+import org.alfresco.service.namespace.QName;
+import org.alfresco.service.namespace.RegexQNamePattern;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.springframework.util.StringUtils;
+
+/**
+ * Node class specific for use by Template pages that support Bean objects as part of the model.
+ * The default template engine FreeMarker can use these objects and they are provided to support it.
+ * A single method is completely freemarker specific - getXmlNodeModel()
+ *
+ * The class exposes Node properties, children as dynamically populated maps and lists.
+ *
+ * Various helper methods are provided to access common and useful node variables such
+ * as the content url and type information.
+ *
+ * @author Kevin Roast
+ */
+public final class Node implements Serializable
+{
+ private static Log logger = LogFactory.getLog(Node.class);
+
+ private final static String NAMESPACE_BEGIN = "" + QName.NAMESPACE_BEGIN;
+ private final static String CONTENT_DEFAULT_URL = "/download/direct/{0}/{1}/{2}/{3}";
+ private final static String CONTENT_PROP_URL = "/download/direct/{0}/{1}/{2}/{3}?property={4}";
+ private final static String FOLDER_BROWSE_URL = "/navigate/browse/{0}/{1}/{2}";
+
+ /** The children of this node */
+ private Node[] children = null;
+
+ /** The associations from this node */
+ private ScriptableQNameMap assocs = null;
+
+ /** Cached values */
+ private NodeRef nodeRef;
+ private String name;
+ private QName type;
+ private String path;
+ private String id;
+ private Set aspects = null;
+ private ScriptableQNameMap properties;
+ private boolean propsRetrieved = false;
+ private ServiceRegistry services = null;
+ private Boolean isDocument = null;
+ private Boolean isContainer = null;
+ private String displayPath = null;
+ private String mimetype = null;
+ private Long size = null;
+ private TemplateImageResolver imageResolver = null;
+ private Node parent = null;
+ private ChildAssociationRef primaryParentAssoc = null;
+
+
+ /**
+ * Constructor
+ *
+ * @param nodeRef The NodeRef this Node wrapper represents
+ * @param services The ServiceRegistry the Node can use to access services
+ * @param resolver Image resolver to use to retrieve icons
+ */
+ public Node(NodeRef nodeRef, ServiceRegistry services, TemplateImageResolver resolver)
+ {
+ if (nodeRef == null)
+ {
+ throw new IllegalArgumentException("NodeRef must be supplied.");
+ }
+
+ if (services == null)
+ {
+ throw new IllegalArgumentException("The ServiceRegistry must be supplied.");
+ }
+
+ this.nodeRef = nodeRef;
+ this.id = nodeRef.getId();
+ this.services = services;
+ this.imageResolver = resolver;
+
+ this.properties = new ScriptableQNameMap(this.services.getNamespaceService());
+ }
+
+ /**
+ * @return The GUID for the node
+ */
+ public String getId()
+ {
+ return this.id;
+ }
+
+ /**
+ * @return Returns the NodeRef this Node object represents
+ */
+ public NodeRef getNodeRef()
+ {
+ return this.nodeRef;
+ }
+
+ /**
+ * @return Returns the type.
+ */
+ public QName getType()
+ {
+ if (this.type == null)
+ {
+ this.type = this.services.getNodeService().getType(this.nodeRef);
+ }
+
+ return type;
+ }
+
+ /**
+ * @return The display name for the node
+ */
+ public String getName()
+ {
+ if (this.name == null)
+ {
+ // try and get the name from the properties first
+ this.name = (String)getProperties().get("cm:name");
+
+ // if we didn't find it as a property get the name from the association name
+ if (this.name == null)
+ {
+ ChildAssociationRef parentRef = this.services.getNodeService().getPrimaryParent(this.nodeRef);
+ if (parentRef != null && parentRef.getQName() != null)
+ {
+ this.name = parentRef.getQName().getLocalName();
+ }
+ else
+ {
+ this.name = "";
+ }
+ }
+ }
+
+ return this.name;
+ }
+
+ /**
+ * @return The children of this Node as Node wrappers
+ */
+ public Node[] getChildren()
+ {
+ if (this.children == null)
+ {
+ List childRefs = this.services.getNodeService().getChildAssocs(this.nodeRef);
+ this.children = new Node[childRefs.size()];
+ for (int i=0; inode.getAssocs()["translations"][0]
+ *
+ * @return associations as a Map of assoc name to an Array of Nodes.
+ */
+ public Map getAssocs()
+ {
+ if (this.assocs == null)
+ {
+ List refs = this.services.getNodeService().getTargetAssocs(this.nodeRef, RegexQNamePattern.MATCH_ALL);
+ this.assocs = new ScriptableQNameMap(this.services.getNamespaceService());
+ for (AssociationRef ref : refs)
+ {
+ String qname = ref.getTypeQName().toString();
+ Node[] nodes = (Node[])assocs.get(qname);
+ if (nodes == null)
+ {
+ // first access for the list for this qname
+ nodes = new Node[1];
+ }
+ else
+ {
+ Node[] newNodes = new Node[nodes.length + 1];
+ System.arraycopy(nodes, 0, newNodes, 0, nodes.length);
+ nodes = newNodes;
+ }
+ nodes[nodes.length] = new Node(ref.getTargetRef(), this.services, this.imageResolver);
+
+ this.assocs.put(ref.getTypeQName().toString(), nodes);
+ }
+ }
+
+ return this.assocs;
+ }
+
+ /**
+ * Return all the properties known about this node.
+ *
+ * The Map returned implements the Scriptable interface to allow access to the properties via
+ * JavaScript associative array access. This means properties of a node can be access thus:
+ * node.getProperties()["name"]
+ *
+ * @return Map of properties for this Node.
+ */
+ public Map getProperties()
+ {
+ if (this.propsRetrieved == false)
+ {
+ Map props = this.services.getNodeService().getProperties(this.nodeRef);
+
+ for (QName qname : props.keySet())
+ {
+ Serializable propValue = props.get(qname);
+ if (propValue instanceof NodeRef)
+ {
+ // NodeRef object properties are converted to new Node objects
+ // so they can be used as objects within a template
+ propValue = new Node(((NodeRef)propValue), this.services, this.imageResolver);
+ }
+ else if (propValue instanceof ContentData)
+ {
+ // ContentData object properties are converted to ScriptContentData objects
+ // so the content and other properties of those objects can be accessed
+ propValue = new ScriptContentData((ContentData)propValue, qname);
+ }
+ this.properties.put(qname.toString(), propValue);
+ }
+
+ this.propsRetrieved = true;
+ }
+
+ return this.properties;
+ }
+
+ /**
+ * @return true if this Node is a container (i.e. a folder)
+ */
+ public boolean isContainer()
+ {
+ if (isContainer == null)
+ {
+ DictionaryService dd = this.services.getDictionaryService();
+ isContainer = Boolean.valueOf( (dd.isSubClass(getType(), ContentModel.TYPE_FOLDER) == true &&
+ dd.isSubClass(getType(), ContentModel.TYPE_SYSTEM_FOLDER) == false) );
+ }
+
+ return isContainer.booleanValue();
+ }
+
+ /**
+ * @return true if this Node is a Document (i.e. with content)
+ */
+ public boolean isDocument()
+ {
+ if (isDocument == null)
+ {
+ DictionaryService dd = this.services.getDictionaryService();
+ isDocument = Boolean.valueOf(dd.isSubClass(getType(), ContentModel.TYPE_CONTENT));
+ }
+
+ return isDocument.booleanValue();
+ }
+
+ /**
+ * @return The list of aspects applied to this node
+ */
+ public Set getAspects()
+ {
+ if (this.aspects == null)
+ {
+ this.aspects = this.services.getNodeService().getAspects(this.nodeRef);
+ }
+
+ return this.aspects;
+ }
+
+ /**
+ * @param aspect The aspect name to test for
+ *
+ * @return true if the node has the aspect false otherwise
+ */
+ public boolean hasAspect(String aspect)
+ {
+ if (this.aspects == null)
+ {
+ this.aspects = this.services.getNodeService().getAspects(this.nodeRef);
+ }
+
+ if (aspect.startsWith(NAMESPACE_BEGIN))
+ {
+ return aspects.contains((QName.createQName(aspect)));
+ }
+ else
+ {
+ boolean found = false;
+ for (QName qname : this.aspects)
+ {
+ if (qname.toPrefixString(this.services.getNamespaceService()).equals(aspect))
+ {
+ found = true;
+ break;
+ }
+ }
+ return found;
+ }
+ }
+
+ /**
+ * @return Display path to this node
+ */
+ public String getDisplayPath()
+ {
+ if (displayPath == null)
+ {
+ try
+ {
+ displayPath = this.services.getNodeService().getPath(this.nodeRef).toDisplayPath(this.services.getNodeService());
+ }
+ catch (AccessDeniedException err)
+ {
+ displayPath = "";
+ }
+ }
+
+ return displayPath;
+ }
+
+ /**
+ * @return the small icon image for this node
+ */
+ public String getIcon16()
+ {
+ if (this.imageResolver != null)
+ {
+ if (isDocument())
+ {
+ return this.imageResolver.resolveImagePathForName(getName(), true);
+ }
+ else
+ {
+ return "/images/icons/space_small.gif";
+ }
+ }
+ else
+ {
+ return "/images/filetypes/_default.gif";
+ }
+ }
+
+ /**
+ * @return the large icon image for this node
+ */
+ public String getIcon32()
+ {
+ if (this.imageResolver != null)
+ {
+ if (isDocument())
+ {
+ return this.imageResolver.resolveImagePathForName(getName(), false);
+ }
+ else
+ {
+ String icon = (String)getProperties().get("app:icon");
+ if (icon != null)
+ {
+ return "/images/icons/" + icon + ".gif";
+ }
+ else
+ {
+ return "/images/icons/space-icon-default.gif";
+ }
+ }
+ }
+ else
+ {
+ return "/images/filetypes32/_default.gif";
+ }
+ }
+
+ /**
+ * @return true if the node is currently locked
+ */
+ public boolean isLocked()
+ {
+ boolean locked = false;
+
+ if (getAspects().contains(ContentModel.ASPECT_LOCKABLE))
+ {
+ LockStatus lockStatus = this.services.getLockService().getLockStatus(this.nodeRef);
+ if (lockStatus == LockStatus.LOCKED || lockStatus == LockStatus.LOCK_OWNER)
+ {
+ locked = true;
+ }
+ }
+
+ return locked;
+ }
+
+ /**
+ * @return the parent node
+ */
+ public Node getParent()
+ {
+ if (parent == null)
+ {
+ NodeRef parentRef = this.services.getNodeService().getPrimaryParent(nodeRef).getParentRef();
+ // handle root node (no parent!)
+ if (parentRef != null)
+ {
+ parent = new Node(parentRef, this.services, this.imageResolver);
+ }
+ }
+
+ return parent;
+ }
+
+ /**
+ *
+ * @return the primary parent association so we can get at the association QName and the association type QName.
+ */
+ public ChildAssociationRef getPrimaryParentAssoc()
+ {
+ if (primaryParentAssoc == null)
+ {
+ primaryParentAssoc = this.services.getNodeService().getPrimaryParent(nodeRef);
+ }
+ return primaryParentAssoc;
+ }
+
+ /**
+ * @return the content String for this node from the default content property
+ * (@see ContentModel.PROP_CONTENT)
+ */
+ public String getContent()
+ {
+ ContentService contentService = this.services.getContentService();
+ ContentReader reader = contentService.getReader(this.nodeRef, ContentModel.PROP_CONTENT);
+ return (reader != null && reader.exists()) ? reader.getContentString() : "";
+ }
+
+ /**
+ * @return For a content document, this method returns the URL to the content stream for
+ * the default content property (@see ContentModel.PROP_CONTENT)
+ *
+ * For a container node, this method return the URL to browse to the folder in the web-client
+ */
+ public String getUrl()
+ {
+ if (isDocument() == true)
+ {
+ try
+ {
+ return MessageFormat.format(CONTENT_DEFAULT_URL, new Object[] {
+ nodeRef.getStoreRef().getProtocol(),
+ nodeRef.getStoreRef().getIdentifier(),
+ nodeRef.getId(),
+ StringUtils.replace(URLEncoder.encode(getName(), "UTF-8"), "+", "%20") } );
+ }
+ catch (UnsupportedEncodingException err)
+ {
+ throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + nodeRef, err);
+ }
+ }
+ else
+ {
+ return MessageFormat.format(FOLDER_BROWSE_URL, new Object[] {
+ nodeRef.getStoreRef().getProtocol(),
+ nodeRef.getStoreRef().getIdentifier(),
+ nodeRef.getId() } );
+ }
+ }
+
+ /**
+ * @return The mimetype encoding for content attached to the node from the default content property
+ * (@see ContentModel.PROP_CONTENT)
+ */
+ public String getMimetype()
+ {
+ if (mimetype == null)
+ {
+ ScriptContentData content = (ScriptContentData)this.getProperties().get(ContentModel.PROP_CONTENT);
+ if (content != null)
+ {
+ mimetype = content.getMimetype();
+ }
+ }
+
+ return mimetype;
+ }
+
+ /**
+ * @return The size in bytes of the content attached to the node from the default content property
+ * (@see ContentModel.PROP_CONTENT)
+ */
+ public long getSize()
+ {
+ if (size == null)
+ {
+ ScriptContentData content = (ScriptContentData)this.getProperties().get(ContentModel.PROP_CONTENT);
+ if (content != null)
+ {
+ size = content.getSize();
+ }
+ }
+
+ return size != null ? size.longValue() : 0L;
+ }
+
+ /**
+ * @return the image resolver instance used by this node
+ */
+ public TemplateImageResolver getImageResolver()
+ {
+ return this.imageResolver;
+ }
+
+ /**
+ * Override Object.toString() to provide useful debug output
+ */
+ public String toString()
+ {
+ if (this.services.getNodeService().exists(nodeRef))
+ {
+ return "Node Type: " + getType() +
+ "\nNode Properties: " + this.getProperties().toString() +
+ "\nNode Aspects: " + this.getAspects().toString();
+ }
+ else
+ {
+ return "Node no longer exists: " + nodeRef;
+ }
+ }
+
+
+ /**
+ * Inner class wrapping and providing access to a ContentData property
+ */
+ public class ScriptContentData implements Serializable
+ {
+ /**
+ * Constructor
+ *
+ * @param contentData The ContentData object this object wraps
+ * @param property The property the ContentData is attached too
+ */
+ public ScriptContentData(ContentData contentData, QName property)
+ {
+ this.contentData = contentData;
+ this.property = property;
+ }
+
+ /**
+ * @return the content stream
+ */
+ public String getContent()
+ {
+ ContentService contentService = services.getContentService();
+ ContentReader reader = contentService.getReader(nodeRef, property);
+
+ return (reader != null && reader.exists()) ? reader.getContentString() : "";
+ }
+
+ /**
+ * @return
+ */
+ public String getUrl()
+ {
+ try
+ {
+ return MessageFormat.format(CONTENT_PROP_URL, new Object[] {
+ nodeRef.getStoreRef().getProtocol(),
+ nodeRef.getStoreRef().getIdentifier(),
+ nodeRef.getId(),
+ StringUtils.replace(URLEncoder.encode(getName(), "UTF-8"), "+", "%20"),
+ StringUtils.replace(URLEncoder.encode(property.toString(), "UTF-8"), "+", "%20") } );
+ }
+ catch (UnsupportedEncodingException err)
+ {
+ throw new AlfrescoRuntimeException("Failed to encode content URL for node: " + nodeRef, err);
+ }
+ }
+
+ public long getSize()
+ {
+ return contentData.getSize();
+ }
+
+ public String getMimetype()
+ {
+ return contentData.getMimetype();
+ }
+
+ private ContentData contentData;
+ private QName property;
+ }
+}
\ No newline at end of file
diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java b/source/java/org/alfresco/repo/jscript/RhinoScriptService.java
new file mode 100644
index 0000000000..ee842eeb6b
--- /dev/null
+++ b/source/java/org/alfresco/repo/jscript/RhinoScriptService.java
@@ -0,0 +1,214 @@
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.Map;
+
+import org.alfresco.error.AlfrescoRuntimeException;
+import org.alfresco.model.ContentModel;
+import org.alfresco.service.cmr.repository.ContentReader;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.ScriptException;
+import org.alfresco.service.cmr.repository.ScriptService;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+
+/**
+ * Implementation of the ScriptService using the Rhino JavaScript engine.
+ *
+ * @author Kevin Roast
+ */
+public class RhinoScriptService implements ScriptService
+{
+ /** The permission-safe node service */
+ private NodeService nodeService;
+
+ /** The Content Service to use */
+ private ContentService contentService;
+
+ /**
+ * Set the node service
+ *
+ * @param nodeService The permission-safe node service
+ */
+ public void setNodeService(NodeService nodeService)
+ {
+ this.nodeService = nodeService;
+ }
+
+ /**
+ * Set the content service
+ *
+ * @param contentService The ContentService to use
+ */
+ public void setContentService(ContentService contentService)
+ {
+ this.contentService = contentService;
+ }
+
+ /**
+ * @see org.alfresco.service.cmr.repository.ScriptService#executeScript(java.lang.String, java.util.Map)
+ */
+ public Object executeScript(String scriptClasspath, Map model) throws ScriptException
+ {
+ if (scriptClasspath == null)
+ {
+ throw new IllegalArgumentException("Script ClassPath is mandatory.");
+ }
+
+ Reader reader = null;
+ try
+ {
+ InputStream stream = getClass().getClassLoader().getResourceAsStream(scriptClasspath);
+ if (stream == null)
+ {
+ throw new AlfrescoRuntimeException("Unable to load classpath resource: " + scriptClasspath);
+ }
+ reader = new InputStreamReader(stream);
+
+ return executeScriptImpl(reader, model);
+ }
+ catch (Throwable err)
+ {
+ throw new ScriptException("Failed to execute script '" + scriptClasspath + "': " + err.getMessage(), err);
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ try {reader.close();} catch (IOException ioErr) {}
+ }
+ }
+ }
+
+ /**
+ * @see org.alfresco.service.cmr.repository.ScriptService#executeScript(org.alfresco.service.cmr.repository.NodeRef, java.util.Map)
+ */
+ public Object executeScript(NodeRef scriptRef, Map model) throws ScriptException
+ {
+ if (scriptRef == null)
+ {
+ throw new IllegalArgumentException("Script NodeRef is mandatory.");
+ }
+
+ Reader reader = null;
+ try
+ {
+ if (this.nodeService.exists(scriptRef) == false)
+ {
+ throw new AlfrescoRuntimeException("Script Node does not exist: " + scriptRef);
+ }
+ ContentReader cr = this.contentService.getReader(scriptRef, ContentModel.PROP_CONTENT);
+ if (cr == null || cr.exists() == false)
+ {
+ throw new AlfrescoRuntimeException("Script Node content not found: " + scriptRef);
+ }
+ reader = new InputStreamReader(cr.getContentInputStream());
+
+ return executeScriptImpl(reader, model);
+ }
+ catch (Throwable err)
+ {
+ throw new ScriptException("Failed to execute script '" + scriptRef.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)
+ */
+ public Object executeScriptString(String script, Map model) throws ScriptException
+ {
+ if (script == null || script.length() == 0)
+ {
+ throw new IllegalArgumentException("Script argument is mandatory.");
+ }
+
+ Reader reader = null;
+ try
+ {
+ reader = new StringReader(script);
+
+ return executeScriptImpl(reader, model);
+ }
+ catch (Throwable err)
+ {
+ throw new ScriptException("Failed to execute supplied script: " + err.getMessage(), err);
+ }
+ }
+
+ /**
+ * Execute the script content from the supplied Reader. Adds the data model into the default
+ * root scope for access by the script.
+ *
+ * @param reader Reader referencing the script to execute.
+ * @param model Data model containing objects to be added to the root scope.
+ *
+ * @return result of the script execution, can be null.
+ *
+ * @throws AlfrescoRuntimeException
+ */
+ private Object executeScriptImpl(Reader reader, Map model)
+ throws AlfrescoRuntimeException
+ {
+ // check that rhino script engine is available
+ Context cx = Context.enter();
+ try
+ {
+ // The easiest way to embed Rhino is just to create a new scope this way whenever
+ // you need one. However, initStandardObjects is an expensive method to call and it
+ // allocates a fair amount of memory.
+ Scriptable scope = cx.initStandardObjects();
+
+ // insert supplied object model into root of the default scope
+ if (model != null)
+ {
+ for (String key : model.keySet())
+ {
+ ScriptableObject.putProperty(scope, key, model.get(key));
+ }
+ }
+
+ // execute the script
+ Object result = cx.evaluateReader(scope, reader, "AlfrescoScript", 1, null);
+
+ return result;
+ }
+ catch (Throwable err)
+ {
+ throw new AlfrescoRuntimeException(err.getMessage(), err);
+ }
+ finally
+ {
+ cx.exit();
+ }
+ }
+}
diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java b/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java
new file mode 100644
index 0000000000..72132b6c90
--- /dev/null
+++ b/source/java/org/alfresco/repo/jscript/RhinoScriptTest.java
@@ -0,0 +1,184 @@
+/*
+ * 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.InputStream;
+import java.util.List;
+
+import junit.framework.TestCase;
+
+import org.alfresco.repo.dictionary.DictionaryComponent;
+import org.alfresco.repo.dictionary.DictionaryDAO;
+import org.alfresco.repo.dictionary.M2Model;
+import org.alfresco.repo.node.BaseNodeServiceTest;
+import org.alfresco.repo.security.authentication.AuthenticationComponent;
+import org.alfresco.repo.transaction.TransactionUtil;
+import org.alfresco.service.ServiceRegistry;
+import org.alfresco.service.cmr.repository.ChildAssociationRef;
+import org.alfresco.service.cmr.repository.ContentService;
+import org.alfresco.service.cmr.repository.NodeRef;
+import org.alfresco.service.cmr.repository.NodeService;
+import org.alfresco.service.cmr.repository.StoreRef;
+import org.alfresco.service.cmr.repository.TemplateService;
+import org.alfresco.service.transaction.TransactionService;
+import org.alfresco.util.ApplicationContextHelper;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * @author Kevin Roast
+ */
+public class RhinoScriptTest extends TestCase
+{
+ private static final ApplicationContext ctx = ApplicationContextHelper.getApplicationContext();
+
+ private ContentService contentService;
+ private TemplateService templateService;
+ private NodeService nodeService;
+ private TransactionService transactionService;
+ private ServiceRegistry serviceRegistry;
+ private AuthenticationComponent authenticationComponent;
+
+ /*
+ * @see junit.framework.TestCase#setUp()
+ */
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+
+ transactionService = (TransactionService)this.ctx.getBean("transactionComponent");
+ contentService = (ContentService)this.ctx.getBean("contentService");
+ nodeService = (NodeService)this.ctx.getBean("nodeService");
+ templateService = (TemplateService)this.ctx.getBean("templateService");
+ serviceRegistry = (ServiceRegistry)this.ctx.getBean("ServiceRegistry");
+
+ this.authenticationComponent = (AuthenticationComponent)ctx.getBean("authenticationComponent");
+ this.authenticationComponent.setSystemUserAsCurrentUser();
+
+ DictionaryDAO dictionaryDao = (DictionaryDAO)ctx.getBean("dictionaryDAO");
+
+ // load the system model
+ ClassLoader cl = BaseNodeServiceTest.class.getClassLoader();
+ InputStream modelStream = cl.getResourceAsStream("alfresco/model/contentModel.xml");
+ assertNotNull(modelStream);
+ M2Model model = M2Model.createModel(modelStream);
+ dictionaryDao.putModel(model);
+
+ // load the test model
+ modelStream = cl.getResourceAsStream("org/alfresco/repo/node/BaseNodeServiceTest_model.xml");
+ assertNotNull(modelStream);
+ model = M2Model.createModel(modelStream);
+ dictionaryDao.putModel(model);
+
+ DictionaryComponent dictionary = new DictionaryComponent();
+ dictionary.setDictionaryDAO(dictionaryDao);
+ BaseNodeServiceTest.loadModel(ctx);
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ authenticationComponent.clearCurrentSecurityContext();
+ super.tearDown();
+ }
+
+ public void testRhino()
+ {
+ TransactionUtil.executeInUserTransaction(
+ transactionService,
+ new TransactionUtil.TransactionWork