@Override
public String encodeActionURL(String url)
{
FacesContext ctx = FacesContext.getCurrentInstance();
String viewId = null, path = null;
QueryString queryStr = null;
int queryStart = -1;
boolean isPortletURL = false;
boolean isPortletURLSelfReference = false;
boolean isStrictXhtmlEncoded = isStrictXhtmlEncoded(url);
Bridge.PortletPhase urlType = Bridge.PortletPhase.ACTION_PHASE;
// First check to see if the special URI indicating we should encode
// a Nonfaces target to just the current portlet (without an associated
// path based resource).
if (isPortletURL(url))
{
isPortletURL = true;
//URL is of the form scheme:urlType?queryString
// remove the scheme
path = url.substring(url.indexOf(":")+ 1);
queryStart = path.indexOf('?');
if (queryStart != -1)
{
// Get the query string
queryStr = new QueryString(path.substring(queryStart + 1), "UTF8");
path = path.substring(0, queryStart);
}
if (path.equalsIgnoreCase("render"))
{
urlType = Bridge.PortletPhase.RENDER_PHASE;
}
else if (path.equalsIgnoreCase("action"))
{
urlType = Bridge.PortletPhase.ACTION_PHASE;
}
else
{
log("PortletExternalContextImpl.encodeActionURL: malformed portlet url "
+ url);
return url;
}
// We allow use of this syntax to reference another (or this) jsf page --
// For example if one wants to create a redisplay link for this page
// we recognize its a JSF page because it includes a QS parameter
// that references either the viewId or viewPath
String s = queryStr.getParameter(Bridge.FACES_VIEW_ID_PARAMETER);
String s1 = queryStr.getParameter(Bridge.FACES_VIEW_PATH_PARAMETER);
if (s != null && s.equals(Bridge.FACES_USE_CURRENT_VIEW_PARAMETER))
{
isPortletURLSelfReference = true;
// by removing the parameter we will rely on retaining the current view info based on \
// the current render params
queryStr.removeParameter(Bridge.FACES_VIEW_ID_PARAMETER);
}
else if (s != null && s1.equals(Bridge.FACES_USE_CURRENT_VIEW_PARAMETER))
{
isPortletURLSelfReference = true;
// by removing the parameter we will rely on retaining the current view info based on \
// the current render params
queryStr.removeParameter(Bridge.FACES_VIEW_PATH_PARAMETER);
}
}
else if (url.startsWith("#") || isExternalURL(url) || isDirectLink(url))
{
return url;
}
else
{
// Its a Path encoded URL
// url might contain DirectLink=false parameter -- spec says remove if
// it does.
url = removeDirectLink(url);
// Now determine the target viewId
// First: split URL into path and query string
// Hold onto QueryString for later processing
queryStart = url.indexOf('?');
if (queryStart != -1)
{
// Get the query string
queryStr = new QueryString(url.substring(queryStart + 1), "UTF8");
path = url.substring(0, queryStart);
}
else
{
path = url;
// construct an empty queryString to hold the viewId
queryStr = new QueryString("UTF8");
}
// Convert relative path to context path
if (isRelativePath(path))
{
path = getPathFromRelativePath(path);
}
// Now se if this is a Faces URL
viewId = getViewIdFromPath(path);
if (viewId != null)
{
encodeFacesActionTarget(queryStr, viewId);
}
else
{
// URL points at non-Faces action
encodeNonFacesActionTarget(queryStr, path);
}
}
if (getPortletPhase() == Bridge.PortletPhase.RENDER_PHASE)
{ // render - write
// the viewId into
// the response
// (interaction
// state)
RenderResponse renderResponse = (RenderResponse) getResponse();
PortletURL actionURL = null;
// Non-JSF actions are renderURLs as we merely dispatch to them
if (urlType == Bridge.PortletPhase.ACTION_PHASE)
{
actionURL = renderResponse.createActionURL();
}
else
{
actionURL = renderResponse.createRenderURL();
}
// Add parameters so they don't get lost
Enumeration<String> list = queryStr.getParameterNames();
while (list.hasMoreElements())
{
String param = list.nextElement().toString();
if (param.equals(Bridge.PORTLET_MODE_PARAMETER))
{
try
{
actionURL.setPortletMode(new PortletMode(queryStr.getParameter(param)));
}
catch (Exception e)
{
; // do nothing -- just ignore
}
}
else if (param.equals(Bridge.PORTLET_WINDOWSTATE_PARAMETER))
{
try
{
actionURL.setWindowState(new WindowState(queryStr.getParameter(param)));
}
catch (Exception e)
{
; // do nothing -- just ignore
}
}
else if (param.equals(Bridge.PORTLET_SECURE_PARAMETER))
{
try
{
actionURL.setSecure(Boolean.getBoolean(queryStr.getParameter(param)));
}
catch (Exception e)
{
; // do nothing -- just ignore
}
}
else
{
actionURL.setParameter(param, queryStr.getParameter(param));
}
}
// Carry forward render parameters if this is a portlet:url that references the current view
if (isPortletURLSelfReference)
{
Map m = mOrigPortletRequest.getParameterMap();
Set<Map.Entry<String, String[]>> s = m.entrySet();
Iterator<Map.Entry<String, String[]>> i = s.iterator();
while (i.hasNext())
{
Map.Entry<String, String[]> entry = i.next();
// only add if not already added from above
if (queryStr.getParameter(entry.getKey()) == null)
{
actionURL.setParameter(entry.getKey(), entry.getValue());
}
}
}
url = actionURL.toString();
// JSF expects encodeActionURL to not perturb any XML encoding (or lack thereof) of the url.
// I.e. the caller is responsible for either pre or post XML escaping the string sent/returned
// by encodeActionURL. Unfortunately, the portlet 1.0 spec didn't define whether toString()
// returns an XML encoded string or not. Most containers behaved like the servlet environment and did not,
// however wsrp based containers had the option of encoding the consumer rewrite url either with & or &.
// Some containers choose the later.
//
// So special care must be taken here to fix up the url returned by the container
// to have the same encoding as what was sent in.
if (!isStrictXhtmlEncoded)
{
// If we weren't already Xhtml encoded (used &) then renderkit will likely
// do it after us if it needs to do such encoding -- so undo any xml encoding the portlet URL might
// have done to avoid double encoding -- Otherwise if the string passed in was encoded the renderkit
// is unlikely to reencode after the call -- so don't undo any encoding.
url = url.replace("&", "&");
}
else if (!isStrictXhtmlEncoded(url)) // check the new url and process if its not xml encoded
{
url = xhtmlEncode(url);
}
}
else
{ // action - write the viewId to navigational state
ActionResponse actionResponse = (ActionResponse) getResponse();
// set request params into navigational states
Enumeration<String> list = queryStr.getParameterNames();
while (list.hasMoreElements())
{
String param = list.nextElement();
if (param.equals(Bridge.PORTLET_MODE_PARAMETER))
{
try
{
actionResponse.setPortletMode(new PortletMode(queryStr.getParameter(param)));
}
catch (Exception e)
{
//TODO: Ignoring is probably dangerous here as it means that we are
// EITHER using exceptions for flow control (which is extreemly
// inefficient) or we should log a message saying what the issue
// is. According to the Javadocs an exception is thrown here if the
// portlet mode is not allowed or if sendRedirect has already been
// called. In either case we should log an information type message
// here.
; // do nothing -- just ignore
}
}
else if (param.equals(Bridge.PORTLET_WINDOWSTATE_PARAMETER))
{
try
{
actionResponse.setWindowState(new WindowState(queryStr.getParameter(param)));
}
catch (Exception e)
{
; // do nothing -- just ignore
}
}
else if (param.equals(Bridge.PORTLET_SECURE_PARAMETER))
{
; // ignore -- do nothing as can't encode into an actionResponse
}
else
{
actionResponse.setRenderParameter(param, queryStr.getParameter(param));
}
}
}
// Because we want to support translating a redirect that occurs
// during a render as an in place navigation AND we can't reverse