diff --git a/src/helma/framework/ResponseTrans.java b/src/helma/framework/ResponseTrans.java index ae31d1f0..5cb9f65d 100644 --- a/src/helma/framework/ResponseTrans.java +++ b/src/helma/framework/ResponseTrans.java @@ -37,14 +37,12 @@ public class ResponseTrans implements Serializable { // the buffers used to build the single body parts - // transient, response must be constructed before this is serialized - public transient String title; - public transient String body; - public transient String message; + public transient String title, head, body, message; public ResponseTrans () { super (); - title = body = message = ""; + title = head = body = message = ""; } public void reset () { diff --git a/src/helma/framework/core/ESRequestData.java b/src/helma/framework/core/ESRequestData.java index 8013fb5e..035f536c 100644 --- a/src/helma/framework/core/ESRequestData.java +++ b/src/helma/framework/core/ESRequestData.java @@ -12,16 +12,16 @@ import helma.objectmodel.INode; /** - * An EcmaScript object to access the form data sent with a HTTP request + * An EcmaScript object that makes stuff in a hashtable accessible as its properties */ -public class ESRequestData extends ESWrapper { +public class ESRequestData extends ESObject { private Hashtable data; private RequestEvaluator reval; - public ESRequestData (ESObject prototype, Evaluator evaluator, RequestEvaluator reval) { - super (prototype, evaluator); + public ESRequestData (RequestEvaluator reval) { + super (null, reval.evaluator); this.reval = reval; } @@ -33,11 +33,11 @@ public class ESRequestData extends ESWrapper { * Overridden to make the object read-only */ public void putProperty(String propertyName, ESValue propertyValue, int hash) throws EcmaScriptException { - throw new EcmaScriptException ("Can't set property, req.data is read-only"); + throw new EcmaScriptException ("Can't set property, object is read-only"); } public boolean deleteProperty(String propertyName, int hash) throws EcmaScriptException { - throw new EcmaScriptException ("Can't delete property, req.data is read-only"); + throw new EcmaScriptException ("Can't delete property, object is read-only"); } public ESValue getProperty(String propertyName, int hash) throws EcmaScriptException { diff --git a/src/helma/framework/core/HopExtension.java b/src/helma/framework/core/HopExtension.java index 701962a8..dfbfcd5e 100644 --- a/src/helma/framework/core/HopExtension.java +++ b/src/helma/framework/core/HopExtension.java @@ -77,6 +77,8 @@ public class HopExtension { reval.esNodePrototype.putHiddenProperty ("href", new NodeHref ("href", evaluator, fp)); reval.esNodePrototype.putHiddenProperty ("setParent", new NodeSetParent ("setParent", evaluator, fp)); reval.esNodePrototype.putHiddenProperty ("invalidate", new NodeInvalidate ("invalidate", evaluator, fp)); + reval.esNodePrototype.putHiddenProperty("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, reval, false, false)); + reval.esNodePrototype.putHiddenProperty("renderSkin_as_string", new RenderSkin ("renderSkin_as_string", evaluator, fp, reval, false, true)); // methods that give access to properties and global user lists go.putHiddenProperty("Node", node); // register the constructor for a plain Node object. @@ -97,7 +99,10 @@ public class HopExtension { go.putHiddenProperty("getXmlDocument", new GlobalGetXmlDocument ("getXmlDocument", evaluator, fp)); go.putHiddenProperty("getHtmlDocument", new GlobalGetHtmlDocument ("getHtmlDocument", evaluator, fp)); go.putHiddenProperty("jdomize", new GlobalJDOM ("jdomize", evaluator, fp)); - go.putHiddenProperty("getSkin", new GlobalGetSkin ("getSkin", evaluator, fp, reval)); + go.putHiddenProperty("getSkin", new GlobalGetSkin ("getSkin", evaluator, fp)); + go.putHiddenProperty("createSkin", new GlobalCreateSkin ("createSkin", evaluator, fp)); + go.putHiddenProperty("renderSkin", new RenderSkin ("renderSkin", evaluator, fp, reval, true, false)); + go.putHiddenProperty("renderSkin_as_string", new RenderSkin ("renderSkin_as_string", evaluator, fp, reval, true, true)); go.deleteProperty("exit", "exit".hashCode()); // and some methods for session management from JS... @@ -548,19 +553,66 @@ public class HopExtension { } } + /** + * Get a parsed Skin from the central skin repository + */ class GlobalGetSkin extends BuiltinFunctionObject { - RequestEvaluator reval; - GlobalGetSkin (String name, Evaluator evaluator, FunctionPrototype fp, RequestEvaluator reval) { + GlobalGetSkin (String name, Evaluator evaluator, FunctionPrototype fp) { + super (fp, evaluator, name, 1); + } + public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + throw new EcmaScriptException ("getSkin() is not implemented yet, use createSkin() instead"); + } + } + + /** + * Get a parsed Skin from an app-managed skin text + */ + class GlobalCreateSkin extends BuiltinFunctionObject { + GlobalCreateSkin (String name, Evaluator evaluator, FunctionPrototype fp) { super (fp, evaluator, name, 1); - this.reval = reval; } public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { if (arguments.length != 1 || ESNull.theNull.equals (arguments[0])) throw new EcmaScriptException ("getSkin must be called with one String argument!"); - return new ESWrapper (new Skin (arguments[0].toString(), reval), evaluator); + return new ESWrapper (new Skin (arguments[0].toString()), evaluator); } } + /** + * Render a skin + */ + class RenderSkin extends BuiltinFunctionObject { + RequestEvaluator reval; + boolean global; + boolean asString; + RenderSkin (String name, Evaluator evaluator, FunctionPrototype fp, + RequestEvaluator reval, boolean global, boolean asString) { + super (fp, evaluator, name, 1); + this.reval = reval; + this.global = global; + this.asString = asString; + } + public ESValue callFunction (ESObject thisObject, ESValue[] arguments) throws EcmaScriptException { + if (arguments.length != 1 || !(arguments[0] instanceof ESWrapper)) + throw new EcmaScriptException ("renderSkin must be called with one Skin argument!"); + try { + Skin skin = (Skin) ((ESWrapper) arguments[0]).toJavaObject (); + ESNode handlerNode = global ? null : (ESNode) thisObject; + if (asString) + reval.res.pushStringBuffer (); + skin.render (reval, handlerNode); + if (asString) + return new ESString (reval.res.popStringBuffer ()); + } catch (ClassCastException x) { + throw new EcmaScriptException ("renderSkin must be called with one Skin argument!"); + } + return ESNull.theNull; + } + } + + + class GlobalGetUser extends BuiltinFunctionObject { RequestEvaluator reval; GlobalGetUser (String name, Evaluator evaluator, FunctionPrototype fp, RequestEvaluator reval) { diff --git a/src/helma/framework/core/RequestEvaluator.java b/src/helma/framework/core/RequestEvaluator.java index f2aaeeaa..1aea93d3 100644 --- a/src/helma/framework/core/RequestEvaluator.java +++ b/src/helma/framework/core/RequestEvaluator.java @@ -108,7 +108,7 @@ public class RequestEvaluator implements Runnable { appnode = new ESAppNode (app.appnode, this); global.putHiddenProperty ("app", appnode); reqPath = new ArrayPrototype (evaluator.getArrayPrototype(), evaluator); - reqData = new ESRequestData (evaluator.getObjectPrototype(), evaluator, this); + reqData = new ESRequestData (this); } catch (Exception e) { System.err.println("Cannot initialize interpreter"); diff --git a/src/helma/framework/core/Skin.java b/src/helma/framework/core/Skin.java index dfd7486b..2463771e 100644 --- a/src/helma/framework/core/Skin.java +++ b/src/helma/framework/core/Skin.java @@ -8,6 +8,8 @@ import java.io.*; import helma.framework.*; import FESI.Data.*; import FESI.Exceptions.*; +import helma.objectmodel.INode; +import helma.objectmodel.IServer; /** @@ -18,10 +20,8 @@ import FESI.Exceptions.*; public class Skin { Object[] parts; - RequestEvaluator reval; - public Skin (String content, RequestEvaluator reval) { - this.reval = reval; + public Skin (String content) { parse (content); } @@ -55,13 +55,15 @@ public class Skin { parts = partBuffer.toArray (); } - public String toString () { + public void render (RequestEvaluator reval, ESNode handlerNode) { if (parts == null) - return ""; - StringBuffer b = new StringBuffer (); - for (int i=0; i 0) try { - parameters.putHiddenProperty (lastParamName, new ESString (b.toString())); - } catch (EcmaScriptException noluck) {} + if (lastParamName != null && b.length() > 0) + parameters.put (lastParamName, b.toString()); } - public String toString () { + public void render (RequestEvaluator reval, ESNode handlerNode) { + + if ("response".equalsIgnoreCase (handler)) { + renderFromResponse (reval); + return; + } try { - ESValue[] arguments = new ESValue[2]; - arguments[0] = new ESString (name); - arguments[1] = parameters; - ESNode handlerNode = null; + ESObject handlerObject = null; + + ESValue[] arguments = new ESValue[1]; + ESRequestData par = new ESRequestData (reval); + par.setData (parameters); + arguments[0] = par; if (handler != null) { - int l = reval.reqPath.size(); - for (int i=l-1; i>=0; i--) { - if (handler.equalsIgnoreCase (((ESNode) reval.reqPath.getProperty(i)).getPrototypeName())) { - handlerNode = (ESNode) reval.reqPath.getProperty(i); - break; + // not a macro handled by global - check handler object + if (handlerNode != null) { + // was called with this object - check it or its parents for matching prototype + if (!handler.equalsIgnoreCase (handlerNode.getPrototypeName ())) { + // the handler object is not what we want + INode n = handlerNode.getNode(); + while (n != null) { + if (handler.equalsIgnoreCase (n.getPrototype())) { + handlerObject = reval.getNodeWrapper (n); + break; + } + n = n.getParent (); + } + } else { + // we already have the right handler object + handlerObject = handlerNode; } } - } else { - handlerNode = (ESNode) reval.reqPath.getProperty(0); + + if (handlerObject == null) { + // eiter because handlerNode == null or the right object wasn't found in the targetNode path + // go check request path for an object with matching prototype + int l = reval.reqPath.size(); + for (int i=l-1; i>=0; i--) { + if (handler.equalsIgnoreCase (((ESNode) reval.reqPath.getProperty(i)).getPrototypeName())) { + handlerObject = (ESNode) reval.reqPath.getProperty(i); + break; + } + } + } + + } else { + // this is a global macro with no handler specified + handlerObject = reval.global; } - if (handlerNode != null) { - return handlerNode.doIndirectCall (reval.evaluator, handlerNode, "handleMacro", arguments).toString(); + if (handlerObject != null) { + handlerObject.doIndirectCall (reval.evaluator, handlerObject, name+"_macro", arguments); } else { - return "[HopMacro unhandled: "+handler+"."+name+"]"; + String msg = "[HopMacro unhandled: "+handler+"."+name+"]"; + reval.res.write (" "+msg+" "); + IServer.getLogger().log (msg); } } catch (Exception x) { - return "[HopMacro error: "+x.getMessage()+"]"; + String msg = "[HopMacro error: "+x+"]"; + reval.res.write (" "+msg+" "); + IServer.getLogger().log (msg); } } + private void renderFromResponse (RequestEvaluator reval) { + if ("title".equals (name)) + reval.res.write (reval.res.title); + else if ("head".equals (name)) + reval.res.write (reval.res.head); + else if ("body".equals (name)) + reval.res.write (reval.res.body); + else if ("message".equals (name)) + reval.res.write (reval.res.message); + } + + public String toString () { + return "[HopMacro: "+handler+","+name+"]"; + } }