. Added cm:name based path support to the UITemplate component

- this component is generally used to render FreeMarker templates directly to the page - particularly useful for Dashlet pages
 - now supports an additional attribute 'templatePath' which should be set to a cm:name based path to a template for rendering
 - For example the simplest portable dashlet JSP page to render a template would look like this:

<%@ taglib uri="/WEB-INF/repo.tld" prefix="r" %>
<r:template templatePath="/Company Home/Data Dictionary/Presentation Templates/my_docs.ftl" />

 - Fixes http://issues.alfresco.com/browse/AWC-1091
. Additional helper override for resolving cm:name based webdav style paths to a NodeRef
. ClipboardItem interface javadoc and removal of obsolete methods from implementing classes

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@4963 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2007-01-30 12:06:19 +00:00
parent fc074b48f8
commit 88512cf711
7 changed files with 191 additions and 70 deletions

View File

@@ -208,6 +208,19 @@ public abstract class BaseServlet extends HttpServlet
return resolveWebDAVPath(wc, args, true);
}
/**
* Resolves the given path elements to a NodeRef in the current repository
*
* @param context Faces context
* @param args The elements of the path to lookup
* @param decode True to decode the arg from UTF-8 format, false for no decoding
*/
public static NodeRef resolveWebDAVPath(FacesContext context, String[] args, boolean decode)
{
WebApplicationContext wc = FacesContextUtils.getRequiredWebApplicationContext(context);
return resolveWebDAVPath(wc, args, decode);
}
/**
* Resolves the given path elements to a NodeRef in the current repository
*

View File

@@ -103,11 +103,6 @@ abstract class AbstractClipboardItem implements ClipboardItem
return this.icon;
}
public String getId()
{
return this.ref.getId();
}
public NodeRef getNodeRef()
{
return this.ref;

View File

@@ -28,21 +28,51 @@ import org.alfresco.service.namespace.QName;
*/
public interface ClipboardItem
{
/**
* @return the mode status of the clipboard item, the enum can be either Cut or Copy
*/
public ClipboardStatus getMode();
/**
* @return display label (cm:name) of the clipboard item
*/
public String getName();
/**
* @return QName type of the clipboard item
*/
public QName getType();
/**
* @return the app:icon property of the clipboard item
*/
public String getIcon();
public String getId();
/**
* @return the NodeRef of the clipboard item
*/
public NodeRef getNodeRef();
/**
* @return true if the item on the clipboard supports linking (.url) as a link type
*/
public boolean supportsLink();
/**
* @param viewId JSF View Id to check against
*
* @return true if the clipboard item can be pasted to the specified JSF view
*/
public boolean canPasteToViewId(String viewId);
/**
* @param fc FacesContext
* @param viewId JSF View Id to paste into
* @param action Clipboard action constant (@see org.alfresco.web.ui.repo.component.shelf.UIClipboardShelfItem)
*
* @return true on successful paste, false otherwise
*
* @throws Throwable on fatal error during paste
*/
public boolean paste(FacesContext fc, String viewId, int action) throws Throwable;
}

View File

