WebScript Runtime response/url model extended to support output of client-side webscript url generation function.

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@5902 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2007-06-09 17:03:11 +00:00
parent bb2536fcf5
commit a3c59244d1
18 changed files with 172 additions and 44 deletions

View File

@@ -90,7 +90,7 @@
<div id="spacePanel"> <div id="spacePanel">
<#-- populated via an AJAX call to 'myspacespanel' webscript --> <#-- populated via an AJAX call to 'myspacespanel' webscript -->
<#-- resolved path, filter and home.noderef required as arguments --> <#-- resolved path, filter and home.noderef required as arguments -->
<script>MySpaces.ServiceContext="${url.serviceContext}";MySpaces.Path="${path?replace("\"","\\\"")}";MySpaces.Filter="${filter}";MySpaces.Home="${home.nodeRef}";</script> <script>MySpaces.ServiceContext="${url.serviceContext}";MySpaces.ScriptUrlEncoder=eval("MySpaces.ScriptUrlEncoder=" + unescape("${url.getClientUrlFunction("encUrl")}"));MySpaces.Path="${path?replace("\"","\\\"")}";MySpaces.Filter="${filter}";MySpaces.Home="${home.nodeRef}";</script>
</div> </div>
<div class="spaceFooter"> <div class="spaceFooter">
<#-- the count value is retrieved and set dynamically from the AJAX webscript output above --> <#-- the count value is retrieved and set dynamically from the AJAX webscript output above -->

View File

@@ -14,7 +14,10 @@
<#if d.isDocument> <#if d.isDocument>
<a href="${url.context}${d.url}" target="new" onclick="event.cancelBubble=true"><img class="spaceIconImage" alt="" width="16" height="16" src="${url.context}${d.icon16?replace(".gif",".png")}" border=0></a> <a href="${url.context}${d.url}" target="new" onclick="event.cancelBubble=true"><img class="spaceIconImage" alt="" width="16" height="16" src="${url.context}${d.icon16?replace(".gif",".png")}" border=0></a>
<#else> <#else>
<a href="${scripturl("${url.serviceContext}/myspaces?f=${args.f}&p=${args.p}/${d.name}", false)}"><img class="spaceIconImage" alt="" width="16" height="16" src="${url.context}${d.icon16?replace(".gif",".png")}" border="0" onclick="event.cancelBubble=true;"></a> <#-- the component parts need to build up an encoded url to the outer webscript -->
<#-- the client-side url encoder method of the outer webscript runtime will be used -->
<span class="spaceNavLinkUrl">${url.serviceContext}/myspaces?f=${args.f}&amp;p=${args.p}/${d.name}</span>
<span class="spaceNavLinkImg" style="display:none"><img class="spaceIconImage" alt="" width="16" height="16" src="${url.context}${d.icon16?replace(".gif",".png")}" border="0"></span>
</#if> </#if>
<div style="display:none"><img class="spaceIconImage64" alt="" width="64" height="64" src="${url.context}${d.icon64}"></div> <div style="display:none"><img class="spaceIconImage64" alt="" width="64" height="64" src="${url.context}${d.icon64}"></div>
</div> </div>

View File

