mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-08-07 17:49:17 +00:00
Change header case check to case-insensitive in RemoteClient. Addition of helper object to Script root scope for WebScripts - 'scriptUtils' with helper methods for encoding and stripping of HTML/JS etc. git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@10216 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
996 lines
35 KiB
Java
996 lines
35 KiB
Java
/*
|
|
* Copyright (C) 2005-2007 Alfresco Software Limited.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
* As a special exception to the terms and conditions of version 2.0 of
|
|
* the GPL, you may redistribute this Program in connection with Free/Libre
|
|
* and Open Source Software ("FLOSS") applications as described in Alfresco's
|
|
* FLOSS exception. You should have recieved a copy of the text describing
|
|
* the FLOSS exception, and it is also available here:
|
|
* http://www.alfresco.com/legal/licensing"
|
|
*/
|
|
package org.alfresco.web.ui.common;
|
|
|
|
import java.io.IOException;
|
|
import java.text.DateFormat;
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.Calendar;
|
|
import java.util.Date;
|
|
import java.util.Enumeration;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import javax.faces.application.FacesMessage;
|
|
import javax.faces.component.NamingContainer;
|
|
import javax.faces.component.UIComponent;
|
|
import javax.faces.component.UIForm;
|
|
import javax.faces.context.FacesContext;
|
|
import javax.faces.context.ResponseWriter;
|
|
import javax.faces.el.EvaluationException;
|
|
import javax.faces.el.MethodBinding;
|
|
import javax.faces.event.AbortProcessingException;
|
|
import javax.faces.event.ActionEvent;
|
|
|
|
import org.alfresco.config.ConfigElement;
|
|
import org.alfresco.error.AlfrescoRuntimeException;
|
|
import org.alfresco.filesys.CIFSServerBean;
|
|
import org.alfresco.filesys.repo.ContentContext;
|
|
import org.alfresco.jlan.server.core.SharedDevice;
|
|
import org.alfresco.jlan.server.core.SharedDeviceList;
|
|
import org.alfresco.jlan.server.filesys.DiskSharedDevice;
|
|
import org.alfresco.jlan.server.filesys.FilesystemsConfigSection;
|
|
import org.alfresco.model.ApplicationModel;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.repo.security.permissions.AccessDeniedException;
|
|
import org.alfresco.repo.webdav.WebDAVServlet;
|
|
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
|
import org.alfresco.service.cmr.model.FileFolderService;
|
|
import org.alfresco.service.cmr.model.FileInfo;
|
|
import org.alfresco.service.cmr.model.FileNotFoundException;
|
|
import org.alfresco.service.cmr.repository.InvalidNodeRefException;
|
|
import org.alfresco.service.cmr.repository.NoTransformerException;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.util.URLEncoder;
|
|
import org.alfresco.web.app.Application;
|
|
import org.alfresco.web.app.servlet.DownloadContentServlet;
|
|
import org.alfresco.web.app.servlet.ExternalAccessServlet;
|
|
import org.alfresco.web.bean.NavigationBean;
|
|
import org.alfresco.web.bean.repository.Node;
|
|
import org.alfresco.web.bean.repository.Repository;
|
|
import org.alfresco.web.data.IDataContainer;
|
|
import org.alfresco.web.ui.common.component.UIStatusMessage;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
import org.apache.myfaces.shared_impl.renderkit.html.HtmlFormRendererBase;
|
|
import org.springframework.web.jsf.FacesContextUtils;
|
|
|
|
/**
|
|
* Class containing misc helper methods used by the JSF components.
|
|
*
|
|
* @author Kevin Roast
|
|
*/
|
|
public final class Utils extends StringUtils
|
|
{
|
|
private static final String MSG_TIME_PATTERN = "time_pattern";
|
|
private static final String MSG_DATE_PATTERN = "date_pattern";
|
|
private static final String MSG_DATE_TIME_PATTERN = "date_time_pattern";
|
|
|
|
private static final Log logger = LogFactory.getLog(Utils.class);
|
|
|
|
/**
|
|
* Private constructor
|
|
*/
|
|
private Utils()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Helper to output an attribute to the output stream
|
|
*
|
|
* @param out ResponseWriter
|
|
* @param attr attribute value object (cannot be null)
|
|
* @param mapping mapping to output as e.g. style="..."
|
|
*
|
|
* @throws IOException
|
|
*/
|
|
public static void outputAttribute(ResponseWriter out, Object attr, String mapping)
|
|
throws IOException
|
|
{
|
|
if (attr != null)
|
|
{
|
|
out.write(' ');
|
|
out.write(mapping);
|
|
out.write("=\"");
|
|
out.write(attr.toString());
|
|
out.write('"');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the hidden field name for any action component.
|
|
*
|
|
* All components that wish to simply encode a form value with their client ID can reuse the same
|
|
* hidden field within the parent form. NOTE: components which use this method must only encode
|
|
* their client ID as the value and nothing else!
|
|
*
|
|
* Build a shared field name from the parent form name and the string "act".
|
|
*
|
|
* @return hidden field name shared by all action components within the Form.
|
|
*/
|
|
public static String getActionHiddenFieldName(FacesContext context, UIComponent component)
|
|
{
|
|
return Utils.getParentForm(context, component).getClientId(context) + NamingContainer.SEPARATOR_CHAR + "act";
|
|
}
|
|
|
|
/**
|
|
* Helper to recursively render a component and it's child components
|
|
*
|
|
* @param context FacesContext
|
|
* @param component UIComponent
|
|
*
|
|
* @throws IOException
|
|
*/
|
|
public static void encodeRecursive(FacesContext context, UIComponent component)
|
|
throws IOException
|
|
{
|
|
if (component.isRendered() == true)
|
|
{
|
|
component.encodeBegin(context);
|
|
|
|
// follow the spec for components that render their children
|
|
if (component.getRendersChildren() == true)
|
|
{
|
|
component.encodeChildren(context);
|
|
}
|
|
else
|
|
{
|
|
if (component.getChildCount() != 0)
|
|
{
|
|
for (Iterator i=component.getChildren().iterator(); i.hasNext(); /**/)
|
|
{
|
|
encodeRecursive(context, (UIComponent)i.next());
|
|
}
|
|
}
|
|
}
|
|
|
|
component.encodeEnd(context);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Generate the JavaScript to submit set the specified hidden Form field to the
|
|
* supplied value and submit the parent Form.
|
|
*
|
|
* NOTE: the supplied hidden field name is added to the Form Renderer map for output.
|
|
*
|
|
* @param context FacesContext
|
|
* @param component UIComponent to generate JavaScript for
|
|
* @param fieldId Hidden field id to set value for
|
|
* @param fieldValue Hidden field value to set hidden field too on submit
|
|
*
|
|
* @return JavaScript event code
|
|
*/
|
|
public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId, String fieldValue)
|
|
{
|
|
return generateFormSubmit(context, component, fieldId, fieldValue, false, null);
|
|
}
|
|
|
|
/**
|
|
* Generate the JavaScript to submit set the specified hidden Form field to the
|
|
* supplied value and submit the parent Form.
|
|
*
|
|
* NOTE: the supplied hidden field name is added to the Form Renderer map for output.
|
|
*
|
|
* @param context FacesContext
|
|
* @param component UIComponent to generate JavaScript for
|
|
* @param fieldId Hidden field id to set value for
|
|
* @param fieldValue Hidden field value to set hidden field too on submit
|
|
* @param params Optional map of param name/values to output
|
|
*
|
|
* @return JavaScript event code
|
|
*/
|
|
public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId,
|
|
String fieldValue, Map<String, String> params)
|
|
{
|
|
return generateFormSubmit(context, component, fieldId, fieldValue, false, params);
|
|
}
|
|
|
|
/**
|
|
* Generate the JavaScript to submit set the specified hidden Form field to the
|
|
* supplied value and submit the parent Form.
|
|
*
|
|
* NOTE: the supplied hidden field name is added to the Form Renderer map for output.
|
|
*
|
|
* @param context FacesContext
|
|
* @param component UIComponent to generate JavaScript for
|
|
* @param fieldId Hidden field id to set value for
|
|
* @param fieldValue Hidden field value to set hidden field too on submit
|
|
* @param valueIsParam Determines whether the fieldValue parameter should be treated
|
|
* as a parameter in the generated JavaScript, false will treat
|
|
* the value i.e. surround it with single quotes
|
|
* @param params Optional map of param name/values to output
|
|
*
|
|
* @return JavaScript event code
|
|
*/
|
|
public static String generateFormSubmit(FacesContext context, UIComponent component, String fieldId,
|
|
String fieldValue, boolean valueIsParam, Map<String, String> params)
|
|
{
|
|
UIForm form = Utils.getParentForm(context, component);
|
|
if (form == null)
|
|
{
|
|
throw new IllegalStateException("Must nest components inside UIForm to generate form submit!");
|
|
}
|
|
|
|
String formClientId = form.getClientId(context);
|
|
|
|
StringBuilder buf = new StringBuilder(200);
|
|
buf.append("document.forms['");
|
|
buf.append(formClientId);
|
|
buf.append("']['");
|
|
buf.append(fieldId);
|
|
buf.append("'].value=");
|
|
if (valueIsParam == false)
|
|
{
|
|
buf.append("'");
|
|
}
|
|
buf.append(fieldValue);
|
|
if (valueIsParam == false)
|
|
{
|
|
buf.append("'");
|
|
}
|
|
buf.append(";");
|
|
|
|
if (params != null)
|
|
{
|
|
for (String name : params.keySet())
|
|
{
|
|
buf.append("document.forms['");
|
|
buf.append(formClientId);
|
|
buf.append("']['");
|
|
buf.append(name);
|
|
buf.append("'].value='");
|
|
buf.append(replace(params.get(name), "'", "\\'"));
|
|
buf.append("';");
|
|
|
|
// weak, but this seems to be the way Sun RI do it...
|
|
//FormRenderer.addNeededHiddenField(context, name);
|
|
HtmlFormRendererBase.addHiddenCommandParameter(context, form, name);
|
|
}
|
|
}
|
|
|
|
buf.append("document.forms['");
|
|
buf.append(formClientId);
|
|
buf.append("'].submit();");
|
|
|
|
if (valueIsParam == false)
|
|
{
|
|
buf.append("return false;");
|
|
}
|
|
|
|
// weak, but this seems to be the way Sun RI do it...
|
|
//FormRenderer.addNeededHiddenField(context, fieldId);
|
|
HtmlFormRendererBase.addHiddenCommandParameter(context, form, fieldId);
|
|
|
|
return buf.toString();
|
|
}
|
|
|
|
/**
|
|
* Generate the JavaScript to submit the parent Form.
|
|
*
|
|
* @param context FacesContext
|
|
* @param component UIComponent to generate JavaScript for
|
|
*
|
|
* @return JavaScript event code
|
|
*/
|
|
public static String generateFormSubmit(FacesContext context, UIComponent component)
|
|
{
|
|
UIForm form = Utils.getParentForm(context, component);
|
|
if (form == null)
|
|
{
|
|
throw new IllegalStateException("Must nest components inside UIForm to generate form submit!");
|
|
}
|
|
|
|
String formClientId = form.getClientId(context);
|
|
|
|
StringBuilder buf = new StringBuilder(48);
|
|
|
|
buf.append("document.forms['");
|
|
buf.append(formClientId);
|
|
buf.append("'].submit()");
|
|
|
|
buf.append(";return false;");
|
|
|
|
return buf.toString();
|
|
}
|
|
|
|
/**
|
|
* Enum representing the client URL type to generate
|
|
*/
|
|
public enum URLMode {HTTP_DOWNLOAD, HTTP_INLINE, WEBDAV, CIFS, SHOW_DETAILS, BROWSE, FTP}
|
|
|
|
/**
|
|
* Generates a URL for the given usage for the given node.
|
|
*
|
|
* The supported values for the usage parameter are of URLMode enum type
|
|
* @see URLMode
|
|
*
|
|
* @param context Faces context
|
|
* @param node The node to generate the URL for
|
|
* @param name Name to use for the download file part of the link if any
|
|
* @param usage What the URL is going to be used for
|
|
*
|
|
* @return The URL for the requested usage without the context path
|
|
*/
|
|
public static String generateURL(FacesContext context, Node node, String name, URLMode usage)
|
|
{
|
|
String url = null;
|
|
|
|
switch (usage)
|
|
{
|
|
case WEBDAV:
|
|
{
|
|
// calculate a WebDAV URL for the given node
|
|
FileFolderService fileFolderService = Repository.getServiceRegistry(
|
|
context).getFileFolderService();
|
|
try
|
|
{
|
|
List<FileInfo> paths = fileFolderService.getNamePath(null, node.getNodeRef());
|
|
|
|
// build up the webdav url
|
|
StringBuilder path = new StringBuilder("/").append(WebDAVServlet.WEBDAV_PREFIX);
|
|
|
|
// build up the path skipping the first path as it is the root folder
|
|
for (int x = 1; x < paths.size(); x++)
|
|
{
|
|
path.append("/").append(URLEncoder.encode(paths.get(x).getName()));
|
|
}
|
|
url = path.toString();
|
|
}
|
|
catch (AccessDeniedException e)
|
|
{
|
|
// cannot build path if user don't have access all the way up
|
|
}
|
|
catch (FileNotFoundException nodeErr)
|
|
{
|
|
// cannot build path if file no longer exists
|
|
}
|
|
break;
|
|
}
|
|
|
|
case CIFS:
|
|
{
|
|
// calculate a CIFS path for the given node
|
|
|
|
// get hold of the node service, cifsServer and navigation bean
|
|
NodeService nodeService = Repository.getServiceRegistry(context).getNodeService();
|
|
NavigationBean navBean = (NavigationBean)context.getExternalContext().
|
|
getSessionMap().get(NavigationBean.BEAN_NAME);
|
|
CIFSServerBean cifsServer = (CIFSServerBean)FacesContextUtils.getRequiredWebApplicationContext(
|
|
context).getBean("cifsServer");
|
|
|
|
if (nodeService != null && navBean != null && cifsServer != null)
|
|
{
|
|
// Resolve CIFS network folder location for this node
|
|
|
|
FilesystemsConfigSection filesysConfig = (FilesystemsConfigSection) cifsServer.getConfiguration().getConfigSection(FilesystemsConfigSection.SectionName);
|
|
DiskSharedDevice diskShare = null;
|
|
|
|
SharedDeviceList shares = filesysConfig.getShares();
|
|
Enumeration<SharedDevice> shareEnum = shares.enumerateShares();
|
|
|
|
while ( shareEnum.hasMoreElements() && diskShare == null) {
|
|
SharedDevice curShare = shareEnum.nextElement();
|
|
if ( curShare.getContext() instanceof ContentContext)
|
|
diskShare = (DiskSharedDevice) curShare;
|
|
}
|
|
|
|
|
|
if (diskShare != null)
|
|
{
|
|
ContentContext contentCtx = (ContentContext) diskShare.getContext();
|
|
NodeRef rootNode = contentCtx.getRootNode();
|
|
try
|
|
{
|
|
url = Repository.getNamePath(nodeService, node.getNodePath(), rootNode, "\\",
|
|
"file:///" + navBean.getCIFSServerPath(diskShare));
|
|
}
|
|
catch (AccessDeniedException e)
|
|
{
|
|
// cannot build path if user don't have access all the way up
|
|
}
|
|
catch (InvalidNodeRefException nodeErr)
|
|
{
|
|
// cannot build path if node no longer exists
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HTTP_DOWNLOAD:
|
|
{
|
|
url = DownloadContentServlet.generateDownloadURL(node.getNodeRef(), name);
|
|
break;
|
|
}
|
|
|
|
case HTTP_INLINE:
|
|
{
|
|
url = DownloadContentServlet.generateBrowserURL(node.getNodeRef(), name);
|
|
break;
|
|
}
|
|
|
|
case SHOW_DETAILS:
|
|
{
|
|
DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService();
|
|
|
|
// default to showing details of content
|
|
String outcome = ExternalAccessServlet.OUTCOME_DOCDETAILS;
|
|
|
|
// if the node is a type of folder then make the outcome to show space details
|
|
if ((dd.isSubClass(node.getType(), ContentModel.TYPE_FOLDER)) ||
|
|
(dd.isSubClass(node.getType(), ApplicationModel.TYPE_FOLDERLINK)))
|
|
{
|
|
outcome = ExternalAccessServlet.OUTCOME_SPACEDETAILS;
|
|
}
|
|
|
|
// build the url
|
|
url = ExternalAccessServlet.generateExternalURL(outcome,
|
|
Repository.getStoreRef().getProtocol() + "/" +
|
|
Repository.getStoreRef().getIdentifier() + "/" + node.getId());
|
|
break;
|
|
}
|
|
|
|
case BROWSE:
|
|
{
|
|
url = ExternalAccessServlet.generateExternalURL(ExternalAccessServlet.OUTCOME_BROWSE,
|
|
Repository.getStoreRef().getProtocol() + "/" +
|
|
Repository.getStoreRef().getIdentifier() + "/" + node.getId());
|
|
}
|
|
|
|
case FTP:
|
|
{
|
|
// not implemented yet!
|
|
break;
|
|
}
|
|
}
|
|
|
|
return url;
|
|
}
|
|
|
|
/**
|
|
* Generates a URL for the given usage for the given node.
|
|
*
|
|
* The supported values for the usage parameter are of URLMode enum type
|
|
* @see URLMode
|
|
*
|
|
* @param context Faces context
|
|
* @param node The node to generate the URL for
|
|
* @param usage What the URL is going to be used for
|
|
*
|
|
* @return The URL for the requested usage without the context path
|
|
*/
|
|
public static String generateURL(FacesContext context, Node node, URLMode usage)
|
|
{
|
|
return generateURL(context, node, node.getName(), usage);
|
|
}
|
|
|
|
/**
|
|
* Build a context path safe image tag for the supplied image path.
|
|
* Image path should be supplied with a leading slash '/'.
|
|
*
|
|
* @param context FacesContext
|
|
* @param image The local image path from the web folder with leading slash '/'
|
|
* @param width Width in pixels
|
|
* @param height Height in pixels
|
|
* @param alt Optional alt/title text
|
|
* @param onclick JavaScript onclick event handler code
|
|
*
|
|
* @return Populated <code>img</code> tag
|
|
*/
|
|
public static String buildImageTag(FacesContext context, String image, int width, int height,
|
|
String alt, String onclick)
|
|
{
|
|
return buildImageTag(context, image, width, height, alt, onclick, null);
|
|
}
|
|
|
|
/**
|
|
* Build a context path safe image tag for the supplied image path.
|
|
* Image path should be supplied with a leading slash '/'.
|
|
*
|
|
* @param context FacesContext
|
|
* @param image The local image path from the web folder with leading slash '/'
|
|
* @param width Width in pixels
|
|
* @param height Height in pixels
|
|
* @param alt Optional alt/title text
|
|
* @param onclick JavaScript onclick event handler code
|
|
* @param verticalAlign Optional HTML alignment value
|
|
*
|
|
* @return Populated <code>img</code> tag
|
|
*/
|
|
public static String buildImageTag(FacesContext context, String image, int width, int height,
|
|
String alt, String onclick, String verticalAlign)
|
|
{
|
|
StringBuilder buf = new StringBuilder(200);
|
|
|
|
String style = "border-width:0px;";
|
|
buf.append("<img src='")
|
|
.append(context.getExternalContext().getRequestContextPath())
|
|
.append(image)
|
|
.append("' width='")
|
|
.append(width)
|
|
.append("' height='")
|
|
.append(height)
|
|
.append("'");
|
|
|
|
if (alt != null)
|
|
{
|
|
alt = Utils.encode(alt);
|
|
buf.append(" alt='")
|
|
.append(alt)
|
|
.append("' title='")
|
|
.append(alt)
|
|
.append("'");
|
|
}
|
|
else
|
|
{
|
|
buf.append(" alt=''");
|
|
}
|
|
|
|
if (verticalAlign != null)
|
|
{
|
|
StringBuilder styleBuf = new StringBuilder(40);
|
|
styleBuf.append(style).append("vertical-align:").append(verticalAlign).append(";");
|
|
style = styleBuf.toString();
|
|
}
|
|
|
|
if (onclick != null)
|
|
{
|
|
buf.append(" onclick=\"").append(onclick).append('"');
|
|
StringBuilder styleBuf = new StringBuilder(style.length() + 16);
|
|
styleBuf.append(style).append("cursor:pointer;");
|
|
style = styleBuf.toString();
|
|
}
|
|
buf.append(" style='").append(style).append("'/>");
|
|
|
|
return buf.toString();
|
|
}
|
|
|
|
/**
|
|
* Build a context path safe image tag for the supplied image path.
|
|
* Image path should be supplied with a leading slash '/'.
|
|
*
|
|
* @param context FacesContext
|
|
* @param image The local image path from the web folder with leading slash '/'
|
|
* @param width Width in pixels
|
|
* @param height Height in pixels
|
|
* @param alt Optional alt/title text
|
|
*
|
|
* @return Populated <code>img</code> tag
|
|
*/
|
|
public static String buildImageTag(FacesContext context, String image, int width, int height, String alt)
|
|
{
|
|
return buildImageTag(context, image, width, height, alt, null);
|
|
}
|
|
|
|
/**
|
|
* Build a context path safe image tag for the supplied image path.
|
|
* Image path should be supplied with a leading slash '/'.
|
|
*
|
|
* @param context FacesContext
|
|
* @param image The local image path from the web folder with leading slash '/'
|
|
* @param alt Optional alt/title text
|
|
*
|
|
* @return Populated <code>img</code> tag
|
|
*/
|
|
public static String buildImageTag(FacesContext context, String image, String alt)
|
|
{
|
|
return buildImageTag(context, image, alt, null);
|
|
}
|
|
|
|
/**
|
|
* Build a context path safe image tag for the supplied image path.
|
|
* Image path should be supplied with a leading slash '/'.
|
|
*
|
|
* @param context FacesContext
|
|
* @param image The local image path from the web folder with leading slash '/'
|
|
* @param alt Optional alt/title text
|
|
* @param verticalAlign Optional HTML alignment value
|
|
*
|
|
* @return Populated <code>img</code> tag
|
|
*/
|
|
public static String buildImageTag(FacesContext context, String image, String alt, String verticalAlign)
|
|
{
|
|
StringBuilder buf = new StringBuilder(128);
|
|
buf.append("<img src='")
|
|
.append(context.getExternalContext().getRequestContextPath())
|
|
.append(image)
|
|
.append("' ");
|
|
|
|
String style = "border-width:0px;";
|
|
if (alt != null)
|
|
{
|
|
alt = Utils.encode(alt);
|
|
buf.append(" alt='")
|
|
.append(alt)
|
|
.append("' title='")
|
|
.append(alt)
|
|
.append("'");
|
|
}
|
|
else
|
|
{
|
|
buf.append(" alt=''");
|
|
}
|
|
|
|
if (verticalAlign != null)
|
|
{
|
|
StringBuilder styleBuf = new StringBuilder(40);
|
|
styleBuf.append(style).append("vertical-align:").append(verticalAlign).append(";");
|
|
style = styleBuf.toString();
|
|
}
|
|
|
|
buf.append(" style='").append(style).append("'/>");
|
|
|
|
return buf.toString();
|
|
}
|
|
|
|
/**
|
|
* Return the parent UIForm component for the specified UIComponent
|
|
*
|
|
* @param context FaceContext
|
|
* @param component The UIComponent to find parent Form for
|
|
*
|
|
* @return UIForm parent or null if none found in hiearachy
|
|
*/
|
|
public static UIForm getParentForm(FacesContext context, UIComponent component)
|
|
{
|
|
UIComponent parent = component.getParent();
|
|
while (parent != null)
|
|
{
|
|
if (parent instanceof UIForm)
|
|
{
|
|
break;
|
|
}
|
|
parent = parent.getParent();
|
|
}
|
|
return (UIForm)parent;
|
|
}
|
|
|
|
/**
|
|
* Return the parent UIComponent implementing the NamingContainer interface for
|
|
* the specified UIComponent.
|
|
*
|
|
* @param context FaceContext
|
|
* @param component The UIComponent to find parent Form for
|
|
*
|
|
* @return NamingContainer parent or null if none found in hiearachy
|
|
*/
|
|
public static UIComponent getParentNamingContainer(FacesContext context, UIComponent component)
|
|
{
|
|
UIComponent parent = component.getParent();
|
|
while (parent != null)
|
|
{
|
|
if (parent instanceof NamingContainer)
|
|
{
|
|
break;
|
|
}
|
|
parent = parent.getParent();
|
|
}
|
|
return (UIComponent)parent;
|
|
}
|
|
|
|
/**
|
|
* Return the parent UIComponent implementing the IDataContainer interface for
|
|
* the specified UIComponent.
|
|
*
|
|
* @param context FaceContext
|
|
* @param component The UIComponent to find parent IDataContainer for
|
|
*
|
|
* @return IDataContainer parent or null if none found in hiearachy
|
|
*/
|
|
public static IDataContainer getParentDataContainer(FacesContext context, UIComponent component)
|
|
{
|
|
UIComponent parent = component.getParent();
|
|
while (parent != null)
|
|
{
|
|
if (parent instanceof IDataContainer)
|
|
{
|
|
break;
|
|
}
|
|
parent = parent.getParent();
|
|
}
|
|
return (IDataContainer)parent;
|
|
}
|
|
|
|
/**
|
|
* Determines whether the given component is disabled or readonly
|
|
*
|
|
* @param component The component to test
|
|
* @return true if the component is either disabled or set to readonly
|
|
*/
|
|
public static boolean isComponentDisabledOrReadOnly(UIComponent component)
|
|
{
|
|
boolean disabled = false;
|
|
boolean readOnly = false;
|
|
|
|
Object disabledAttr = component.getAttributes().get("disabled");
|
|
if (disabledAttr != null)
|
|
{
|
|
disabled = disabledAttr.equals(Boolean.TRUE);
|
|
}
|
|
|
|
if (disabled == false)
|
|
{
|
|
Object readOnlyAttr = component.getAttributes().get("readonly");
|
|
if (readOnlyAttr != null)
|
|
{
|
|
readOnly = readOnlyAttr.equals(Boolean.TRUE);
|
|
}
|
|
}
|
|
|
|
return disabled || readOnly;
|
|
}
|
|
|
|
/**
|
|
* Invoke the method encapsulated by the supplied MethodBinding
|
|
*
|
|
* @param context FacesContext
|
|
* @param method MethodBinding to invoke
|
|
* @param event ActionEvent to pass to the method of signature:
|
|
* public void myMethodName(ActionEvent event)
|
|
*/
|
|
public static void processActionMethod(FacesContext context, MethodBinding method, ActionEvent event)
|
|
{
|
|
try
|
|
{
|
|
method.invoke(context, new Object[] {event});
|
|
}
|
|
catch (EvaluationException e)
|
|
{
|
|
Throwable cause = e.getCause();
|
|
if (cause instanceof AbortProcessingException)
|
|
{
|
|
throw (AbortProcessingException)cause;
|
|
}
|
|
else
|
|
{
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a global error message
|
|
*
|
|
* @param msg The error message
|
|
*/
|
|
public static void addErrorMessage(String msg)
|
|
{
|
|
addErrorMessage(msg, null);
|
|
}
|
|
|
|
/**
|
|
* Adds a global error message and logs exception details
|
|
*
|
|
* @param msg The error message
|
|
* @param err The exception to log
|
|
*/
|
|
public static void addErrorMessage(String msg, Throwable err)
|
|
{
|
|
FacesContext context = FacesContext.getCurrentInstance( );
|
|
FacesMessage facesMsg = new FacesMessage(FacesMessage.SEVERITY_ERROR, msg, msg);
|
|
context.addMessage(null, facesMsg);
|
|
if (err != null)
|
|
{
|
|
if ((err instanceof InvalidNodeRefException == false &&
|
|
err instanceof AccessDeniedException == false &&
|
|
err instanceof NoTransformerException == false) || logger.isDebugEnabled())
|
|
{
|
|
logger.error(msg, err);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds a global status message that will be displayed by a Status Message UI component
|
|
*
|
|
* @param severity Severity of the message
|
|
* @param msg Text of the message
|
|
*/
|
|
public static void addStatusMessage(FacesMessage.Severity severity, String msg)
|
|
{
|
|
FacesContext fc = FacesContext.getCurrentInstance();
|
|
String time = getTimeFormat(fc).format(new Date(System.currentTimeMillis()));
|
|
FacesMessage fm = new FacesMessage(severity, time, msg);
|
|
fc.addMessage(UIStatusMessage.STATUS_MESSAGE, fm);
|
|
}
|
|
|
|
/**
|
|
* @return the formatter for locale sensitive Time formatting
|
|
*/
|
|
public static DateFormat getTimeFormat(FacesContext fc)
|
|
{
|
|
return getDateFormatFromPattern(fc, Application.getMessage(fc, MSG_TIME_PATTERN));
|
|
}
|
|
|
|
/**
|
|
* @return the formatter for locale sensitive Date formatting
|
|
*/
|
|
public static DateFormat getDateFormat(FacesContext fc)
|
|
{
|
|
return getDateFormatFromPattern(fc, Application.getMessage(fc, MSG_DATE_PATTERN));
|
|
}
|
|
|
|
/**
|
|
* @return the formatter for locale sensitive Date & Time formatting
|
|
*/
|
|
public static DateFormat getDateTimeFormat(FacesContext fc)
|
|
{
|
|
return getDateFormatFromPattern(fc, Application.getMessage(fc, MSG_DATE_TIME_PATTERN));
|
|
}
|
|
|
|
/**
|
|
* @return DataFormat object for the specified pattern
|
|
*/
|
|
private static DateFormat getDateFormatFromPattern(FacesContext fc, String pattern)
|
|
{
|
|
if (pattern == null)
|
|
{
|
|
throw new IllegalArgumentException("DateTime pattern is mandatory.");
|
|
}
|
|
try
|
|
{
|
|
return new SimpleDateFormat(pattern, Application.getLanguage(fc));
|
|
}
|
|
catch (IllegalArgumentException err)
|
|
{
|
|
throw new AlfrescoRuntimeException("Invalid DateTime pattern", err);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Parse XML format date YYYY-MM-DDTHH:MM:SS
|
|
* @param isoDate
|
|
* @return Date or null if failed to parse
|
|
*/
|
|
public static Date parseXMLDateFormat(String isoDate)
|
|
{
|
|
Date parsed = null;
|
|
|
|
try
|
|
{
|
|
int offset = 0;
|
|
|
|
// extract year
|
|
int year = Integer.parseInt(isoDate.substring(offset, offset += 4));
|
|
if (isoDate.charAt(offset) != '-')
|
|
{
|
|
throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset));
|
|
}
|
|
|
|
// extract month
|
|
int month = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
|
|
if (isoDate.charAt(offset) != '-')
|
|
{
|
|
throw new IndexOutOfBoundsException("Expected - character but found " + isoDate.charAt(offset));
|
|
}
|
|
|
|
// extract day
|
|
int day = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
|
|
if (isoDate.charAt(offset) != 'T')
|
|
{
|
|
throw new IndexOutOfBoundsException("Expected T character but found " + isoDate.charAt(offset));
|
|
}
|
|
|
|
// extract hours, minutes, seconds and milliseconds
|
|
int hour = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
|
|
if (isoDate.charAt(offset) != ':')
|
|
{
|
|
throw new IndexOutOfBoundsException("Expected : character but found " + isoDate.charAt(offset));
|
|
}
|
|
int minutes = Integer.parseInt(isoDate.substring(offset += 1, offset += 2));
|
|
if (isoDate.charAt(offset) != ':')
|
|
{
|
|
throw new IndexOutOfBoundsException("Expected : character but found " + isoDate.charAt(offset));
|
|
}
|
|
int seconds = Integer.parseInt(isoDate.substring(offset += 1 , offset += 2));
|
|
|
|
// initialize Calendar object
|
|
Calendar calendar = Calendar.getInstance();
|
|
calendar.setLenient(false);
|
|
calendar.set(Calendar.YEAR, year);
|
|
calendar.set(Calendar.MONTH, month - 1);
|
|
calendar.set(Calendar.DAY_OF_MONTH, day);
|
|
calendar.set(Calendar.HOUR_OF_DAY, hour);
|
|
calendar.set(Calendar.MINUTE, minutes);
|
|
calendar.set(Calendar.SECOND, seconds);
|
|
|
|
// extract the date
|
|
parsed = calendar.getTime();
|
|
}
|
|
catch(IndexOutOfBoundsException e)
|
|
{
|
|
}
|
|
catch(NumberFormatException e)
|
|
{
|
|
}
|
|
catch(IllegalArgumentException e)
|
|
{
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
* Given a ConfigElement instance retrieve the display label, this could be
|
|
* dervied from a message bundle key or a literal string
|
|
*
|
|
* @param context FacesContext
|
|
* @param configElement The ConfigElement to test
|
|
* @return The resolved display label
|
|
*/
|
|
public static String getDisplayLabel(FacesContext context, ConfigElement configElement)
|
|
{
|
|
String label = null;
|
|
|
|
// look for a localized string
|
|
String msgId = configElement.getAttribute("display-label-id");
|
|
if (msgId != null)
|
|
{
|
|
label = Application.getMessage(context, msgId);
|
|
}
|
|
|
|
// if there wasn't an externalized string look for a literal string
|
|
if (label == null)
|
|
{
|
|
label = configElement.getAttribute("display-label");
|
|
}
|
|
|
|
return label;
|
|
}
|
|
|
|
/**
|
|
* Given a ConfigElement instance retrieve the description, this could be
|
|
* dervied from a message bundle key or a literal string
|
|
*
|
|
* @param context FacesContext
|
|
* @param configElement The ConfigElement to test
|
|
* @return The resolved description
|
|
*/
|
|
public static String getDescription(FacesContext context, ConfigElement configElement)
|
|
{
|
|
String description = null;
|
|
|
|
// look for a localized string
|
|
String msgId = configElement.getAttribute("description-id");
|
|
if (msgId != null)
|
|
{
|
|
description = Application.getMessage(context, msgId);
|
|
}
|
|
|
|
// if there wasn't an externalized string look for a literal string
|
|
if (description == null)
|
|
{
|
|
description = configElement.getAttribute("description");
|
|
}
|
|
|
|
return description;
|
|
}
|
|
}
|