diff --git a/source/java/org/alfresco/repo/jscript/Node.java b/source/java/org/alfresco/repo/jscript/Node.java index 2d7940249c..60e563b0eb 100644 --- a/source/java/org/alfresco/repo/jscript/Node.java +++ b/source/java/org/alfresco/repo/jscript/Node.java @@ -56,6 +56,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.mozilla.javascript.Context; import org.mozilla.javascript.NativeArray; +import org.mozilla.javascript.ScriptRuntime; +import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.mozilla.javascript.Wrapper; import org.springframework.util.StringUtils; @@ -72,7 +74,7 @@ import org.springframework.util.StringUtils; * * @author Kevin Roast */ -public final class Node implements Serializable +public final class Node implements Serializable, Scopeable { private static Log logger = LogFactory.getLog(Node.class); @@ -81,18 +83,21 @@ public final class Node implements Serializable 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; + /** Root scope for this object */ + private Scriptable scope; /** Cached values */ private NodeRef nodeRef; private String name; private QName type; private String id; + /** The aspects applied to this node */ private Set aspects = null; + /** The associations from this node */ + private ScriptableQNameMap assocs = null; + /** The children of this node */ + private Node[] children = null; + /** The properties of this node */ private ScriptableQNameMap properties = null; private ServiceRegistry services = null; private NodeService nodeService = null; @@ -136,6 +141,14 @@ public final class Node implements Serializable this.imageResolver = resolver; } + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + // ------------------------------------------------------------------------------ // Node Wrapper API @@ -248,6 +261,7 @@ public final class Node implements Serializable { // create our Node representation from the NodeRef Node child = new Node(childRefs.get(i).getChildRef(), this.services, this.imageResolver); + child.setScope(this.scope); this.children[i] = child; } } @@ -338,6 +352,7 @@ public final class Node implements Serializable nodes = newNodes; } nodes[nodes.length - 1] = new Node(ref.getTargetRef(), this.services, this.imageResolver); + nodes[nodes.length - 1].setScope(this.scope); this.assocs.put(ref.getTypeQName().toString(), nodes); } @@ -371,11 +386,14 @@ public final class Node implements Serializable for (QName qname : props.keySet()) { Serializable propValue = props.get(qname); + + // perform conversions from Java objects to JavaScript scriptable instances 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); + ((Node)propValue).setScope(this.scope); } else if (propValue instanceof ContentData) { @@ -383,6 +401,18 @@ public final class Node implements Serializable // so the content and other properties of those objects can be accessed propValue = new ScriptContentData((ContentData)propValue, qname); } + else if (propValue instanceof Date) + { + // convert Date to JavaScript native Date object + // call the "Date" constructor on the root scope object - passing in the millisecond + // value from the Java date - this will construct a JavaScript Date with the same value + Date date = (Date)propValue; + Object val = ScriptRuntime.newObject( + Context.getCurrentContext(), this.scope, "Date", new Object[] {date.getTime()}); + propValue = (Serializable)val; + } + // simple numbers and strings are handled automatically by Rhino + this.properties.put(qname.toString(), propValue); } } @@ -675,6 +705,7 @@ public final class Node implements Serializable if (parentRef != null) { parent = new Node(parentRef, this.services, this.imageResolver); + parent.setScope(this.scope); } } @@ -945,6 +976,7 @@ public final class Node implements Serializable FileInfo fileInfo = this.services.getFileFolderService().create( this.nodeRef, name, ContentModel.TYPE_CONTENT); node = new Node(fileInfo.getNodeRef(), this.services, this.imageResolver); + node.setScope(this.scope); } } catch (FileExistsException fileErr) @@ -978,6 +1010,7 @@ public final class Node implements Serializable FileInfo fileInfo = this.services.getFileFolderService().create( this.nodeRef, name, ContentModel.TYPE_FOLDER); node = new Node(fileInfo.getNodeRef(), this.services, this.imageResolver); + node.setScope(this.scope); } } catch (FileExistsException fileErr) @@ -1019,6 +1052,7 @@ public final class Node implements Serializable createQName(type), props); node = new Node(childAssocRef.getChildRef(), this.services, this.imageResolver); + node.setScope(this.scope); } } catch (AccessDeniedException accessErr) @@ -1092,6 +1126,7 @@ public final class Node implements Serializable getPrimaryParentAssoc().getQName(), deepCopy); copy = new Node(copyRef, this.services, this.imageResolver); + copy.setScope(this.scope); } } catch (AccessDeniedException accessErr) @@ -1308,6 +1343,7 @@ public final class Node implements Serializable { result = new Node[1]; result[0] = new Node(nodes.get(0), this.services, this.imageResolver); + result[0].setScope(this.scope); } } // or all the results @@ -1318,6 +1354,7 @@ public final class Node implements Serializable { NodeRef ref = nodes.get(i); result[i] = new Node(ref, this.services, this.imageResolver); + result[i].setScope(this.scope); } } } diff --git a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java b/source/java/org/alfresco/repo/jscript/RhinoScriptService.java index 3dbe2ba130..45ead21d41 100644 --- a/source/java/org/alfresco/repo/jscript/RhinoScriptService.java +++ b/source/java/org/alfresco/repo/jscript/RhinoScriptService.java @@ -218,7 +218,18 @@ public class RhinoScriptService implements ScriptService { for (String key : model.keySet()) { - Object jsObject = Context.javaToJS(model.get(key), scope); + // set the root scope on appropriate objects + // this is used to allow native JS object creation etc. + Object obj = model.get(key); + if (obj instanceof Scopeable) + { + ((Scopeable)obj).setScope(scope); + } + + // convert/wrap each object to JavaScript compatible + Object jsObject = Context.javaToJS(obj, scope); + + // insert into the root scope ready for access by the script ScriptableObject.putProperty(scope, key, jsObject); } } diff --git a/source/java/org/alfresco/repo/jscript/Scopeable.java b/source/java/org/alfresco/repo/jscript/Scopeable.java new file mode 100644 index 0000000000..17aae1adca --- /dev/null +++ b/source/java/org/alfresco/repo/jscript/Scopeable.java @@ -0,0 +1,37 @@ +/* + * 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 org.mozilla.javascript.Scriptable; + +/** + * Interface contract for objects that supporting setting of the global scripting scope. + * This is used to mark objects that are not themselves natively scriptable (i.e. they are + * wrapped Java objects) but need to access the global scope for the purposes of JavaScript + * object creation etc. + * + * @author Kevin Roast + */ +public interface Scopeable +{ + /** + * Set the Scriptable global scope + * + * @param scope global scope + */ + void setScope(Scriptable scope); +} diff --git a/source/java/org/alfresco/repo/jscript/Search.java b/source/java/org/alfresco/repo/jscript/Search.java index ae89a2e178..6a0d55d74e 100644 --- a/source/java/org/alfresco/repo/jscript/Search.java +++ b/source/java/org/alfresco/repo/jscript/Search.java @@ -37,6 +37,7 @@ import org.apache.commons.logging.LogFactory; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; +import org.mozilla.javascript.Scriptable; /** * Search component for use by the ScriptService. @@ -51,7 +52,7 @@ import org.dom4j.io.SAXReader; * * @author Kevin Roast */ -public final class Search +public final class Search implements Scopeable { private static Log logger = LogFactory.getLog(Search.class); @@ -59,6 +60,9 @@ public final class Search private StoreRef storeRef; private TemplateImageResolver imageResolver; + /** Root scope for this object */ + private Scriptable scope; + /** * Constructor @@ -72,6 +76,14 @@ public final class Search this.imageResolver = imageResolver; } + /** + * @see org.alfresco.repo.jscript.Scopeable#setScope(org.mozilla.javascript.Scriptable) + */ + public void setScope(Scriptable scope) + { + this.scope = scope; + } + /** * Execute a Lucene search * @@ -171,7 +183,9 @@ public final class Search for (ResultSetRow row: results) { NodeRef nodeRef = row.getNodeRef(); - nodes[count++] = new Node(nodeRef, services, this.imageResolver); + nodes[count] = new Node(nodeRef, services, this.imageResolver); + nodes[count].setScope(this.scope); + count++; } } }