@@ -206,11 +206,12 @@ public abstract class AbstractWebScript implements WebScript
* Create a model for script usage * Create a model for script usage
* *
* @param req web script request * @param req web script request
* @param res web script response
* @param customModel custom model entries * @param customModel custom model entries
* *
* @return script model * @return script model
*/ */
final protected Map<String, Object> createScriptModel(WebScriptRequest req, Map<String, Object> customModel) final protected Map<String, Object> createScriptModel(WebScriptRequest req, WebScriptResponse res, Map<String, Object> customModel)
{ {
// create script model // create script model
Map<String, Object> model = new HashMap<String, Object>(7, 1.0f); Map<String, Object> model = new HashMap<String, Object>(7, 1.0f);
@@ -235,7 +236,7 @@ public abstract class AbstractWebScript implements WebScript
// add web script context // add web script context
model.put("args", createArgModel(req)); model.put("args", createArgModel(req));
model.put("guest", req.isGuest()); model.put("guest", req.isGuest());
model.put("url", new URLModel(req)); model.put("url", new URLModel(req, res));
model.put("server", new ServerModel(descriptorService.getServerDescriptor())); model.put("server", new ServerModel(descriptorService.getServerDescriptor()));
// add custom model // add custom model
@@ -282,7 +283,7 @@ public abstract class AbstractWebScript implements WebScript
// add web script context // add web script context
model.put("args", createArgModel(req)); model.put("args", createArgModel(req));
model.put("guest", req.isGuest()); model.put("guest", req.isGuest());
model.put("url", new URLModel(req)); model.put("url", new URLModel(req, res));
model.put("server", new ServerModel(descriptorService.getServerDescriptor())); model.put("server", new ServerModel(descriptorService.getServerDescriptor()));
// add template support // add template support

View File

@@ -109,7 +109,7 @@ public class DeclarativeWebScript extends AbstractWebScript
if (logger.isDebugEnabled()) if (logger.isDebugEnabled())
logger.debug("Executing script " + executeScript); logger.debug("Executing script " + executeScript);
Map<String, Object> scriptModel = createScriptModel(req, model); Map<String, Object> scriptModel = createScriptModel(req, res, model);
// add return model allowing script to add items to template model // add return model allowing script to add items to template model
Map<String, Object> returnModel = new ScriptableHashMap<String, Object>(); Map<String, Object> returnModel = new ScriptableHashMap<String, Object>();
scriptModel.put("model", returnModel); scriptModel.put("model", returnModel);

View File

@@ -33,15 +33,18 @@ package org.alfresco.web.scripts;
public class URLModel public class URLModel
{ {
private WebScriptRequest req; private WebScriptRequest req;
private WebScriptResponse res;
/** /**
* Construct * Construct
* *
* @param req * @param req
* @param res
*/ */
URLModel(WebScriptRequest req) URLModel(WebScriptRequest req, WebScriptResponse res)
{ {
this.req = req; this.req = req;
this.res = res;
} }
/** /**
@@ -153,5 +156,16 @@ public class URLModel
{ {
return getExtension(); return getExtension();
} }
/**
* Gets the client url encoding function script
*
* @param name name of the function object to return
*
* @return script to encode urls on the client
*/
public String getClientUrlFunction(String name)
{
return res.getEncodeScriptUrlFunction(name);
}
} }

View File

@@ -166,5 +166,4 @@ public interface WebScriptRequest
* @return true => force return of 200, otherwise return status explicitly set * @return true => force return of 200, otherwise return status explicitly set
*/ */
public boolean forceSuccessStatus(); public boolean forceSuccessStatus();
} }

View File

@@ -34,7 +34,6 @@ import org.alfresco.web.scripts.WebScriptDescription.FormatStyle;
*/ */
public abstract class WebScriptRequestImpl implements WebScriptRequest public abstract class WebScriptRequestImpl implements WebScriptRequest
{ {
/* (non-Javadoc) /* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptRequest#getExtensionPath() * @see org.alfresco.web.scripts.WebScriptRequest#getExtensionPath()
*/ */
@@ -154,5 +153,4 @@ public abstract class WebScriptRequestImpl implements WebScriptRequest
{ {
return false; return false;
} }
} }

View File

@@ -82,11 +82,19 @@ public interface WebScriptResponse
/** /**
* Encode a script URL * Encode a script URL
* *
* Note: Some Web Script Runtime environments (e.g. portal) require urls to be re-written. * Note: Some Web Script Runtime environments (e.g. JSR-168, JSF) require urls to be re-written.
* *
* @param url url to encode * @param url to encode
* @return encoded url * @return encoded url
*/ */
public String encodeScriptUrl(String url); public String encodeScriptUrl(String url);
/**
* Return a client side javascript function to build urls to this service
*
* @param name Generated function name
*
* @return javascript function definition
*/
public String getEncodeScriptUrlFunction(String name);
} }

View File

@@ -204,7 +204,7 @@ public abstract class WebScriptRuntime
WebScriptResponse res = createResponse(); WebScriptResponse res = createResponse();
Map<String, Object> model = new HashMap<String, Object>(); Map<String, Object> model = new HashMap<String, Object>();
model.put("status", status); model.put("status", status);
model.put("url", new URLModel(req)); model.put("url", new URLModel(req, res));
// locate status template // locate status template
// NOTE: search order... // NOTE: search order...

View File