@@ -127,7 +127,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem
{
// LINK operation
if (logger.isDebugEnabled())
logger.debug("Attempting to link node ID: " + getId() + " into node ID: " + destRef.getId());
logger.debug("Attempting to link node ID: " + getNodeRef() + " into node: " + destRef.toString());
// we create a special Link Object node that has a property to reference the original
// create the node using the nodeService (can only use FileFolderService for content)
@@ -178,7 +178,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem
{
// COPY operation
if (logger.isDebugEnabled())
logger.debug("Attempting to copy node ID: " + getId() + " into node ID: " + destRef.getId());
logger.debug("Attempting to copy node: " + getNodeRef() + " into node ID: " + destRef.toString());
if (dd.isSubClass(getType(), ContentModel.TYPE_CONTENT) ||
dd.isSubClass(getType(), ContentModel.TYPE_FOLDER))
@@ -218,7 +218,7 @@ public class WorkspaceClipboardItem extends AbstractClipboardItem
{
// MOVE operation
if (logger.isDebugEnabled())
logger.debug("Attempting to move node ID: " + getId() + " into node ID: " + destRef.getId());
logger.debug("Attempting to move node: " + getNodeRef() + " into node ID: " + destRef.toString());
if (dd.isSubClass(getType(), ContentModel.TYPE_CONTENT) ||
dd.isSubClass(getType(), ContentModel.TYPE_FOLDER))

View File

@@ -18,6 +18,7 @@ package org.alfresco.web.ui.repo.component.template;
import java.io.IOException;
import java.util.Map;
import java.util.StringTokenizer;
import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding;
@@ -29,6 +30,7 @@ import org.alfresco.service.cmr.repository.TemplateException;
import org.alfresco.service.cmr.repository.TemplateImageResolver;
import org.alfresco.service.cmr.repository.TemplateService;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.BaseServlet;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.repository.User;
import org.alfresco.web.ui.common.Utils;
@@ -36,21 +38,30 @@ import org.alfresco.web.ui.common.component.SelfRenderingComponent;
import org.apache.log4j.Logger;
/**
* Component responsible for rendering the output of a FreeMarker template directly to the page.
* <p>
* FreeMarker templates can be specified as a NodeRef or classpath location. The template output
* will be processed against the default model merged with any custom model reference supplied to
* the component as a value binding attribute. The output of the template is the output of the
* component tag.
*
* @author Kevin Roast
*/
public class UITemplate extends SelfRenderingComponent
{
private final static String ENGINE_DEFAULT = "freemarker";
private final static String TEMPLATE_KEY = "_template_";
private static Logger logger = Logger.getLogger(UITemplate.class);
/** Template engine name */
private String engine = null;
/** Template name/path */
/** Template name/classpath */
private String template = null;
/** Template cm:name based path */
private String templatePath = null;
/** Data model reference */
private Object model = null;
@@ -76,7 +87,8 @@ public class UITemplate extends SelfRenderingComponent
super.restoreState(context, values[0]);
this.engine = (String)values[1];
this.template = (String)values[2];
this.model = (Object)values[3];
this.templatePath = (String)values[3];
this.model = (Object)values[4];
}
/**
@@ -84,12 +96,8 @@ public class UITemplate extends SelfRenderingComponent
*/
public Object saveState(FacesContext context)
{
Object values[] = new Object[4];
// standard component attributes are saved by the super class
values[0] = super.saveState(context);
values[1] = this.engine;
values[2] = this.template;
values[3] = this.model;
Object values[] = new Object[] {
super.saveState(context), this.engine, this.template, this.templatePath, this.model};
return (values);
}
@@ -104,8 +112,30 @@ public class UITemplate extends SelfRenderingComponent
}
// get the template to process
String template = getTemplate();
if (template != null && template.length() != 0)
String templateRef = getTemplate();
if (templateRef == null || templateRef.length() == 0)
{
// no noderef/classpath template found - try a name based path
String path = getTemplatePath();
if (path != null && path.length() != 0)
{
// convert cm:name based path to a NodeRef
StringTokenizer t = new StringTokenizer(path, "/");
int tokenCount = t.countTokens();
String[] elements = new String[tokenCount];
for (int i=0; i<tokenCount; i++)
{
elements[i] = t.nextToken();
}
NodeRef pathRef = BaseServlet.resolveWebDAVPath(context, elements, false);
if (pathRef != null)
{
templateRef = pathRef.toString();
}
}
}
if (templateRef != null && templateRef.length() != 0)
{
// get the class name of the processor to instantiate
String engine = getEngine();
@@ -117,14 +147,14 @@ public class UITemplate extends SelfRenderingComponent
startTime = System.currentTimeMillis();
}
// get the data model to use - building default if required
Object model = getModel();
// get the data model to use - building default FreeMarker model as required
Object model = getFreeMarkerModel(getModel(), templateRef);
// process the template against the model
try
{
TemplateService templateService = Repository.getServiceRegistry(context).getTemplateService();
templateService.processTemplate(engine, getTemplate(), model, context.getResponseWriter());
templateService.processTemplate(engine, templateRef, model, context.getResponseWriter());
}
catch (TemplateException err)
{
@@ -139,6 +169,49 @@ public class UITemplate extends SelfRenderingComponent
}
}
/**
* By default we return a Map model containing root references to the Company Home Space,
* the users Home Space and the Person Node for the current user.
*
* @param model Custom model to merge into default model
* @param template Optional reference to the template to add to model
*
* @return Returns the data model to bind template against.
*/
private Object getFreeMarkerModel(Object model, String template)
{
if (getEngine().equals(ENGINE_DEFAULT))
{
// create an instance of the default FreeMarker template object model
FacesContext fc = FacesContext.getCurrentInstance();
ServiceRegistry services = Repository.getServiceRegistry(fc);
User user = Application.getCurrentUser(fc);
// add the template itself to the model
NodeRef templateRef = null;
if (template.indexOf(StoreRef.URI_FILLER) != -1)
{
// found a noderef template
templateRef = new NodeRef(template);
}
Map root = DefaultModelHelper.buildDefaultModel(services, user, templateRef);
// merge models
if (model instanceof Map)
{
if (logger.isDebugEnabled())
logger.debug("Found valid Map model to merge with FreeMarker: " + model);
root.putAll((Map)model);
}
model = root;
}
return model;
}
// ------------------------------------------------------------------------------
// Strongly typed component property accessors
@@ -166,61 +239,23 @@ public class UITemplate extends SelfRenderingComponent
}
/**
* Return the data model to bind template against.
* <p>
* By default we return a Map model containing root references to the Company Home Space,
* the users Home Space and the Person Node for the current user.
* Return the custom data model to bind template against.
*
* @return Returns the data model to bind template against.
* @return Returns the custom data model to bind template against.
*/
public Object getModel()
{
if (this.model == null)
{
Object model = null;
ValueBinding vb = getValueBinding("model");
if (vb != null)
{
model = vb.getValue(getFacesContext());
this.model = vb.getValue(getFacesContext());
}
if (getEngine().equals(ENGINE_DEFAULT))
{
// create an instance of the default FreeMarker template object model
FacesContext fc = FacesContext.getCurrentInstance();
ServiceRegistry services = Repository.getServiceRegistry(fc);
User user = Application.getCurrentUser(fc);
// add the template itself to the model
NodeRef templateRef = null;
if (getTemplate().indexOf(StoreRef.URI_FILLER) != -1)
{
// found a noderef template
templateRef = new NodeRef(getTemplate());
}
Map root = DefaultModelHelper.buildDefaultModel(services, user, templateRef);
// merge models
if (model instanceof Map)
{
if (logger.isDebugEnabled())
logger.debug("Found valid Map model to merge with FreeMarker: " + model);
root.putAll((Map)model);
}
model = root;
}
return model;
}
else
{
return this.model;
}
return this.model;
}
/**
* @param model The model to set.
*/
@@ -230,7 +265,7 @@ public class UITemplate extends SelfRenderingComponent
}
/**
* @return Returns the template name.
* @return Returns the template NodeRef/classpath.
*/
public String getTemplate()
{
@@ -249,13 +284,40 @@ public class UITemplate extends SelfRenderingComponent
}
/**
* @param template The template name to set.
* @param template The template NodeRef/classpath to set.
*/
public void setTemplate(String template)
{
this.template = template;
}
/**
* @return Returns the template path.
*/
public String getTemplatePath()
{
ValueBinding vb = getValueBinding("templatePath");
if (vb != null)
{
String val = (String)vb.getValue(getFacesContext());
if (val != null)
{
this.templatePath = val.toString();
}
}
return this.templatePath;
}
/**
* @param templatePath The template cm:name based path to set.
*/
public void setTemplatePath(String templatePath)
{
this.templatePath = templatePath;
}
/** Template Image resolver helper */
private TemplateImageResolver imageResolver = new TemplateImageResolver()
{

View File

@@ -52,6 +52,7 @@ public class TemplateTag extends BaseComponentTag
setStringProperty(component, "engine", this.engine);
setStringProperty(component, "template", this.template);
setStringProperty(component, "templatePath", this.templatePath);
setStringBindingProperty(component, "model", this.model);
}
@@ -64,6 +65,7 @@ public class TemplateTag extends BaseComponentTag
this.engine = null;
this.template = null;
this.templatePath = null;
this.model = null;
}
@@ -86,6 +88,16 @@ public class TemplateTag extends BaseComponentTag
{
this.template = template;
}
/**
* Set the template name based path
*
* @param templatePath the template name based path
*/
public void setTemplatePath(String templatePath)
{
this.templatePath = templatePath;
}
/**
* Set the data model
@@ -98,6 +110,9 @@ public class TemplateTag extends BaseComponentTag
}
/** the template name based path */
private String templatePath;
/** the engine name */
private String engine;

View File

@@ -1292,7 +1292,13 @@
<attribute>
<name>template</name>
<required>true</required>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>templatePath</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>