@@ -30,6 +30,8 @@ import java.io.Writer;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.alfresco.web.ui.common.Utils;
/** /**
* HTTP Servlet Web Script Response * HTTP Servlet Web Script Response
* *
@@ -113,4 +115,13 @@ public class WebScriptServletResponse implements WebScriptResponse
return url; return url;
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#getEncodeScriptUrlFunction(java.lang.String)
*/
public String getEncodeScriptUrlFunction(String name)
{
return Utils.encodeJavascript(ENCODE_FUNCTION.replace("$name$", name));
}
private static final String ENCODE_FUNCTION = "{ $name$: function(url) { return url; } }";
} }

View File

@@ -87,7 +87,6 @@ public abstract class WebScriptURLRequest extends WebScriptRequestImpl
return urlParts; return urlParts;
} }
/** /**
* Construct * Construct
* *
@@ -199,6 +198,4 @@ public abstract class WebScriptURLRequest extends WebScriptRequestImpl
{ {
return queryArgs.get(name); return queryArgs.get(name);
} }
} }

View File

@@ -38,7 +38,6 @@ import org.alfresco.web.scripts.WebScriptURLRequest;
*/ */
public class WebScriptJSFRequest extends WebScriptURLRequest public class WebScriptJSFRequest extends WebScriptURLRequest
{ {
/** /**
* Constructor * Constructor
* *
@@ -85,5 +84,4 @@ public class WebScriptJSFRequest extends WebScriptURLRequest
// NOTE: unknown in the JSF environment // NOTE: unknown in the JSF environment
return null; return null;
} }
} }

View File

@@ -105,11 +105,12 @@ public class WebScriptJSFResponse implements WebScriptResponse
return buf.toString(); return buf.toString();
} }
/* (non-Javadoc) /**
* @see org.alfresco.web.scripts.WebScriptResponse#reset() * @see org.alfresco.web.scripts.WebScriptResponse#reset()
*/ */
public void reset() public void reset()
{ {
// nothing to do
} }
/** /**
@@ -128,11 +129,12 @@ public class WebScriptJSFResponse implements WebScriptResponse
return fc.getResponseWriter(); return fc.getResponseWriter();
} }
/* (non-Javadoc) /**
* @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int) * @see org.alfresco.web.scripts.WebScriptResponse#setStatus(int)
*/ */
public void setStatus(int status) public void setStatus(int status)
{ {
// makes no sense in the JSF env
} }
/** /**
@@ -142,5 +144,32 @@ public class WebScriptJSFResponse implements WebScriptResponse
{ {
// Alfresco JSF framework only supports the default of text-html // Alfresco JSF framework only supports the default of text-html
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#getEncodeScriptUrlFunction(java.lang.String)
*/
public String getEncodeScriptUrlFunction(String name)
{
UIForm form = Utils.getParentForm(fc, component);
if (form == null)
{
throw new IllegalStateException("Must nest components inside UIForm to generate form submit!");
}
String fieldId = component.getClientId(fc);
String formClientId = form.getClientId(fc);
HtmlFormRendererBase.addHiddenCommandParameter(fc, form, fieldId);
String func = ENCODE_FUNCTION.replace("$name$", name);
func = func.replace("$formClientId$", formClientId);
func = func.replace("$fieldId$", fieldId);
return Utils.encodeJavascript(func);
}
private static final String ENCODE_FUNCTION =
"{ $name$: function(url) {" +
" var out = '';" +
" out += \"#\\\" onclick=\\\"document.forms['$formClientId$']['$fieldId$'].value='\";" +
" out += escape(url);" +
" out += \"';document.forms['$formClientId$'].submit();return false;\";" +
" return out; } }";
} }

View File

@@ -33,6 +33,7 @@ import javax.portlet.RenderResponse;
import org.alfresco.web.scripts.WebScriptRequest; import org.alfresco.web.scripts.WebScriptRequest;
import org.alfresco.web.scripts.WebScriptResponse; import org.alfresco.web.scripts.WebScriptResponse;
import org.alfresco.web.ui.common.Utils;
/** /**
@@ -127,4 +128,34 @@ public class WebScriptPortletResponse implements WebScriptResponse
return portletUrl.toString(); return portletUrl.toString();
} }
/* (non-Javadoc)
* @see org.alfresco.web.scripts.WebScriptResponse#getEncodeScriptUrlFunction(java.lang.String)
*/
public String getEncodeScriptUrlFunction(String name)
{
PortletURL portletUrl = res.createActionURL();
String func = ENCODE_FUNCTION.replace("$name$", name);
func = func.replace("$actionUrl$", portletUrl.toString());
return Utils.encodeJavascript(func);
}
private static final String ENCODE_FUNCTION =
"{ $name$: function(url) {" +
" var out = \"$actionUrl$\";" +
" var argsIndex = url.indexOf(\"?\");" +
" if (argsIndex == -1)" +
" {" +
" out += \"&scriptUrl=\" + escape(url);" +
" }" +
" else" +
" {" +
" out += \"&scriptUrl=\" + escape(url.substring(0, argsIndex));" +
" var args = url.substring(argsIndex + 1).split(\"&\");" +
" for (var i=0; i<args.length; i++)" +
" {" +
" out += \"arg.\" + args[i];" +
" }" +
" }" +
" return out; } }";
} }

View File

@@ -116,7 +116,6 @@ public final class Utils
* *
* @param string the String to convert * @param string the String to convert
*/ */
//XXXarielb perhaps use org.springframework.web.util.HtmlUtils instead?
public static String encode(String string) public static String encode(String string)
{ {
if (string == null) if (string == null)
@@ -235,6 +234,28 @@ public final class Utils
} }
} }
/**
* Encode a string to the %AB hex style JavaScript compatible notation.
* Used to encode a string to a value that can be safely inserted into an HTML page and
* then decoded (and probably eval()ed) using the unescape() JavaScript method.
*
* @param s string to encode
*
* @return %AB hex style encoded string
*/
public static String encodeJavascript(String s)
{
StringBuilder buf = new StringBuilder(s.length() * 3);
for (int i=0; i<s.length(); i++)
{
char c = s.charAt(i);
int iChar = (int)c;
buf.append('%');
buf.append(Integer.toHexString(iChar));
}
return buf.toString();
}
/** /**
* Replace one string instance with another within the specified string * Replace one string instance with another within the specified string
* *
@@ -474,11 +495,9 @@ public final class Utils
String formClientId = form.getClientId(context); String formClientId = form.getClientId(context);
StringBuilder buf = new StringBuilder(200); StringBuilder buf = new StringBuilder(200);
buf.append("document.forms["); buf.append("document.forms['");
buf.append("'");
buf.append(formClientId); buf.append(formClientId);
buf.append("'"); buf.append("']['");
buf.append("]['");
buf.append(fieldId); buf.append(fieldId);
buf.append("'].value="); buf.append("'].value=");
if (valueIsParam == false) if (valueIsParam == false)
@@ -496,11 +515,9 @@ public final class Utils
{ {
for (String name : params.keySet()) for (String name : params.keySet())
{ {
buf.append("document.forms["); buf.append("document.forms['");
buf.append("'");
buf.append(formClientId); buf.append(formClientId);
buf.append("'"); buf.append("']['");
buf.append("]['");
buf.append(name); buf.append(name);
buf.append("'].value='"); buf.append("'].value='");
buf.append(params.get(name)); buf.append(params.get(name));
@@ -512,11 +529,9 @@ public final class Utils
} }
} }
buf.append("document.forms["); buf.append("document.forms['");
buf.append("'");
buf.append(formClientId); buf.append(formClientId);
buf.append("'"); buf.append("'].submit();");
buf.append("].submit();");
if (valueIsParam == false) if (valueIsParam == false)
{ {
@@ -550,11 +565,9 @@ public final class Utils
StringBuilder buf = new StringBuilder(48); StringBuilder buf = new StringBuilder(48);
buf.append("document.forms["); buf.append("document.forms['");
buf.append("'");
buf.append(formClientId); buf.append(formClientId);
buf.append("'"); buf.append("'].submit()");
buf.append("].submit()");
buf.append(";return false;"); buf.append(";return false;");

View File

@@ -29,7 +29,7 @@
<init-param> <init-param>
<name>scriptUrl</name> <name>scriptUrl</name>
<value>/alfresco/service/mytasks</value> <value>/alfresco/wcservice/mytasks</value>
</init-param> </init-param>
<supports> <supports>
@@ -50,7 +50,7 @@
<init-param> <init-param>
<name>scriptUrl</name> <name>scriptUrl</name>
<value>/alfresco/service/doclist</value> <value>/alfresco/wcservice/doclist</value>
</init-param> </init-param>
<supports> <supports>
@@ -71,7 +71,7 @@
<init-param> <init-param>
<name>scriptUrl</name> <name>scriptUrl</name>
<value>/alfresco/service/myspaces</value> <value>/alfresco/wcservice/myspaces</value>
</init-param> </init-param>
<supports> <supports>
@@ -92,7 +92,7 @@
<init-param> <init-param>
<name>scriptUrl</name> <name>scriptUrl</name>
<value>/alfresco/service/mywebforms</value> <value>/alfresco/wcservice/mywebforms</value>
</init-param> </init-param>
<supports> <supports>

View File

@@ -11,6 +11,7 @@ var MySpaces = {
Home: null, Home: null,
ServiceContext: null, ServiceContext: null,
popupPanel: null, popupPanel: null,
ScriptUrlEncoder: null,
start: function() start: function()
{ {
@@ -26,14 +27,32 @@ var MySpaces = {
{ {
// push the response into the space panel div // push the response into the space panel div
$('spacePanel').setHTML(response.responseText); $('spacePanel').setHTML(response.responseText);
// Construct the links to the outer webscript - we use the client script url encoder
// method provided by the outer webscript runtime to generate urls to call it from the
// inner webscript we called via a servlet - this means we can gen urls to call JSF or
// portlet webscript from the inner.
var navLinks = $$('#spacePanel .spaceNavLinkUrl');
var navImgs = $$('#spacePanel .spaceNavLinkImg');
navLinks.each(function(navLink, i)
{
navLink.setHTML('<a href="' + MySpaces.ScriptUrlEncoder.encUrl(navLink.innerHTML.replace(/&amp;/, "&")) + '">' + navImgs[i].innerHTML + '</a>');
navImgs[i].innerHTML = ""; // remove the html so the class is not selected during init()
});
// extract the count value from a hidden div and display it // extract the count value from a hidden div and display it
$('spaceCount').setHTML($('spaceCountValue').innerHTML); $('spaceCount').setHTML($('spaceCountValue').innerHTML);
// wire up all the events and animations // wire up all the events and animations
MySpaces.init(); MySpaces.init();
}, },
failure: function(response) failure: function(response)
{ {
// display the error
$('spacePanel').setHTML("Sorry, data currently unavailable."); $('spacePanel').setHTML("Sorry, data currently unavailable.");
// hide the ajax wait panel and show the main spaces panel
$('spacePanelOverlay').setStyle('visibility', 'hidden');
$('spacePanel').setStyle('visibility', 'visible');
} }
} }
); );
@@ -43,6 +62,7 @@ var MySpaces = {
init: function() init: function()
{ {
MySpaces.parseSpacePanels(); MySpaces.parseSpacePanels();
// hide the ajax wait panel and show the main spaces panel // hide the ajax wait panel and show the main spaces panel
$('spacePanelOverlay').setStyle('visibility', 'hidden'); $('spacePanelOverlay').setStyle('visibility', 'hidden');
$('spacePanel').setStyle('visibility', 'visible'); $('spacePanel').setStyle('visibility', 'visible');

View File

@@ -25,7 +25,12 @@ var MyTasks = {
}, },
failure: function(response) failure: function(response)
{ {
// display the error
$('taskPanel').setHTML("Sorry, data currently unavailable."); $('taskPanel').setHTML("Sorry, data currently unavailable.");
// hide the ajax wait panel and show the main task panel
$('taskPanelOverlay').setStyle('visibility', 'hidden');
$('taskPanel').setStyle('visibility', 'visible');
} }
} }
); );
@@ -35,6 +40,7 @@ var MyTasks = {
init: function() init: function()
{ {
MyTasks.parseTaskPanels(); MyTasks.parseTaskPanels();
// hide the ajax wait panel and show the main task panel // hide the ajax wait panel and show the main task panel
$('taskPanelOverlay').setStyle('visibility', 'hidden'); $('taskPanelOverlay').setStyle('visibility', 'hidden');
$('taskPanel').setStyle('visibility', 'visible'); $('taskPanel').setStyle('visibility', 'visible');