mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
Moving to root below branch label
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2005 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
@@ -0,0 +1,800 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.EditableValueHolder;
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import javax.faces.event.FacesEvent;
|
||||
import javax.transaction.UserTransaction;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.common.WebResources;
|
||||
|
||||
/**
|
||||
* Abstract component to allow the selection of a hierarchical item
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public abstract class AbstractItemSelector extends UIInput
|
||||
{
|
||||
private static final String MSG_GO_UP = "go_up";
|
||||
private static final String MSG_OK = "ok";
|
||||
private static final String MSG_CANCEL = "cancel";
|
||||
|
||||
private final static String OK_BUTTON = "_ok";
|
||||
protected final static String OPTION = "_option";
|
||||
|
||||
protected final static int MODE_BEFORE_SELECTION = 0;
|
||||
protected final static int MODE_INITIAL_SELECTION = 1;
|
||||
protected final static int MODE_DRILLDOWN_SELECTION = 2;
|
||||
protected final static int MODE_CONFIRM_SELECTION = 3;
|
||||
protected final static int MODE_CANCEL_SELECTION = 4;
|
||||
|
||||
/** label to be displayed before a space is selected */
|
||||
protected String label = null;
|
||||
|
||||
/** cellspacing between options */
|
||||
protected Integer spacing = null;
|
||||
|
||||
/** what mode the component is in */
|
||||
protected int mode = MODE_BEFORE_SELECTION;
|
||||
|
||||
/** currently browsing node id */
|
||||
protected String navigationId = null;
|
||||
|
||||
/** id of the initially selected item, if value is not set */
|
||||
protected String initialSelectionId = null;
|
||||
|
||||
/** Flag to show whether the component is disabled */
|
||||
protected Boolean disabled;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public AbstractItemSelector()
|
||||
{
|
||||
// set the default renderer
|
||||
setRendererType(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public abstract String getFamily();
|
||||
|
||||
/**
|
||||
* Retrieves the default label to show if none has been defined and nothing has been selected
|
||||
*
|
||||
* @return Default label
|
||||
*/
|
||||
public abstract String getDefaultLabel();
|
||||
|
||||
/**
|
||||
* Retrieves the id of the parent node of the current navigation node
|
||||
*
|
||||
* @param context The Faces context
|
||||
* @return Id of the parent node or null if the parent is the root
|
||||
*/
|
||||
public abstract String getParentNodeId(FacesContext context);
|
||||
|
||||
/**
|
||||
* Returns a collection of child associations for the current navigation node
|
||||
*
|
||||
* @param context The Faces context
|
||||
* @return The children
|
||||
*/
|
||||
public abstract Collection<ChildAssociationRef> getChildrenForNode(FacesContext context);
|
||||
|
||||
/**
|
||||
* Returns a collection of child associations of the root
|
||||
*
|
||||
* @param context The Faces context
|
||||
* @return The root options
|
||||
*/
|
||||
public abstract Collection<ChildAssociationRef> getRootChildren(FacesContext context);
|
||||
|
||||
/**
|
||||
* @return The icon image to display next to the item links, or null for no icon
|
||||
*/
|
||||
public abstract String getItemIcon();
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.label = (String)values[1];
|
||||
this.spacing = (Integer)values[2];
|
||||
this.mode = ((Integer)values[3]).intValue();
|
||||
this.navigationId = (String)values[4];
|
||||
this.initialSelectionId = (String)values[5];
|
||||
this.disabled = (Boolean)values[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[7];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.label;
|
||||
values[2] = this.spacing;
|
||||
values[3] = this.mode;
|
||||
values[4] = this.navigationId;
|
||||
values[5] = this.initialSelectionId;
|
||||
values[6] = this.disabled;
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void decode(FacesContext context)
|
||||
{
|
||||
Map requestMap = context.getExternalContext().getRequestParameterMap();
|
||||
String fieldId = getHiddenFieldName();
|
||||
String value = (String)requestMap.get(fieldId);
|
||||
|
||||
int mode = this.mode;
|
||||
if (value != null && value.length() != 0)
|
||||
{
|
||||
// break up the submitted value into it's parts
|
||||
|
||||
// first part is the mode the component is in
|
||||
// followed by the id of the selection if we are drilling down
|
||||
String id = null;
|
||||
int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR);
|
||||
if (sepIndex != -1)
|
||||
{
|
||||
mode = Integer.parseInt(value.substring(0, sepIndex));
|
||||
if (value.length() > sepIndex + 1)
|
||||
{
|
||||
id = value.substring(sepIndex + 1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = Integer.parseInt(value);
|
||||
}
|
||||
|
||||
// raise an event so we can pick the changed values up later
|
||||
ItemSelectorEvent event = new ItemSelectorEvent(this, mode, id);
|
||||
this.queueEvent(event);
|
||||
}
|
||||
|
||||
if (mode == MODE_CONFIRM_SELECTION)
|
||||
{
|
||||
// only bother to check the selection if the mode is set to MODE_AFTER_SELECTION
|
||||
// see if a selection has been submitted
|
||||
String selection = (String)requestMap.get(getClientId(context) + OPTION);
|
||||
if (selection != null && selection.length() != 0)
|
||||
{
|
||||
((EditableValueHolder)this).setSubmittedValue(new NodeRef(Repository.getStoreRef(), selection));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIInput#broadcast(javax.faces.event.FacesEvent)
|
||||
*/
|
||||
public void broadcast(FacesEvent event) throws AbortProcessingException
|
||||
{
|
||||
if (event instanceof ItemSelectorEvent)
|
||||
{
|
||||
ItemSelectorEvent spaceEvent = (ItemSelectorEvent)event;
|
||||
this.mode = spaceEvent.Mode;
|
||||
this.navigationId = spaceEvent.Id;
|
||||
}
|
||||
else
|
||||
{
|
||||
super.broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDisabled())
|
||||
{
|
||||
// render a read-only view of the selected category (if any)
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
// see if there is a current value for the category
|
||||
NodeRef nodeRef = (NodeRef)getSubmittedValue();
|
||||
if (nodeRef == null)
|
||||
{
|
||||
Object val = getValue();
|
||||
if (val instanceof NodeRef)
|
||||
{
|
||||
nodeRef = (NodeRef)val;
|
||||
}
|
||||
else if (val instanceof String && ((String)val).length() != 0)
|
||||
{
|
||||
nodeRef = new NodeRef((String)val);
|
||||
}
|
||||
}
|
||||
|
||||
// if there is a value show it's name
|
||||
if (nodeRef != null)
|
||||
{
|
||||
NodeService service = Repository.getServiceRegistry(context).getNodeService();
|
||||
out.write(Repository.getNameForNode(service, nodeRef));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// render an editable control for selecting categories
|
||||
String clientId = getClientId(context);
|
||||
|
||||
StringBuilder buf = new StringBuilder(512);
|
||||
Map attrs = this.getAttributes();
|
||||
boolean showValueInHiddenField = false;
|
||||
NodeRef value = null;
|
||||
|
||||
String image = null;
|
||||
if (getItemIcon() != null)
|
||||
{
|
||||
image = "<span style='padding-right:4px'>" + Utils.buildImageTag(context, getItemIcon(), null, "absmiddle") + "</span>";
|
||||
}
|
||||
|
||||
switch (this.mode)
|
||||
{
|
||||
case MODE_BEFORE_SELECTION:
|
||||
case MODE_CONFIRM_SELECTION:
|
||||
case MODE_CANCEL_SELECTION:
|
||||
{
|
||||
UserTransaction tx = null;
|
||||
try
|
||||
{
|
||||
tx = Repository.getUserTransaction(context, true);
|
||||
tx.begin();
|
||||
|
||||
NodeRef submittedValue = (NodeRef)getSubmittedValue();
|
||||
if (submittedValue != null)
|
||||
{
|
||||
value = submittedValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
Object val = getValue();
|
||||
if (val instanceof NodeRef)
|
||||
{
|
||||
value = (NodeRef)val;
|
||||
}
|
||||
else if (val instanceof String && ((String)val).length() != 0)
|
||||
{
|
||||
value = new NodeRef((String)val);
|
||||
}
|
||||
}
|
||||
|
||||
// show just the initial or current selection link
|
||||
String label;
|
||||
if (value == null)
|
||||
{
|
||||
label = getLabel();
|
||||
|
||||
// if the label is still null get the default from the message bundle
|
||||
if (label == null)
|
||||
{
|
||||
label = getDefaultLabel();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
label = Repository.getNameForNode(getNodeService(context), value);
|
||||
showValueInHiddenField = true;
|
||||
}
|
||||
|
||||
// output surrounding span for style purposes
|
||||
buf.append("<span");
|
||||
if (attrs.get("style") != null)
|
||||
{
|
||||
buf.append(" style=\"")
|
||||
.append(attrs.get("style"))
|
||||
.append('"');
|
||||
}
|
||||
if (attrs.get("styleClass") != null)
|
||||
{
|
||||
buf.append(" class=")
|
||||
.append(attrs.get("styleClass"));
|
||||
}
|
||||
buf.append(">");
|
||||
|
||||
// rendering as initial selection mode means the sibilings of the selected
|
||||
// item are shown instead of the children on first click in.
|
||||
int theMode = MODE_INITIAL_SELECTION;
|
||||
|
||||
// if we have an initial selection and no value set the initial one up
|
||||
if (value == null && this.getInitialSelection() != null)
|
||||
{
|
||||
value = new NodeRef(Repository.getStoreRef(), this.getInitialSelection());
|
||||
}
|
||||
|
||||
// field value is whether we are picking and the current or parent Id value
|
||||
String fieldValue;
|
||||
if (value != null)
|
||||
{
|
||||
fieldValue = encodeFieldValues(theMode, value.getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldValue = encodeFieldValues(theMode, null);
|
||||
}
|
||||
buf.append("<a href='#' onclick=\"");
|
||||
buf.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), fieldValue));
|
||||
buf.append('"');
|
||||
if (attrs.get("nodeStyle") != null)
|
||||
{
|
||||
buf.append(" style=\"")
|
||||
.append(attrs.get("nodeStyle"))
|
||||
.append('"');
|
||||
}
|
||||
if (attrs.get("nodeStyleClass") != null)
|
||||
{
|
||||
buf.append(" class=")
|
||||
.append(attrs.get("nodeStyleClass"));
|
||||
}
|
||||
buf.append(">")
|
||||
.append(label)
|
||||
.append("</a></span>");
|
||||
|
||||
tx.commit();
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
|
||||
Utils.addErrorMessage(err.getMessage(), err);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MODE_DRILLDOWN_SELECTION:
|
||||
case MODE_INITIAL_SELECTION:
|
||||
{
|
||||
// show the picker list
|
||||
// get the children of the node ref to show
|
||||
NodeService service = getNodeService(context);
|
||||
UserTransaction tx = null;
|
||||
try
|
||||
{
|
||||
tx = Repository.getUserTransaction(context, true);
|
||||
tx.begin();
|
||||
|
||||
buf.append("<table border=0 cellspacing=1 cellpadding=1");
|
||||
if (attrs.get("style") != null)
|
||||
{
|
||||
buf.append(" style=\"")
|
||||
.append(attrs.get("style"))
|
||||
.append('"');
|
||||
}
|
||||
if (attrs.get("styleClass") != null)
|
||||
{
|
||||
buf.append(" class=")
|
||||
.append(attrs.get("styleClass"));
|
||||
}
|
||||
buf.append(">");
|
||||
|
||||
// if we are setting up the initial selection we need to get the
|
||||
// parent id of the initial selection so the user can actually see
|
||||
// the item when the list is rendered
|
||||
if (this.mode == MODE_INITIAL_SELECTION)
|
||||
{
|
||||
this.navigationId = getParentNodeId(context);
|
||||
}
|
||||
|
||||
// render "Go Up" link
|
||||
if (this.navigationId != null)
|
||||
{
|
||||
// get the id of the parent node of the current navigation node,
|
||||
// null indicates we are at the root level
|
||||
String id = getParentNodeId(context);
|
||||
|
||||
buf.append("<tr><td></td><td>");
|
||||
|
||||
String upImage = Utils.buildImageTag(context, WebResources.IMAGE_GO_UP, null, "absmiddle");
|
||||
|
||||
// render a link to the parent node
|
||||
renderNodeLink(context, id, Application.getMessage(context, MSG_GO_UP), upImage, buf);
|
||||
buf.append("</td></tr>");
|
||||
}
|
||||
|
||||
String okButtonId = clientId + OK_BUTTON;
|
||||
boolean okButtonEnabled = false;
|
||||
|
||||
// display the children of the specified navigation node ID
|
||||
if (this.navigationId != null)
|
||||
{
|
||||
// get a list of children for the current navigation node
|
||||
Collection<ChildAssociationRef> childRefs = getChildrenForNode(context);
|
||||
for (ChildAssociationRef childRef : childRefs)
|
||||
{
|
||||
// render each child found
|
||||
String childId = childRef.getChildRef().getId();
|
||||
buf.append("<tr><td><input type='radio' name='")
|
||||
.append(clientId).append(OPTION).append("' value='")
|
||||
.append(childId).append("'");
|
||||
if (childId.equals(this.initialSelectionId))
|
||||
{
|
||||
buf.append(" checked");
|
||||
|
||||
// if any radio buttons are checked, the OK button must start enabled
|
||||
okButtonEnabled = true;
|
||||
|
||||
// now remove the initial selection as we only need it the first time
|
||||
this.initialSelectionId = null;
|
||||
}
|
||||
buf.append(" onclick=\"javascript:document.getElementById('")
|
||||
.append(okButtonId)
|
||||
.append("').disabled=false;\"");
|
||||
buf.append("/></td><td>");
|
||||
|
||||
// get the name for the child and output as link
|
||||
NodeRef childNodeRef = new NodeRef(Repository.getStoreRef(), childId);
|
||||
String name = Repository.getNameForNode(service, childNodeRef);
|
||||
renderNodeLink(context, childId, name, image, buf);
|
||||
buf.append("</td></tr>");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// no node set - special case so show the root items
|
||||
Collection<ChildAssociationRef> childRefs = getRootChildren(context);
|
||||
for (ChildAssociationRef childRef : childRefs)
|
||||
{
|
||||
// render each root category found
|
||||
String childId = childRef.getChildRef().getId();
|
||||
buf.append("<tr><td><input type='radio' name='")
|
||||
.append(clientId).append(OPTION).append("' value='")
|
||||
.append(childId).append("'");
|
||||
if (childId.equals(this.initialSelectionId))
|
||||
{
|
||||
buf.append(" checked");
|
||||
|
||||
// if any radio buttons are checked, the OK button must start enabled
|
||||
okButtonEnabled = true;
|
||||
|
||||
// now remove the initial selection as we only need it the first time
|
||||
this.initialSelectionId = null;
|
||||
}
|
||||
buf.append(" onclick=\"javascript:document.getElementById('")
|
||||
.append(okButtonId)
|
||||
.append("').disabled=false;\"");
|
||||
buf.append("/></td><td>");
|
||||
|
||||
// get the name for the child (rather than association name)
|
||||
NodeRef childNodeRef = new NodeRef(Repository.getStoreRef(), childId);
|
||||
String name = Repository.getNameForNode(service, childNodeRef);
|
||||
renderNodeLink(context, childId, name, image, buf);
|
||||
buf.append("</td></tr>");
|
||||
}
|
||||
}
|
||||
|
||||
// render OK button
|
||||
String fieldValue = encodeFieldValues(MODE_CONFIRM_SELECTION, null);
|
||||
buf.append("<tr style='padding-top:4px'><td></td><td align=center>")
|
||||
.append("<input type='button' ")
|
||||
.append(okButtonEnabled == false ? "disabled" : "")
|
||||
.append(" onclick=\"")
|
||||
.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), fieldValue))
|
||||
.append("\" value='")
|
||||
.append(Application.getMessage(context, MSG_OK))
|
||||
.append("' id='")
|
||||
.append(okButtonId)
|
||||
.append("'> ");
|
||||
|
||||
// render Cancel button
|
||||
fieldValue = encodeFieldValues(MODE_CANCEL_SELECTION, null);
|
||||
buf.append("<input type='button' onclick=\"")
|
||||
.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), fieldValue))
|
||||
.append("\" value='")
|
||||
.append(Application.getMessage(context, MSG_CANCEL))
|
||||
.append("'></td></tr>");
|
||||
|
||||
buf.append("</table>");
|
||||
|
||||
tx.commit();
|
||||
}
|
||||
catch (Throwable err)
|
||||
{
|
||||
try { if (tx != null) {tx.rollback();} } catch (Exception tex) {}
|
||||
throw new RuntimeException(err);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// output a hidden field containing the currently selected NodeRef so that JavaScript
|
||||
// can be used to check the state of the component
|
||||
buf.append("<input type='hidden' name='");
|
||||
buf.append(clientId);
|
||||
buf.append("_selected' id='");
|
||||
buf.append(clientId);
|
||||
buf.append("_selected' value='");
|
||||
if (showValueInHiddenField)
|
||||
{
|
||||
buf.append(value);
|
||||
}
|
||||
buf.append("'/>");
|
||||
|
||||
context.getResponseWriter().write(buf.toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Strongly typed component property accessors
|
||||
|
||||
/**
|
||||
* @return Returns the label.
|
||||
*/
|
||||
public String getLabel()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("label");
|
||||
if (vb != null)
|
||||
{
|
||||
this.label = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param label The label to set.
|
||||
*/
|
||||
public void setLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the cell spacing value between space options. Default is 2.
|
||||
*/
|
||||
public Integer getSpacing()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("spacing");
|
||||
if (vb != null)
|
||||
{
|
||||
this.spacing = (Integer)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.spacing != null)
|
||||
{
|
||||
return this.spacing.intValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
// return default
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param spacing The spacing to set.
|
||||
*/
|
||||
public void setSpacing(Integer spacing)
|
||||
{
|
||||
this.spacing = spacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the initial selecttion.
|
||||
*/
|
||||
public String getInitialSelection()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("initialSelection");
|
||||
if (vb != null)
|
||||
{
|
||||
this.initialSelectionId = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.initialSelectionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param initialSelection The initial selection to set.
|
||||
*/
|
||||
public void setInitialSelection(String initialSelection)
|
||||
{
|
||||
this.initialSelectionId = initialSelection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the component should be rendered in a disabled state
|
||||
*
|
||||
* @return Returns whether the component is disabled
|
||||
*/
|
||||
public boolean isDisabled()
|
||||
{
|
||||
if (this.disabled == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("disabled");
|
||||
if (vb != null)
|
||||
{
|
||||
this.disabled = (Boolean)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.disabled == null)
|
||||
{
|
||||
this.disabled = Boolean.FALSE;
|
||||
}
|
||||
|
||||
return this.disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the component should be rendered in a disabled state
|
||||
*
|
||||
* @param disabled true to disable the component
|
||||
*/
|
||||
public void setDisabled(boolean disabled)
|
||||
{
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Protected helpers
|
||||
|
||||
/**
|
||||
* We use a unique hidden field name based on our client Id.
|
||||
* This is on the assumption that there won't be many selectors on screen at once!
|
||||
* Also means we have less values to decode on submit.
|
||||
*
|
||||
* @return hidden field name
|
||||
*/
|
||||
protected String getHiddenFieldName()
|
||||
{
|
||||
return this.getClientId(getFacesContext());
|
||||
}
|
||||
|
||||
protected String encodeFieldValues(int mode, String id)
|
||||
{
|
||||
if (id != null)
|
||||
{
|
||||
return Integer.toString(mode) + NamingContainer.SEPARATOR_CHAR + id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Integer.toString(mode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a node descendant as a clickable link
|
||||
*
|
||||
* @param context FacesContext
|
||||
* @param childRef The ChildAssocRef of the child to render an HTML link for
|
||||
*
|
||||
* @return HTML for a descendant link
|
||||
*/
|
||||
protected String renderNodeLink(FacesContext context, String id, String name, String prefix, StringBuilder buf)
|
||||
{
|
||||
buf.append("<a href='#' onclick=\"");
|
||||
String fieldValue = encodeFieldValues(MODE_DRILLDOWN_SELECTION, id);
|
||||
buf.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), fieldValue));
|
||||
buf.append('"');
|
||||
Map attrs = this.getAttributes();
|
||||
if (attrs.get("nodeStyle") != null)
|
||||
{
|
||||
buf.append(" style=\"")
|
||||
.append(attrs.get("nodeStyle"))
|
||||
.append('"');
|
||||
}
|
||||
if (attrs.get("nodeStyleClass") != null)
|
||||
{
|
||||
buf.append(" class=")
|
||||
.append(attrs.get("nodeStyleClass"));
|
||||
}
|
||||
buf.append('>');
|
||||
|
||||
if (prefix != null)
|
||||
{
|
||||
buf.append(prefix);
|
||||
}
|
||||
buf.append(Utils.encode(name));
|
||||
|
||||
buf.append("</a>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Spring JSF integration to return the Node Service bean instance
|
||||
*
|
||||
* @param context FacesContext
|
||||
*
|
||||
* @return Node Service bean instance or throws exception if not found
|
||||
*/
|
||||
protected static NodeService getNodeService(FacesContext context)
|
||||
{
|
||||
NodeService service = Repository.getServiceRegistry(context).getNodeService();
|
||||
if (service == null)
|
||||
{
|
||||
throw new IllegalStateException("Unable to obtain NodeService bean reference.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Spring JSF integration to return the Dictionary Service bean instance
|
||||
*
|
||||
* @param context FacesContext
|
||||
*
|
||||
* @return Dictionary Service bean instance or throws exception if not found
|
||||
*/
|
||||
protected static DictionaryService getDictionaryService(FacesContext context)
|
||||
{
|
||||
DictionaryService service = Repository.getServiceRegistry(context).getDictionaryService();
|
||||
if (service == null)
|
||||
{
|
||||
throw new IllegalStateException("Unable to obtain DictionaryService bean reference.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing the clicking of a breadcrumb element.
|
||||
*/
|
||||
public static class ItemSelectorEvent extends ActionEvent
|
||||
{
|
||||
public ItemSelectorEvent(UIComponent component, int mode, String id)
|
||||
{
|
||||
super(component);
|
||||
Mode = mode;
|
||||
Id = id;
|
||||
}
|
||||
|
||||
public int Mode;
|
||||
private String Id;
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.web.ui.common.component.IBreadcrumbHandler;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public interface IRepoBreadcrumbHandler extends IBreadcrumbHandler
|
||||
{
|
||||
/**
|
||||
* Return a NodeRef relevant to this breadcrumb element, if any
|
||||
*
|
||||
* @return a NodeRef if relevant to the breadcrumb element, null otherwise
|
||||
*/
|
||||
public NodeRef getNodeRef();
|
||||
}
|
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.search.CategoryService;
|
||||
import org.alfresco.service.cmr.search.CategoryService.Depth;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.ui.repo.WebResources;
|
||||
|
||||
/**
|
||||
* Component to allow the selection of a category
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UICategorySelector extends AbstractItemSelector
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.CategorySelector";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getDefaultLabel()
|
||||
*/
|
||||
public String getDefaultLabel()
|
||||
{
|
||||
return Application.getMessage(FacesContext.getCurrentInstance(), "select_category_prompt");
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Spring JSF integration to return the category service bean instance
|
||||
*
|
||||
* @param context FacesContext
|
||||
*
|
||||
* @return category service bean instance or throws runtime exception if not found
|
||||
*/
|
||||
private static CategoryService getCategoryService(FacesContext context)
|
||||
{
|
||||
CategoryService service = Repository.getServiceRegistry(context).getCategoryService();
|
||||
if (service == null)
|
||||
{
|
||||
throw new IllegalStateException("Unable to obtain CategoryService bean reference.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent id of the current category, or null if the parent has the category root type
|
||||
*
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getParentNodeId(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public String getParentNodeId(FacesContext context)
|
||||
{
|
||||
String id = null;
|
||||
|
||||
if (this.navigationId != null)
|
||||
{
|
||||
ChildAssociationRef parentRef = getNodeService(context).getPrimaryParent(
|
||||
new NodeRef(Repository.getStoreRef(), this.navigationId));
|
||||
Node parentNode = new Node(parentRef.getParentRef());
|
||||
|
||||
DictionaryService dd = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getDictionaryService();
|
||||
|
||||
if (dd.isSubClass(parentNode.getType(), ContentModel.TYPE_CATEGORYROOT) == false)
|
||||
{
|
||||
id = parentRef.getParentRef().getId();
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child categories of the current navigation node
|
||||
*
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getChildrenForNode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Collection<ChildAssociationRef> getChildrenForNode(FacesContext context)
|
||||
{
|
||||
NodeRef nodeRef = new NodeRef(Repository.getStoreRef(), this.navigationId);
|
||||
|
||||
Collection<ChildAssociationRef> childRefs = getCategoryService(context).getChildren(nodeRef,
|
||||
CategoryService.Mode.SUB_CATEGORIES, CategoryService.Depth.IMMEDIATE);
|
||||
|
||||
return childRefs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root categories
|
||||
*
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getRootChildren(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Collection<ChildAssociationRef> getRootChildren(FacesContext context)
|
||||
{
|
||||
return getCategoryService(context).getCategories(Repository.getStoreRef(), ContentModel.ASPECT_GEN_CLASSIFIABLE, Depth.IMMEDIATE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getItemIcon()
|
||||
*/
|
||||
public String getItemIcon()
|
||||
{
|
||||
return WebResources.IMAGE_CATEGORY;
|
||||
}
|
||||
}
|
405
source/java/org/alfresco/web/ui/repo/component/UILockIcon.java
Normal file
405
source/java/org/alfresco/web/ui/repo/component/UILockIcon.java
Normal file
@@ -0,0 +1,405 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.lock.LockService;
|
||||
import org.alfresco.service.cmr.lock.LockStatus;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.common.component.SelfRenderingComponent;
|
||||
import org.alfresco.web.ui.repo.WebResources;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UILockIcon extends SelfRenderingComponent
|
||||
{
|
||||
private static final String MSG_LOCKED_YOU = "locked_you";
|
||||
private static final String MSG_LOCKED_USER = "locked_user";
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.LockIcon";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.lockImage = (String)values[1];
|
||||
this.lockOwnerImage = (String)values[2];
|
||||
this.align = (String)values[3];
|
||||
this.width = ((Integer)values[4]).intValue();
|
||||
this.height = ((Integer)values[5]).intValue();
|
||||
this.value = values[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[7];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.lockImage;
|
||||
values[2] = this.lockOwnerImage;
|
||||
values[3] = this.align;
|
||||
values[4] = this.width;
|
||||
values[5] = this.height;
|
||||
values[6] = this.value;
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
// get the value and see if the image is locked
|
||||
NodeService nodeService = getNodeService(context);
|
||||
boolean locked = false;
|
||||
boolean lockedOwner = false;
|
||||
|
||||
Object val = getValue();
|
||||
NodeRef ref = null;
|
||||
if (val instanceof NodeRef)
|
||||
{
|
||||
ref = (NodeRef)val;
|
||||
if (nodeService.exists(ref) && nodeService.hasAspect(ref, ContentModel.ASPECT_LOCKABLE) == true)
|
||||
{
|
||||
LockStatus lockStatus = getLockService(context).getLockStatus(ref);
|
||||
locked = (lockStatus == LockStatus.LOCKED || lockStatus == LockStatus.LOCK_OWNER);
|
||||
lockedOwner = (lockStatus == LockStatus.LOCK_OWNER);
|
||||
}
|
||||
}
|
||||
|
||||
String msg = null;
|
||||
|
||||
if (locked == true)
|
||||
{
|
||||
out.write(" <img");
|
||||
|
||||
outputAttribute(out, getAttributes().get("style"), "style");
|
||||
outputAttribute(out, getAttributes().get("styleClass"), "class");
|
||||
|
||||
outputAttribute(out, getAlign(), "align");
|
||||
outputAttribute(out, getWidth(), "width");
|
||||
outputAttribute(out, getHeight(), "height");
|
||||
|
||||
out.write("src=\"");
|
||||
out.write(context.getExternalContext().getRequestContextPath());
|
||||
String lockImage = getLockImage();
|
||||
if (lockedOwner == true && getLockOwnerImage() != null)
|
||||
{
|
||||
lockImage = getLockOwnerImage();
|
||||
}
|
||||
out.write(lockImage);
|
||||
out.write("\" border=0");
|
||||
|
||||
if (lockedOwner == true)
|
||||
{
|
||||
msg = Application.getMessage(context, MSG_LOCKED_YOU);
|
||||
if (getLockedOwnerTooltip() != null)
|
||||
{
|
||||
msg = getLockedOwnerTooltip();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String lockingUser = (String)nodeService.getProperty(ref, ContentModel.PROP_LOCK_OWNER);
|
||||
msg = Application.getMessage(context, MSG_LOCKED_USER);
|
||||
if (getLockedUserTooltip() != null)
|
||||
{
|
||||
msg = getLockedUserTooltip();
|
||||
}
|
||||
StringBuilder buf = new StringBuilder(32);
|
||||
msg = buf.append(msg).append(" '")
|
||||
.append(lockingUser)
|
||||
.append("'").toString();
|
||||
}
|
||||
|
||||
msg = Utils.encode(msg);
|
||||
out.write(" alt=\"");
|
||||
out.write(msg);
|
||||
out.write("\" title=\"");
|
||||
out.write(msg);
|
||||
out.write("\">");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Spring JSF integration to return the Node Service bean instance
|
||||
*
|
||||
* @param context FacesContext
|
||||
*
|
||||
* @return Node Service bean instance or throws exception if not found
|
||||
*/
|
||||
private static NodeService getNodeService(FacesContext context)
|
||||
{
|
||||
NodeService service = Repository.getServiceRegistry(context).getNodeService();
|
||||
if (service == null)
|
||||
{
|
||||
throw new IllegalStateException("Unable to obtain NodeService bean reference.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Spring JSF integration to return the Lock Service bean instance
|
||||
*
|
||||
* @param context FacesContext
|
||||
*
|
||||
* @return Lock Service bean instance or throws exception if not found
|
||||
*/
|
||||
private static LockService getLockService(FacesContext context)
|
||||
{
|
||||
LockService service = Repository.getServiceRegistry(context).getLockService();
|
||||
if (service == null)
|
||||
{
|
||||
throw new IllegalStateException("Unable to obtain LockService bean reference.");
|
||||
}
|
||||
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Strongly typed component property accessors
|
||||
|
||||
/**
|
||||
* @return the image to display as the lock icon. A default is provided if none is set.
|
||||
*/
|
||||
public String getLockImage()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("lockImage");
|
||||
if (vb != null)
|
||||
{
|
||||
this.lockImage = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.lockImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lockImage the image to display as the lock icon. A default is provided if none is set.
|
||||
*/
|
||||
public void setLockImage(String lockImage)
|
||||
{
|
||||
this.lockImage = lockImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the image to display if the owner has the lock.
|
||||
*/
|
||||
public String getLockOwnerImage()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("lockOwnerImage");
|
||||
if (vb != null)
|
||||
{
|
||||
this.lockOwnerImage = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.lockOwnerImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lockOwnerImage the image to display if the owner has the lock.
|
||||
*/
|
||||
public void setLockOwnerImage(String lockOwnerImage)
|
||||
{
|
||||
this.lockOwnerImage = lockOwnerImage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the image alignment value.
|
||||
*/
|
||||
public String getAlign()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("align");
|
||||
if (vb != null)
|
||||
{
|
||||
this.align = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.align;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param align The image alignment value to set.
|
||||
*/
|
||||
public void setAlign(String align)
|
||||
{
|
||||
this.align = align;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the icon height.
|
||||
*/
|
||||
public int getHeight()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("height");
|
||||
if (vb != null)
|
||||
{
|
||||
Integer value = (Integer)vb.getValue(getFacesContext());
|
||||
if (value != null)
|
||||
{
|
||||
this.height = value.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
return this.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param height The icon height to set.
|
||||
*/
|
||||
public void setHeight(int height)
|
||||
{
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the icon width.
|
||||
*/
|
||||
public int getWidth()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("width");
|
||||
if (vb != null)
|
||||
{
|
||||
Integer value = (Integer)vb.getValue(getFacesContext());
|
||||
if (value != null)
|
||||
{
|
||||
this.width = value.intValue();
|
||||
}
|
||||
}
|
||||
|
||||
return this.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param width The iconwidth to set.
|
||||
*/
|
||||
public void setWidth(int width)
|
||||
{
|
||||
this.width = width;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the lockedOwnerTooltip.
|
||||
*/
|
||||
public String getLockedOwnerTooltip()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("lockedOwnerTooltip");
|
||||
if (vb != null)
|
||||
{
|
||||
this.lockedOwnerTooltip = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.lockedOwnerTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lockedOwnerTooltip The lockedOwnerTooltip to set.
|
||||
*/
|
||||
public void setLockedOwnerTooltip(String lockedOwnerTooltip)
|
||||
{
|
||||
this.lockedOwnerTooltip = lockedOwnerTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the lockedUserTooltip.
|
||||
*/
|
||||
public String getLockedUserTooltip()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("lockedUserTooltip");
|
||||
if (vb != null)
|
||||
{
|
||||
this.lockedUserTooltip = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.lockedUserTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param lockedUserTooltip The lockedUserTooltip to set.
|
||||
*/
|
||||
public void setLockedUserTooltip(String lockedUserTooltip)
|
||||
{
|
||||
this.lockedUserTooltip = lockedUserTooltip;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the value (Node or NodeRef)
|
||||
*/
|
||||
public Object getValue()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("value");
|
||||
if (vb != null)
|
||||
{
|
||||
this.value = vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value The Node or NodeRef value to set.
|
||||
*/
|
||||
public void setValue(Object value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
private String lockImage = WebResources.IMAGE_LOCK;
|
||||
private String lockOwnerImage = WebResources.IMAGE_LOCK_OWNER;
|
||||
private String align = null;
|
||||
private int width = 16;
|
||||
private int height = 16;
|
||||
private String lockedOwnerTooltip = null;
|
||||
private String lockedUserTooltip = null;
|
||||
private Object value = null;
|
||||
}
|
@@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version
|
||||
* 2.1 of the License, or (at your option) any later version.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.gnu.org/licenses/lgpl.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import javax.faces.event.FacesEvent;
|
||||
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* This component wraps a standard component to give it multi value capabilities.
|
||||
*
|
||||
* A list of existing values are available, items can be removed from this list
|
||||
* or new items added to the list. To add new items the component dynamically
|
||||
* shows the child component this one wraps.
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UIMultiValueEditor extends UIInput
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(UIMultiValueEditor.class);
|
||||
private static final String MSG_SELECTED_ITEMS = "selected_items";
|
||||
private static final String MSG_NO_SELECTED_ITEMS = "no_selected_items";
|
||||
private static final String MSG_SELECT_ITEM = "select_an_item";
|
||||
|
||||
|
||||
public final static String ACTION_SEPARATOR = ";";
|
||||
public final static int ACTION_NONE = -1;
|
||||
public final static int ACTION_REMOVE = 0;
|
||||
public final static int ACTION_SELECT = 1;
|
||||
public final static int ACTION_ADD = 2;
|
||||
|
||||
private Boolean addingNewItem = Boolean.FALSE;
|
||||
private Boolean readOnly;
|
||||
private Object lastItemAdded;
|
||||
private String selectItemMsg;
|
||||
private String selectedItemsMsg;
|
||||
private String noSelectedItemsMsg;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public UIMultiValueEditor()
|
||||
{
|
||||
setRendererType("org.alfresco.faces.List");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.MultiValueEditor";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.lastItemAdded = values[1];
|
||||
this.readOnly = (Boolean)values[2];
|
||||
this.addingNewItem = (Boolean)values[3];
|
||||
this.selectItemMsg = (String)values[4];
|
||||
this.selectedItemsMsg = (String)values[5];
|
||||
this.noSelectedItemsMsg = (String)values[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[7];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.lastItemAdded;
|
||||
values[2] = this.readOnly;
|
||||
values[3] = this.addingNewItem;
|
||||
values[4] = this.selectItemMsg;
|
||||
values[5] = this.selectedItemsMsg;
|
||||
values[6] = this.noSelectedItemsMsg;
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last item added by the user
|
||||
*
|
||||
* @return The last item added
|
||||
*/
|
||||
public Object getLastItemAdded()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("lastItemAdded");
|
||||
if (vb != null)
|
||||
{
|
||||
this.lastItemAdded = vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.lastItemAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last item to be added by the user
|
||||
*
|
||||
* @param lastItemAdded The last item added
|
||||
*/
|
||||
public void setLastItemAdded(Object lastItemAdded)
|
||||
{
|
||||
this.lastItemAdded = lastItemAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to display for the selected items, if one hasn't been
|
||||
* set it defaults to the message in the bundle under key 'selected_items'.
|
||||
*
|
||||
* @return The message
|
||||
*/
|
||||
public String getSelectedItemsMsg()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("selectedItemsMsg");
|
||||
if (vb != null)
|
||||
{
|
||||
this.selectedItemsMsg = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.selectedItemsMsg == null)
|
||||
{
|
||||
this.selectedItemsMsg = Application.getMessage(getFacesContext(), MSG_SELECTED_ITEMS);
|
||||
}
|
||||
|
||||
return this.selectedItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected items message to display in the UI
|
||||
*
|
||||
* @param selectedItemsMsg The message
|
||||
*/
|
||||
public void setSelectedItemsMsg(String selectedItemsMsg)
|
||||
{
|
||||
this.selectedItemsMsg = selectedItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to display when no items have been selected, if one hasn't been
|
||||
* set it defaults to the message in the bundle under key 'no_selected_items'.
|
||||
*
|
||||
* @return The message
|
||||
*/
|
||||
public String getNoSelectedItemsMsg()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("noSelectedItemsMsg");
|
||||
if (vb != null)
|
||||
{
|
||||
this.noSelectedItemsMsg = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.noSelectedItemsMsg == null)
|
||||
{
|
||||
this.noSelectedItemsMsg = Application.getMessage(getFacesContext(), MSG_NO_SELECTED_ITEMS);
|
||||
}
|
||||
|
||||
return this.noSelectedItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the no selected items message to display in the UI
|
||||
*
|
||||
* @param noSelectedItemsMsg The message
|
||||
*/
|
||||
public void setNoSelectedItemsMsg(String noSelectedItemsMsg)
|
||||
{
|
||||
this.noSelectedItemsMsg = noSelectedItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to display for select an item, if one hasn't been
|
||||
* set it defaults to the message in the bundle under key 'select_an_item'.
|
||||
*
|
||||
* @return The message
|
||||
*/
|
||||
public String getSelectItemMsg()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("selectItemMsg");
|
||||
if (vb != null)
|
||||
{
|
||||
this.selectItemMsg = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.selectItemMsg == null)
|
||||
{
|
||||
this.selectItemMsg = Application.getMessage(getFacesContext(), MSG_SELECT_ITEM);
|
||||
}
|
||||
|
||||
return this.selectItemMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the select an item message to display in the UI
|
||||
*
|
||||
* @param selectedItemsMsg The message
|
||||
*/
|
||||
public void setSelectItemMsg(String selectItemMsg)
|
||||
{
|
||||
this.selectItemMsg = selectItemMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the component is in read only mode
|
||||
*
|
||||
* @return true if the component is in read only mode
|
||||
*/
|
||||
public boolean getReadOnly()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("readOnly");
|
||||
if (vb != null)
|
||||
{
|
||||
this.readOnly = (Boolean)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.readOnly == null)
|
||||
{
|
||||
this.readOnly = Boolean.FALSE;
|
||||
}
|
||||
|
||||
return this.readOnly.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the read only mode for the component
|
||||
*
|
||||
* @param readOnly true to set read only mode
|
||||
*/
|
||||
public void setReadOnly(boolean readOnly)
|
||||
{
|
||||
this.readOnly = Boolean.valueOf(readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the component is adding a new item
|
||||
*
|
||||
* @return true if we are adding a new item
|
||||
*/
|
||||
public boolean getAddingNewItem()
|
||||
{
|
||||
return this.addingNewItem.booleanValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#broadcast(javax.faces.event.FacesEvent)
|
||||
*/
|
||||
public void broadcast(FacesEvent event) throws AbortProcessingException
|
||||
{
|
||||
if (event instanceof MultiValueEditorEvent)
|
||||
{
|
||||
MultiValueEditorEvent assocEvent = (MultiValueEditorEvent)event;
|
||||
List items = (List)getValue();
|
||||
|
||||
switch (assocEvent.Action)
|
||||
{
|
||||
case ACTION_SELECT:
|
||||
{
|
||||
this.addingNewItem = Boolean.TRUE;
|
||||
break;
|
||||
}
|
||||
case ACTION_ADD:
|
||||
{
|
||||
if (items == null)
|
||||
{
|
||||
items = new ArrayList();
|
||||
setSubmittedValue(items);
|
||||
}
|
||||
|
||||
items.add(getLastItemAdded());
|
||||
this.addingNewItem = Boolean.FALSE;
|
||||
|
||||
// get hold of the value binding for the lastItemAdded property
|
||||
// and set it to null to show it's been added to the list
|
||||
ValueBinding vb = getValueBinding("lastItemAdded");
|
||||
if (vb != null)
|
||||
{
|
||||
vb.setValue(FacesContext.getCurrentInstance(), null);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_REMOVE:
|
||||
{
|
||||
items.remove(assocEvent.RemoveIndex);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getRendersChildren()
|
||||
*/
|
||||
public boolean getRendersChildren()
|
||||
{
|
||||
// only show the wrapped component when the add button has been clicked
|
||||
|
||||
return !this.addingNewItem.booleanValue();
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing an action relevant to the ChildAssociationEditor component.
|
||||
*/
|
||||
public static class MultiValueEditorEvent extends ActionEvent
|
||||
{
|
||||
public int Action;
|
||||
public int RemoveIndex;
|
||||
|
||||
public MultiValueEditorEvent(UIComponent component, int action, int removeIndex)
|
||||
{
|
||||
super(component);
|
||||
this.Action = action;
|
||||
this.RemoveIndex = removeIndex;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import javax.faces.component.UICommand;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.ActionEvent;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UINodeDescendants extends UICommand
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Construction
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public UINodeDescendants()
|
||||
{
|
||||
setRendererType("org.alfresco.faces.NodeDescendantsLinkRenderer");
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.NodeDescendants";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.maxChildren = (Integer)values[1];
|
||||
this.showEllipses = (Boolean)values[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[3];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.maxChildren;
|
||||
values[2] = this.showEllipses;
|
||||
return (values);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Strongly typed component property accessors
|
||||
|
||||
/**
|
||||
* @return the maximum number of child descendants to be displayed, default maximum is 3.
|
||||
*/
|
||||
public int getMaxChildren()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("maxChildren");
|
||||
if (vb != null)
|
||||
{
|
||||
this.maxChildren = (Integer)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.maxChildren != null)
|
||||
{
|
||||
return this.maxChildren.intValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
// return default
|
||||
return 3;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value The maximum allowed before the no more links are shown
|
||||
*/
|
||||
public void setMaxChildren(int value)
|
||||
{
|
||||
if (value > 0 && value <= 256)
|
||||
{
|
||||
this.maxChildren = Integer.valueOf(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return whether to show ellipses "..." if more descendants than the maxChildren value are found
|
||||
*/
|
||||
public boolean getShowEllipses()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("showEllipses");
|
||||
if (vb != null)
|
||||
{
|
||||
this.showEllipses = (Boolean)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.showEllipses != null)
|
||||
{
|
||||
return this.showEllipses.booleanValue();
|
||||
}
|
||||
else
|
||||
{
|
||||
// return default
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param showLink True to show ellipses "..." if more descendants than maxChildren are found
|
||||
*/
|
||||
public void setShowEllipses(boolean showEllipses)
|
||||
{
|
||||
this.showEllipses = Boolean.valueOf(showEllipses);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing the clicking of a node descendant element.
|
||||
*/
|
||||
public static class NodeSelectedEvent extends ActionEvent
|
||||
{
|
||||
public NodeSelectedEvent(UIComponent component, NodeRef nodeRef, boolean isParent)
|
||||
{
|
||||
super(component);
|
||||
this.NodeReference = nodeRef;
|
||||
this.IsParent = isParent;
|
||||
}
|
||||
|
||||
public NodeRef NodeReference;
|
||||
public boolean IsParent;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private data
|
||||
|
||||
/** maximum number of child descendants to display */
|
||||
private Integer maxChildren = null;
|
||||
|
||||
/** whether to show ellipses if more descendants than the maxChildren are found */
|
||||
private Boolean showEllipses = null;
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import javax.faces.component.UICommand;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.event.ActionEvent;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UINodePath extends UICommand
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Construction
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public UINodePath()
|
||||
{
|
||||
setRendererType("org.alfresco.faces.NodePathLinkRenderer");
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.NodePath";
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing the clicking of a part of the path element.
|
||||
*/
|
||||
public static class PathElementEvent extends ActionEvent
|
||||
{
|
||||
public PathElementEvent(UIComponent component, NodeRef nodeRef)
|
||||
{
|
||||
super(component);
|
||||
this.NodeReference = nodeRef;
|
||||
}
|
||||
|
||||
public NodeRef NodeReference;
|
||||
}
|
||||
}
|
@@ -0,0 +1,332 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.component.UIOutput;
|
||||
import javax.faces.component.UIPanel;
|
||||
import javax.faces.component.UISelectBoolean;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.config.ConfigService;
|
||||
import org.alfresco.service.cmr.dictionary.AspectDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryException;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.TypeDefinition;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.config.ClientConfigElement;
|
||||
import org.alfresco.web.config.ClientConfigElement.CustomProperty;
|
||||
import org.alfresco.web.ui.common.ComponentConstants;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.common.component.SelfRenderingComponent;
|
||||
import org.alfresco.web.ui.repo.RepoConstants;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UISearchCustomProperties extends SelfRenderingComponent implements NamingContainer
|
||||
{
|
||||
public static final String PREFIX_DATE_TO = "to_";
|
||||
public static final String PREFIX_DATE_FROM = "from_";
|
||||
|
||||
private static final String MSG_TO = "to";
|
||||
private static final String MSG_FROM = "from";
|
||||
|
||||
private static Log logger = LogFactory.getLog(UISearchCustomProperties.class);
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.AdvancedSearch";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
if (getChildCount() == 0)
|
||||
{
|
||||
createComponentsFromConfig(context);
|
||||
}
|
||||
|
||||
// encode the components in a 2 column table
|
||||
out.write("<table cellspacing=2 cellpadding=2 border=0");
|
||||
outputAttribute(out, getAttributes().get("styleClass"), "class");
|
||||
outputAttribute(out, getAttributes().get("style"), "style");
|
||||
out.write('>');
|
||||
List<UIComponent> children = getChildren();
|
||||
int colCounter = 0;
|
||||
for (int i=0; i<children.size(); i++)
|
||||
{
|
||||
UIComponent component = children.get(i);
|
||||
if (component instanceof UIPanel)
|
||||
{
|
||||
out.write("<tr><td colspan=2>");
|
||||
Utils.encodeRecursive(context, component);
|
||||
out.write("</td></tr>");
|
||||
colCounter += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((colCounter & 1) == 0)
|
||||
{
|
||||
out.write("<tr>");
|
||||
}
|
||||
out.write("<td>");
|
||||
Utils.encodeRecursive(context, component);
|
||||
out.write("</td>");
|
||||
if ((colCounter & 1) == 1)
|
||||
{
|
||||
out.write("</tr>");
|
||||
}
|
||||
colCounter++;
|
||||
}
|
||||
}
|
||||
out.write("</table>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the components from the Advanced Search config entries
|
||||
*
|
||||
* @param context FacesContext
|
||||
*/
|
||||
private void createComponentsFromConfig(FacesContext context)
|
||||
{
|
||||
DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService();
|
||||
ConfigService configService = Application.getConfigService(context);
|
||||
ClientConfigElement clientConfig = (ClientConfigElement)configService.getGlobalConfig().getConfigElement(
|
||||
ClientConfigElement.CONFIG_ELEMENT_ID);
|
||||
|
||||
// create an appropriate component for each custom property
|
||||
// using the DataDictionary to look-up labels and value types
|
||||
String beanBinding = (String)getAttributes().get("bean") + '.' + (String)getAttributes().get("var");
|
||||
List<CustomProperty> props = clientConfig.getCustomProperties();
|
||||
if (props != null)
|
||||
{
|
||||
for (CustomProperty property : props)
|
||||
{
|
||||
try
|
||||
{
|
||||
// try to find the Property definition for the specified Type or Aspect
|
||||
PropertyDefinition propDef = null;
|
||||
if (property.Type != null)
|
||||
{
|
||||
QName type = Repository.resolveToQName(property.Type);
|
||||
TypeDefinition typeDef = dd.getType(type);
|
||||
propDef = typeDef.getProperties().get(Repository.resolveToQName(property.Property));
|
||||
}
|
||||
else if (property.Aspect != null)
|
||||
{
|
||||
QName aspect = Repository.resolveToQName(property.Aspect);
|
||||
AspectDefinition aspectDef = dd.getAspect(aspect);
|
||||
propDef = aspectDef.getProperties().get(Repository.resolveToQName(property.Property));
|
||||
}
|
||||
|
||||
// if we found a def, then we can build components to represent it
|
||||
if (propDef != null)
|
||||
{
|
||||
// TODO: add display label I18N message support to configelement and here
|
||||
String label = propDef.getTitle() != null ? propDef.getTitle() : propDef.getName().getLocalName();
|
||||
|
||||
// special handling for Date and DateTime
|
||||
DataTypeDefinition dataTypeDef = propDef.getDataType();
|
||||
if (DataTypeDefinition.DATE.equals(dataTypeDef.getName()) || DataTypeDefinition.DATETIME.equals(dataTypeDef.getName()))
|
||||
{
|
||||
getChildren().add( generateControl(context, propDef, label, beanBinding) );
|
||||
}
|
||||
else
|
||||
{
|
||||
getChildren().add( generateLabel(context, label) );
|
||||
getChildren().add( generateControl(context, propDef, null, beanBinding) );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (DictionaryException ddErr)
|
||||
{
|
||||
logger.warn("Error building custom properties for Advanced Search: " + ddErr.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a JSF OutputText component/renderer
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param displayLabel The display label text
|
||||
* @param parent The parent component for the label
|
||||
*
|
||||
* @return UIComponent
|
||||
*/
|
||||
private UIComponent generateLabel(FacesContext context, String displayLabel)
|
||||
{
|
||||
UIOutput label = (UIOutput)context.getApplication().createComponent(ComponentConstants.JAVAX_FACES_OUTPUT);
|
||||
label.setId(context.getViewRoot().createUniqueId());
|
||||
label.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
label.setValue(displayLabel + ": ");
|
||||
return label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an appropriate control for the given property
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param propDef The definition of the property to create the control for
|
||||
* @param displayLabel Display label for the component
|
||||
* @param beanBinding Combined name of the value bound bean and variable used for value binding expression
|
||||
*
|
||||
* @return UIComponent
|
||||
*/
|
||||
private UIComponent generateControl(FacesContext context, PropertyDefinition propDef, String displayLabel, String beanBinding)
|
||||
{
|
||||
UIComponent control = null;
|
||||
|
||||
DataTypeDefinition dataTypeDef = propDef.getDataType();
|
||||
QName typeName = dataTypeDef.getName();
|
||||
|
||||
javax.faces.application.Application facesApp = context.getApplication();
|
||||
|
||||
// create default value binding to a Map of values with a defined name
|
||||
ValueBinding vb = facesApp.createValueBinding(
|
||||
"#{" + beanBinding + "[\"" + propDef.getName().toString() + "\"]}");
|
||||
|
||||
// generate the appropriate input field
|
||||
if (typeName.equals(DataTypeDefinition.BOOLEAN))
|
||||
{
|
||||
control = (UISelectBoolean)facesApp.createComponent(ComponentConstants.JAVAX_FACES_SELECT_BOOLEAN);
|
||||
control.setRendererType(ComponentConstants.JAVAX_FACES_CHECKBOX);
|
||||
control.setValueBinding("value", vb);
|
||||
}
|
||||
else if (typeName.equals(DataTypeDefinition.CATEGORY))
|
||||
{
|
||||
control = (UICategorySelector)facesApp.createComponent(RepoConstants.ALFRESCO_FACES_CATEGORY_SELECTOR);
|
||||
control.setValueBinding("value", vb);
|
||||
}
|
||||
else if (typeName.equals(DataTypeDefinition.DATETIME) || typeName.equals(DataTypeDefinition.DATE))
|
||||
{
|
||||
Boolean showTime = Boolean.valueOf(typeName.equals(DataTypeDefinition.DATETIME));
|
||||
|
||||
// Need to output component for From and To date selectors and labels
|
||||
// also neeed checkbox for enable/disable state - requires an outer wrapper component
|
||||
control = (UIPanel)facesApp.createComponent(ComponentConstants.JAVAX_FACES_PANEL);
|
||||
control.setRendererType(ComponentConstants.JAVAX_FACES_GRID);
|
||||
control.getAttributes().put("columns", Integer.valueOf(2));
|
||||
|
||||
// enabled state checkbox
|
||||
UIInput checkbox = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_SELECT_BOOLEAN);
|
||||
checkbox.setRendererType(ComponentConstants.JAVAX_FACES_CHECKBOX);
|
||||
checkbox.setId(context.getViewRoot().createUniqueId());
|
||||
ValueBinding vbCheckbox = facesApp.createValueBinding(
|
||||
"#{" + beanBinding + "[\"" + propDef.getName().toString() + "\"]}");
|
||||
checkbox.setValueBinding("value", vbCheckbox);
|
||||
control.getChildren().add(checkbox);
|
||||
|
||||
// main display label
|
||||
UIOutput label = (UIOutput)context.getApplication().createComponent(ComponentConstants.JAVAX_FACES_OUTPUT);
|
||||
label.setId(context.getViewRoot().createUniqueId());
|
||||
label.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
label.setValue(displayLabel + ":");
|
||||
control.getChildren().add(label);
|
||||
|
||||
// from date label
|
||||
UIOutput labelFromDate = (UIOutput)context.getApplication().createComponent(ComponentConstants.JAVAX_FACES_OUTPUT);
|
||||
labelFromDate.setId(context.getViewRoot().createUniqueId());
|
||||
labelFromDate.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
labelFromDate.setValue(Application.getMessage(context, MSG_FROM));
|
||||
control.getChildren().add(labelFromDate);
|
||||
|
||||
// from date control
|
||||
UIInput inputFromDate = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT);
|
||||
inputFromDate.setId(context.getViewRoot().createUniqueId());
|
||||
inputFromDate.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
|
||||
inputFromDate.getAttributes().put("startYear", new Integer(1970));
|
||||
inputFromDate.getAttributes().put("yearCount", new Integer(50));
|
||||
inputFromDate.getAttributes().put("showTime", showTime);
|
||||
ValueBinding vbFromDate = facesApp.createValueBinding(
|
||||
"#{" + beanBinding + "[\"" + PREFIX_DATE_FROM + propDef.getName().toString() + "\"]}");
|
||||
inputFromDate.setValueBinding("value", vbFromDate);
|
||||
control.getChildren().add(inputFromDate);
|
||||
|
||||
// to date label
|
||||
UIOutput labelToDate = (UIOutput)context.getApplication().createComponent(ComponentConstants.JAVAX_FACES_OUTPUT);
|
||||
labelToDate.setId(context.getViewRoot().createUniqueId());
|
||||
labelToDate.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
labelToDate.setValue(Application.getMessage(context, MSG_TO));
|
||||
control.getChildren().add(labelToDate);
|
||||
|
||||
// to date control
|
||||
UIInput inputToDate = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT);
|
||||
inputToDate.setId(context.getViewRoot().createUniqueId());
|
||||
inputToDate.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
|
||||
inputToDate.getAttributes().put("startYear", new Integer(1970));
|
||||
inputToDate.getAttributes().put("yearCount", new Integer(50));
|
||||
inputToDate.getAttributes().put("showTime", showTime);
|
||||
ValueBinding vbToDate = facesApp.createValueBinding(
|
||||
"#{" + beanBinding + "[\"" + PREFIX_DATE_TO + propDef.getName().toString() + "\"]}");
|
||||
inputToDate.setValueBinding("value", vbToDate);
|
||||
control.getChildren().add(inputToDate);
|
||||
}
|
||||
else if (typeName.equals(DataTypeDefinition.NODE_REF))
|
||||
{
|
||||
control = (UISpaceSelector)facesApp.createComponent(RepoConstants.ALFRESCO_FACES_SPACE_SELECTOR);
|
||||
control.setValueBinding("value", vb);
|
||||
}
|
||||
else
|
||||
{
|
||||
// any other type is represented as an input text field
|
||||
control = (UIInput)facesApp.createComponent(ComponentConstants.JAVAX_FACES_INPUT);
|
||||
control.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
control.getAttributes().put("size", "28");
|
||||
control.getAttributes().put("maxlength", "1024");
|
||||
control.setValueBinding("value", vb);
|
||||
}
|
||||
|
||||
// set up the common aspects of the control
|
||||
control.setId(context.getViewRoot().createUniqueId());
|
||||
|
||||
return control;
|
||||
}
|
||||
}
|
@@ -0,0 +1,343 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UICommand;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import javax.faces.event.FacesEvent;
|
||||
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.SearchContext;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UISimpleSearch extends UICommand
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
*/
|
||||
public UISimpleSearch()
|
||||
{
|
||||
// specifically set the renderer type to null to indicate to the framework
|
||||
// that this component renders itself - there is no abstract renderer class
|
||||
setRendererType(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.SimpleSearch";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.search = (SearchContext)values[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[2];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.search;
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void decode(FacesContext context)
|
||||
{
|
||||
Map requestMap = context.getExternalContext().getRequestParameterMap();
|
||||
String fieldId = Utils.getActionHiddenFieldName(context, this);
|
||||
String value = (String)requestMap.get(fieldId);
|
||||
// we are clicked if the hidden field contained our client id
|
||||
if (value != null)
|
||||
{
|
||||
if (value.equals(this.getClientId(context)))
|
||||
{
|
||||
String searchText = (String)requestMap.get(getClientId(context));
|
||||
|
||||
if (searchText.length() != 0)
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Search text submitted: " + searchText);
|
||||
int option = -1;
|
||||
String optionFieldName = getClientId(context) + NamingContainer.SEPARATOR_CHAR + OPTION_PARAM;
|
||||
String optionStr = (String)requestMap.get(optionFieldName);
|
||||
if (optionStr.length() != 0)
|
||||
{
|
||||
option = Integer.parseInt(optionStr);
|
||||
}
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Search option submitted: " + option);
|
||||
|
||||
// queue event so system can perform a search and update the component
|
||||
SearchEvent event = new SearchEvent(this, searchText, option);
|
||||
this.queueEvent(event);
|
||||
}
|
||||
}
|
||||
else if (value.equals(ADVSEARCH_PARAM))
|
||||
{
|
||||
// found advanced search navigation action
|
||||
// TODO: TEMP: set this outcome from a component attribute!
|
||||
AdvancedSearchEvent event = new AdvancedSearchEvent(this, "advSearch");
|
||||
this.queueEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UICommand#broadcast(javax.faces.event.FacesEvent)
|
||||
*/
|
||||
public void broadcast(FacesEvent event) throws AbortProcessingException
|
||||
{
|
||||
if (event instanceof SearchEvent)
|
||||
{
|
||||
// update the component parameters from the search event details
|
||||
SearchEvent searchEvent = (SearchEvent)event;
|
||||
|
||||
// construct the Search Context object
|
||||
SearchContext context = new SearchContext();
|
||||
context.setText(searchEvent.SearchText);
|
||||
context.setMode(searchEvent.SearchMode);
|
||||
this.search = context;
|
||||
|
||||
super.broadcast(event);
|
||||
}
|
||||
else if (event instanceof AdvancedSearchEvent)
|
||||
{
|
||||
// special case to navigate to the advanced search screen
|
||||
AdvancedSearchEvent searchEvent = (AdvancedSearchEvent)event;
|
||||
FacesContext fc = getFacesContext();
|
||||
fc.getApplication().getNavigationHandler().handleNavigation(fc, null, searchEvent.Outcome);
|
||||
|
||||
// NOTE: we don't call super() here so that our nav outcome is the one that occurs!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
ResourceBundle bundle = (ResourceBundle)Application.getBundle(context);
|
||||
|
||||
// script for dynamic simple search menu drop-down options
|
||||
out.write("<script>");
|
||||
out.write("function _noenter(event) {" +
|
||||
"if (event && event.keyCode == 13) {" +
|
||||
" _searchSubmit();return false; }" +
|
||||
"else {" +
|
||||
" return true; } }");
|
||||
out.write("function _searchSubmit() {");
|
||||
out.write(Utils.generateFormSubmit(context, this, Utils.getActionHiddenFieldName(context, this), getClientId(context)));
|
||||
out.write("}");
|
||||
out.write("</script>");
|
||||
|
||||
// outer table containing search drop-down icon, text box and search Go image button
|
||||
out.write("<table cellspacing=4 cellpadding=0>");
|
||||
out.write("<tr><td style='padding-top:2px'>");
|
||||
|
||||
String searchImage = Utils.buildImageTag(context, "/images/icons/search_icon.gif", 15, 15,
|
||||
bundle.getString(MSG_GO), "_searchSubmit();");
|
||||
|
||||
out.write(Utils.buildImageTag(context, "/images/icons/search_controls.gif", 27, 13,
|
||||
bundle.getString(MSG_OPTIONS), "javascript:_toggleMenu(event, '_alfsearch');"));
|
||||
|
||||
// dynamic DIV area containing search options
|
||||
out.write("<br><div id='_alfsearch' style='position:absolute;display:none'>");
|
||||
out.write("<table border=0 bgcolor='#eeeeee' style='border-top:thin solid #FFFFFF;border-left:thin solid #FFFFFF;border-right:thin solid #444444;border-bottom:thin solid #444444;' cellspacing=4 cellpadding=0>");
|
||||
|
||||
// output each option - setting the current one to CHECKED
|
||||
String optionFieldName = getClientId(context) + NamingContainer.SEPARATOR_CHAR + OPTION_PARAM;
|
||||
String radioOption = "<tr><td class='userInputForm'><input type='radio' name='" + optionFieldName + "'";
|
||||
out.write(radioOption);
|
||||
out.write(" VALUE='0'");
|
||||
int searchMode = getSearchMode();
|
||||
if (searchMode == 0) out.write(" CHECKED");
|
||||
out.write("><nobr>" + bundle.getString(MSG_ALL_ITEMS) + "</nobr></td></tr>");
|
||||
out.write(radioOption);
|
||||
out.write(" VALUE='1'");
|
||||
if (searchMode == 1) out.write(" CHECKED");
|
||||
out.write("><nobr>" + bundle.getString(MSG_FILE_NAMES_CONTENTS) + "</nobr></td></tr>");
|
||||
out.write(radioOption);
|
||||
out.write(" VALUE='2'");
|
||||
if (searchMode == 2) out.write(" CHECKED");
|
||||
out.write("><nobr>" + bundle.getString(MSG_FILE_NAMES_ONLY) + "</nobr></td></tr>");
|
||||
out.write(radioOption);
|
||||
out.write(" VALUE='3'");
|
||||
if (searchMode == 3) out.write(" CHECKED");
|
||||
out.write("><nobr>" + bundle.getString(MSG_SPACE_NAMES_ONLY) + "</nobr></td></tr>");
|
||||
|
||||
// row with table containing advanced search link and Search Go button
|
||||
out.write("<tr><td><table width=100%><tr><td>");
|
||||
// generate a link that will cause an action event to navigate to the advanced search screen
|
||||
out.write("<a class='small' href='#' onclick=\"");
|
||||
out.write(Utils.generateFormSubmit(context, this, Utils.getActionHiddenFieldName(context, this), ADVSEARCH_PARAM));
|
||||
out.write("\">");
|
||||
out.write(bundle.getString(MSG_ADVANCED_SEARCH));
|
||||
out.write("</a>");
|
||||
out.write("</td><td align=right>");
|
||||
out.write(searchImage);
|
||||
out.write("</td></tr></table></td></tr>");
|
||||
out.write("</table></div>");
|
||||
|
||||
// input text box
|
||||
out.write("</td><td>");
|
||||
out.write("<input name='");
|
||||
out.write(getClientId(context));
|
||||
// TODO: style and class from component properties!
|
||||
out.write("' onkeypress=\"return _noenter(event)\"");
|
||||
out.write(" type='text' maxlength='1024' style='width:130px;padding-top:3px;font-size:10px' value=\"");
|
||||
// output previous search text stored in this component!
|
||||
out.write(Utils.replace(getLastSearch(), "\"", """));
|
||||
out.write("\">");
|
||||
|
||||
// search Go image button
|
||||
out.write("</td><td>");
|
||||
out.write(searchImage);
|
||||
|
||||
// end outer table
|
||||
out.write("</td></tr></table>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current Search Context
|
||||
*/
|
||||
public SearchContext getSearchContext()
|
||||
{
|
||||
return this.search;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The last set search text value
|
||||
*/
|
||||
public String getLastSearch()
|
||||
{
|
||||
if (search != null)
|
||||
{
|
||||
return this.search.getText();
|
||||
}
|
||||
else
|
||||
{
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current search mode (see constants)
|
||||
*/
|
||||
public int getSearchMode()
|
||||
{
|
||||
if (search != null)
|
||||
{
|
||||
return this.search.getMode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return SearchContext.SEARCH_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private data
|
||||
|
||||
private static Log logger = LogFactory.getLog(UISimpleSearch.class);
|
||||
|
||||
/** I18N message Ids */
|
||||
private static final String MSG_ADVANCED_SEARCH = "advanced_search";
|
||||
private static final String MSG_OPTIONS = "options";
|
||||
private static final String MSG_GO = "go";
|
||||
private static final String MSG_SPACE_NAMES_ONLY = "space_names";
|
||||
private static final String MSG_FILE_NAMES_ONLY = "file_names";
|
||||
private static final String MSG_FILE_NAMES_CONTENTS = "file_names_contents";
|
||||
private static final String MSG_ALL_ITEMS = "all_items";
|
||||
|
||||
private static final String OPTION_PARAM = "_option";
|
||||
private static final String ADVSEARCH_PARAM = "_advsearch";
|
||||
|
||||
/** last search context used */
|
||||
private SearchContext search = null;
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing a search execution from the UISimpleSearch component.
|
||||
*/
|
||||
public static class SearchEvent extends ActionEvent
|
||||
{
|
||||
private static final long serialVersionUID = 3918135612344774322L;
|
||||
|
||||
public SearchEvent(UIComponent component, String text, int mode)
|
||||
{
|
||||
super(component);
|
||||
SearchText = text;
|
||||
SearchMode = mode;
|
||||
}
|
||||
|
||||
public String SearchText;
|
||||
public int SearchMode;
|
||||
}
|
||||
|
||||
public static class AdvancedSearchEvent extends ActionEvent
|
||||
{
|
||||
public AdvancedSearchEvent(UIComponent component, String outcome)
|
||||
{
|
||||
super(component);
|
||||
Outcome = outcome;
|
||||
}
|
||||
|
||||
public String Outcome;
|
||||
}
|
||||
}
|
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.ui.repo.WebResources;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UISpaceSelector extends AbstractItemSelector
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.SpaceSelector";
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getDefaultLabel()
|
||||
*/
|
||||
public String getDefaultLabel()
|
||||
{
|
||||
return Application.getMessage(FacesContext.getCurrentInstance(), "select_space_prompt");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent id of the current space or null if the parent space is the company home space
|
||||
*
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getParentNodeId(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public String getParentNodeId(FacesContext context)
|
||||
{
|
||||
String id = null;
|
||||
|
||||
if (this.navigationId != null && this.navigationId.equals(Application.getCompanyRootId()) == false)
|
||||
{
|
||||
ChildAssociationRef parentRef = getNodeService(context).getPrimaryParent(
|
||||
new NodeRef(Repository.getStoreRef(), this.navigationId));
|
||||
id = parentRef.getParentRef().getId();
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the child spaces of the current space
|
||||
*
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getChildrenForNode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Collection<ChildAssociationRef> getChildrenForNode(FacesContext context)
|
||||
{
|
||||
NodeRef nodeRef = new NodeRef(Repository.getStoreRef(), this.navigationId);
|
||||
List<ChildAssociationRef> allKids = getNodeService(context).getChildAssocs(nodeRef);
|
||||
DictionaryService dd = getDictionaryService(context);
|
||||
NodeService service = getNodeService(context);
|
||||
|
||||
// filter out those children that are not spaces
|
||||
List<ChildAssociationRef> spaceKids = new ArrayList<ChildAssociationRef>();
|
||||
for (ChildAssociationRef ref : allKids)
|
||||
{
|
||||
if (dd.isSubClass(service.getType(ref.getChildRef()), ContentModel.TYPE_FOLDER) &&
|
||||
dd.isSubClass(service.getType(ref.getChildRef()), ContentModel.TYPE_SYSTEM_FOLDER) == false)
|
||||
{
|
||||
spaceKids.add(ref);
|
||||
}
|
||||
}
|
||||
|
||||
return spaceKids;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current users home space
|
||||
*
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getRootChildren(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Collection<ChildAssociationRef> getRootChildren(FacesContext context)
|
||||
{
|
||||
// get the root space from the current user
|
||||
//String rootId = Application.getCurrentUser(context).getHomeSpaceId();
|
||||
NodeRef rootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
|
||||
|
||||
// get a child association reference back to the real repository root to satisfy
|
||||
// the generic API we have in the abstract super class
|
||||
ChildAssociationRef childRefFromRealRoot = getNodeService(context).getPrimaryParent(rootRef);
|
||||
List<ChildAssociationRef> roots = new ArrayList<ChildAssociationRef>(1);
|
||||
roots.add(childRefFromRealRoot);
|
||||
|
||||
return roots;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.AbstractItemSelector#getItemIcon()
|
||||
*/
|
||||
public String getItemIcon()
|
||||
{
|
||||
return WebResources.IMAGE_SPACE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.evaluator;
|
||||
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.security.AccessStatus;
|
||||
import org.alfresco.service.cmr.security.PermissionService;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.ui.common.component.evaluator.BaseEvaluator;
|
||||
|
||||
/**
|
||||
* Evaulator for returning whether a Node is Allowed/Denied a list of permissions
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class PermissionEvaluator extends BaseEvaluator
|
||||
{
|
||||
/**
|
||||
* Evaluate against the component attributes. Return true to allow the inner
|
||||
* components to render, false to hide them during rendering.
|
||||
*
|
||||
* IN: Value - either a Node (preferred) or NodeRef to test
|
||||
* Allow - Permission(s) (comma separated) to test against
|
||||
* Deny - Permission(s) (comma separated) to test against
|
||||
*
|
||||
* @return true to allow rendering of child components, false otherwise
|
||||
*/
|
||||
public boolean evaluate()
|
||||
{
|
||||
boolean result = false;
|
||||
|
||||
// TODO: implement Deny permissions checking (as required...)
|
||||
|
||||
try
|
||||
{
|
||||
Object obj = getValue();
|
||||
if (obj instanceof Node)
|
||||
{
|
||||
// used the cached permissions checks in the Node instance
|
||||
// this means multiple calls to evaluators don't need to keep calling services
|
||||
// and permissions on a Node shouldn't realistically change over the life of an instance
|
||||
String[] allow = getAllowPermissions();
|
||||
if (allow.length != 0)
|
||||
{
|
||||
result = true;
|
||||
for (int i=0; i<allow.length; i++)
|
||||
{
|
||||
result = result & ((Node)obj).hasPermission(allow[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (obj instanceof NodeRef)
|
||||
{
|
||||
// perform the check for permissions here against NodeRef using service
|
||||
PermissionService service = Repository.getServiceRegistry(getFacesContext()).getPermissionService();
|
||||
String[] allow = getAllowPermissions();
|
||||
if (allow.length != 0)
|
||||
{
|
||||
result = true;
|
||||
for (int i=0; i<allow.length; i++)
|
||||
{
|
||||
result = result & (AccessStatus.ALLOWED == service.hasPermission(((NodeRef)obj), allow[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception err)
|
||||
{
|
||||
// return default value on error
|
||||
s_logger.debug("Error during PermissionEvaluator evaluation: " + err.getMessage());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.allow = (String)values[1];
|
||||
this.deny = (String)values[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[3];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.allow;
|
||||
values[2] = this.deny;
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the array of Allow permissions
|
||||
*/
|
||||
private String[] getAllowPermissions()
|
||||
{
|
||||
String[] allowPermissions;
|
||||
|
||||
String allow = getAllow();
|
||||
if (allow != null)
|
||||
{
|
||||
if (allow.indexOf(',') == -1)
|
||||
{
|
||||
// simple case - one permission
|
||||
allowPermissions = new String[1];
|
||||
allowPermissions[0] = allow;
|
||||
}
|
||||
else
|
||||
{
|
||||
// complex case - multiple permissions
|
||||
StringTokenizer t = new StringTokenizer(allow, ",");
|
||||
allowPermissions = new String[t.countTokens()];
|
||||
for (int i=0; i<allowPermissions.length; i++)
|
||||
{
|
||||
allowPermissions[i] = t.nextToken();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
allowPermissions = new String[0];
|
||||
}
|
||||
|
||||
return allowPermissions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the allow permissions to match value against
|
||||
*
|
||||
* @return the allow permissions to match value against
|
||||
*/
|
||||
public String getAllow()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("allow");
|
||||
if (vb != null)
|
||||
{
|
||||
this.allow = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the allow permissions to match value against
|
||||
*
|
||||
* @param allow allow permissions to match value against
|
||||
*/
|
||||
public void setAllow(String allow)
|
||||
{
|
||||
this.allow = allow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the deny permissions to match value against
|
||||
*
|
||||
* @return the deny permissions to match value against
|
||||
*/
|
||||
public String getDeny()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("deny");
|
||||
if (vb != null)
|
||||
{
|
||||
this.deny = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.deny;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the deny permissions to match value against
|
||||
*
|
||||
* @param deny deny permissions to match value against
|
||||
*/
|
||||
public void setDeny(String deny)
|
||||
{
|
||||
this.deny = deny;
|
||||
}
|
||||
|
||||
|
||||
/** the deny permissions to match value against */
|
||||
private String deny = null;
|
||||
|
||||
/** the allow permissions to match value against */
|
||||
private String allow = null;
|
||||
}
|
@@ -0,0 +1,924 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version
|
||||
* 2.1 of the License, or (at your option) any later version.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.gnu.org/licenses/lgpl.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.property;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import javax.faces.event.FacesEvent;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.cmr.search.ResultSet;
|
||||
import org.alfresco.service.cmr.search.SearchService;
|
||||
import org.alfresco.service.namespace.NamespaceService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.DataDictionary;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* Base class for all association editor components
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public abstract class BaseAssociationEditor extends UIInput
|
||||
{
|
||||
private final static Log logger = LogFactory.getLog(BaseAssociationEditor.class);
|
||||
|
||||
private final static String ACTION_SEPARATOR = ";";
|
||||
private final static int ACTION_NONE = -1;
|
||||
private final static int ACTION_REMOVE = 0;
|
||||
private final static int ACTION_SELECT = 1;
|
||||
private final static int ACTION_ADD = 2;
|
||||
private final static int ACTION_CHANGE = 3;
|
||||
private final static int ACTION_CANCEL = 4;
|
||||
private final static int ACTION_SEARCH = 5;
|
||||
private final static int ACTION_SET = 6;
|
||||
|
||||
private final static String MSG_ERROR_ASSOC = "error_association";
|
||||
private final static String FIELD_CONTAINS = "_contains";
|
||||
private final static String FIELD_AVAILABLE = "_available";
|
||||
|
||||
/** I18N message strings */
|
||||
private final static String MSG_ADD_TO_LIST_BUTTON = "add_to_list_button";
|
||||
private final static String MSG_SELECT_BUTTON = "select_button";
|
||||
private static final String MSG_NO_SELECTED_ITEMS = "no_selected_items";
|
||||
private final static String MSG_SEARCH_SELECT_ITEMS = "search_select_items";
|
||||
private final static String MSG_SEARCH_SELECT_ITEM = "search_select_item";
|
||||
private final static String MSG_SELECTED_ITEMS = "selected_items";
|
||||
private final static String MSG_REMOVE = "remove";
|
||||
private final static String MSG_ADD = "add";
|
||||
private final static String MSG_OK = "ok";
|
||||
private final static String MSG_CANCEL = "cancel";
|
||||
private final static String MSG_SEARCH = "search";
|
||||
private final static String MSG_CHANGE = "change";
|
||||
|
||||
protected String associationName;
|
||||
protected String availableOptionsSize;
|
||||
protected String selectItemMsg;
|
||||
protected String selectItemsMsg;
|
||||
protected String selectedItemsMsg;
|
||||
protected String noSelectedItemsMsg;
|
||||
protected Boolean disabled;
|
||||
|
||||
protected boolean showAvailable = false;
|
||||
|
||||
/** Map of the original associations keyed by the id of the child */
|
||||
protected Map<String, Object> originalAssocs;
|
||||
protected Map<String, Object> added;
|
||||
protected Map<String, Object> removed;
|
||||
|
||||
/** List containing the currently available options */
|
||||
protected List<NodeRef> availableOptions;
|
||||
|
||||
protected String changingAssociation;
|
||||
protected boolean highlightedRow;
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public BaseAssociationEditor()
|
||||
{
|
||||
setRendererType(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.associationName = (String)values[1];
|
||||
this.originalAssocs = (Map<String, Object>)values[2];
|
||||
this.availableOptions = (List<NodeRef>)values[3];
|
||||
this.availableOptionsSize = (String)values[4];
|
||||
this.selectItemMsg = (String)values[5];
|
||||
this.selectItemsMsg = (String)values[6];
|
||||
this.selectedItemsMsg = (String)values[7];
|
||||
this.changingAssociation = (String)values[8];
|
||||
this.disabled = (Boolean)values[9];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[10];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.associationName;
|
||||
values[2] = this.originalAssocs;
|
||||
values[3] = this.availableOptions;
|
||||
values[4] = this.availableOptionsSize;
|
||||
values[5] = this.selectItemMsg;
|
||||
values[6] = this.selectItemsMsg;
|
||||
values[7] = this.selectedItemsMsg;
|
||||
values[8] = this.changingAssociation;
|
||||
values[9] = this.disabled;
|
||||
|
||||
// NOTE: we don't save the state of the added and removed maps as these
|
||||
// need to be rebuilt everytime
|
||||
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#decode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void decode(FacesContext context)
|
||||
{
|
||||
Map requestMap = context.getExternalContext().getRequestParameterMap();
|
||||
Map valuesMap = context.getExternalContext().getRequestParameterValuesMap();
|
||||
String fieldId = getHiddenFieldName();
|
||||
String value = (String)requestMap.get(fieldId);
|
||||
|
||||
int action = ACTION_NONE;
|
||||
String removeId = null;
|
||||
if (value != null && value.length() != 0)
|
||||
{
|
||||
// break up the action into it's parts
|
||||
int sepIdx = value.indexOf(ACTION_SEPARATOR);
|
||||
if (sepIdx != -1)
|
||||
{
|
||||
action = Integer.parseInt(value.substring(0, sepIdx));
|
||||
removeId = value.substring(sepIdx+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
action = Integer.parseInt(value);
|
||||
}
|
||||
}
|
||||
|
||||
// gather the current state and queue an event
|
||||
String[] addedItems = (String[])valuesMap.get(fieldId + FIELD_AVAILABLE);
|
||||
String contains = (String)requestMap.get(fieldId + FIELD_CONTAINS);
|
||||
|
||||
AssocEditorEvent event = new AssocEditorEvent(this, action, addedItems, removeId, contains);
|
||||
queueEvent(event);
|
||||
|
||||
super.decode(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#broadcast(javax.faces.event.FacesEvent)
|
||||
*/
|
||||
public void broadcast(FacesEvent event) throws AbortProcessingException
|
||||
{
|
||||
if (event instanceof AssocEditorEvent)
|
||||
{
|
||||
AssocEditorEvent assocEvent = (AssocEditorEvent)event;
|
||||
Node node = (Node)getValue();
|
||||
|
||||
switch (assocEvent.Action)
|
||||
{
|
||||
case ACTION_SEARCH:
|
||||
{
|
||||
this.showAvailable = true;
|
||||
this.availableOptions = new ArrayList<NodeRef>();
|
||||
getAvailableOptions(FacesContext.getCurrentInstance(), assocEvent.Contains);
|
||||
break;
|
||||
}
|
||||
case ACTION_SELECT:
|
||||
{
|
||||
this.showAvailable = true;
|
||||
break;
|
||||
}
|
||||
case ACTION_ADD:
|
||||
{
|
||||
addTarget(node, assocEvent.ToAdd);
|
||||
break;
|
||||
}
|
||||
case ACTION_REMOVE:
|
||||
{
|
||||
removeTarget(node, assocEvent.RemoveId);
|
||||
break;
|
||||
}
|
||||
case ACTION_CHANGE:
|
||||
{
|
||||
this.changingAssociation = assocEvent.RemoveId;
|
||||
this.showAvailable = true;
|
||||
break;
|
||||
}
|
||||
case ACTION_CANCEL:
|
||||
{
|
||||
this.showAvailable = false;
|
||||
break;
|
||||
}
|
||||
case ACTION_SET:
|
||||
{
|
||||
if (assocEvent.ToAdd != null && assocEvent.ToAdd.length > 0)
|
||||
{
|
||||
removeTarget(node, this.changingAssociation);
|
||||
addTarget(node, assocEvent.ToAdd);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// reset the highlighted row flag
|
||||
this.highlightedRow = false;
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
String clientId = getClientId(context);
|
||||
|
||||
// get the child associations currently on the node and any that have been added
|
||||
NodeService nodeService = Repository.getServiceRegistry(context).getNodeService();
|
||||
|
||||
// show the editable association component
|
||||
AssociationDefinition assocDef = getAssociationDefinition(context);
|
||||
if (assocDef == null)
|
||||
{
|
||||
logger.warn("Failed to find association definition for association '" + associationName + "'");
|
||||
|
||||
// add an error message as the property is not defined in the data dictionary
|
||||
String msg = MessageFormat.format(Application.getMessage(context, MSG_ERROR_ASSOC), new Object[] {this.associationName});
|
||||
Utils.addErrorMessage(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
String targetType = assocDef.getTargetClass().getName().toString();
|
||||
boolean allowMany = assocDef.isTargetMany();
|
||||
|
||||
populateAssocationMaps((Node)getValue());
|
||||
|
||||
if (isDisabled())
|
||||
{
|
||||
// show the current list of associations in a read-only form
|
||||
renderReadOnlyAssociations(context, out, nodeService);
|
||||
}
|
||||
else
|
||||
{
|
||||
// start outer table
|
||||
out.write("<table border='0' cellspacing='4' cellpadding='0' class='selector'>");
|
||||
|
||||
if (allowMany)
|
||||
{
|
||||
out.write("<tr><td colspan='2'>1. ");
|
||||
out.write(getSelectItemsMsg());
|
||||
out.write("</td></tr>");
|
||||
|
||||
// show the search field
|
||||
renderSearchField(context, out);
|
||||
|
||||
// show available options for this association
|
||||
renderAvailableOptions(context, out, nodeService, targetType, allowMany);
|
||||
|
||||
// add the Add to List button
|
||||
out.write("<tr><td colspan='2'>2. <input type='submit' value='");
|
||||
out.write(Application.getMessage(context, MSG_ADD_TO_LIST_BUTTON));
|
||||
out.write("' onclick=\"");
|
||||
out.write(generateFormSubmit(context, Integer.toString(ACTION_ADD)));
|
||||
out.write("\"/>");
|
||||
|
||||
// add some padding
|
||||
out.write("<tr><td height='6'></td></tr>");
|
||||
|
||||
out.write("<tr><td colspan='2'>");
|
||||
out.write(getSelectedItemsMsg());
|
||||
out.write("</td></tr>");
|
||||
|
||||
// show all the current associations
|
||||
out.write("<tr><td colspan='2'><table cellspacing='0' cellpadding='2' border='0' class='selectedItems'>");
|
||||
out.write("<tr><td colspan='2' class='selectedItemsHeader'>");
|
||||
out.write(Application.getMessage(context, "name"));
|
||||
out.write("</td></tr>");
|
||||
renderExistingAssociations(context, out, nodeService, allowMany);
|
||||
out.write("</table></td></tr>");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this.showAvailable)
|
||||
{
|
||||
out.write("<tr><td colspan='2'>1. ");
|
||||
out.write(getSelectItemMsg());
|
||||
out.write("</td></tr>");
|
||||
|
||||
// show the search field
|
||||
renderSearchField(context, out);
|
||||
|
||||
// show available options for this association
|
||||
renderAvailableOptions(context, out, nodeService, targetType, allowMany);
|
||||
|
||||
// add the ok and cancel buttons
|
||||
out.write("<tr><td colspan='2' align='right'><input type='submit' value='");
|
||||
out.write(Application.getMessage(context, MSG_OK));
|
||||
out.write("' onclick=\"");
|
||||
out.write(generateFormSubmit(context, Integer.toString(ACTION_SET)));
|
||||
out.write("\"/> <input type='submit' value='");
|
||||
out.write(Application.getMessage(context, MSG_CANCEL));
|
||||
out.write("' onclick=\"");
|
||||
out.write(generateFormSubmit(context, Integer.toString(ACTION_CANCEL)));
|
||||
out.write("\"/></td></tr>");
|
||||
}
|
||||
else
|
||||
{
|
||||
// show the select button if required
|
||||
if ((allowMany == false && this.originalAssocs.size() == 0 && this.added.size() == 0) ||
|
||||
(allowMany == false && this.originalAssocs.size() == 1 && this.removed.size() == 1 && this.added.size() == 0) )
|
||||
{
|
||||
out.write("<tr><td><input type='submit' value='");
|
||||
out.write(Application.getMessage(context, MSG_SELECT_BUTTON));
|
||||
out.write("' onclick=\"");
|
||||
out.write(generateFormSubmit(context, Integer.toString(ACTION_SELECT)));
|
||||
out.write("\"/></td></tr>");
|
||||
}
|
||||
else
|
||||
{
|
||||
// show the current association
|
||||
renderExistingAssociations(context, out, nodeService, allowMany);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("number original = " + this.originalAssocs.size());
|
||||
logger.debug("number added = " + this.added.size());
|
||||
logger.debug("number removed = " + this.removed.size());
|
||||
}
|
||||
|
||||
// close table
|
||||
out.write("</table>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the association this component is editing
|
||||
*
|
||||
* @return Association name
|
||||
*/
|
||||
public String getAssociationName()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("associationName");
|
||||
if (vb != null)
|
||||
{
|
||||
this.associationName = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.associationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of the association this component will edit
|
||||
*
|
||||
* @param associationName Name of the association to edit
|
||||
*/
|
||||
public void setAssociationName(String associationName)
|
||||
{
|
||||
this.associationName = associationName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the component should be rendered in a disabled state
|
||||
*
|
||||
* @return Returns whether the component is disabled
|
||||
*/
|
||||
public boolean isDisabled()
|
||||
{
|
||||
if (this.disabled == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("disabled");
|
||||
if (vb != null)
|
||||
{
|
||||
this.disabled = (Boolean)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.disabled == null)
|
||||
{
|
||||
this.disabled = Boolean.FALSE;
|
||||
}
|
||||
|
||||
return this.disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the component should be rendered in a disabled state
|
||||
*
|
||||
* @param disabled true to disable the component
|
||||
*/
|
||||
public void setDisabled(boolean disabled)
|
||||
{
|
||||
this.disabled = disabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the size of the select control when multiple items
|
||||
* can be selected
|
||||
*
|
||||
* @return The size of the select control
|
||||
*/
|
||||
public String getAvailableOptionsSize()
|
||||
{
|
||||
if (this.availableOptionsSize == null)
|
||||
{
|
||||
this.availableOptionsSize = "4";
|
||||
}
|
||||
|
||||
return this.availableOptionsSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size of the select control used when multiple items can
|
||||
* be selected
|
||||
*
|
||||
* @param availableOptionsSize The size
|
||||
*/
|
||||
public void setAvailableOptionsSize(String availableOptionsSize)
|
||||
{
|
||||
this.availableOptionsSize = availableOptionsSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to display when no items have been selected, if one hasn't been
|
||||
* set it defaults to the message in the bundle under key 'no_selected_items'.
|
||||
*
|
||||
* @return The message
|
||||
*/
|
||||
public String getNoSelectedItemsMsg()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("noSelectedItemsMsg");
|
||||
if (vb != null)
|
||||
{
|
||||
this.noSelectedItemsMsg = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.noSelectedItemsMsg == null)
|
||||
{
|
||||
this.noSelectedItemsMsg = Application.getMessage(getFacesContext(), MSG_NO_SELECTED_ITEMS);
|
||||
}
|
||||
|
||||
return this.noSelectedItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the no selected items message to display in the UI
|
||||
*
|
||||
* @param noSelectedItemsMsg The message
|
||||
*/
|
||||
public void setNoSelectedItemsMsg(String noSelectedItemsMsg)
|
||||
{
|
||||
this.noSelectedItemsMsg = noSelectedItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to display for the selected items, if one hasn't been
|
||||
* set it defaults to the message in the bundle under key 'selected_items'.
|
||||
*
|
||||
* @return The message
|
||||
*/
|
||||
public String getSelectedItemsMsg()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("selectedItemsMsg");
|
||||
if (vb != null)
|
||||
{
|
||||
this.selectedItemsMsg = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.selectedItemsMsg == null)
|
||||
{
|
||||
this.selectedItemsMsg = Application.getMessage(getFacesContext(), MSG_SELECTED_ITEMS);
|
||||
}
|
||||
|
||||
return this.selectedItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the selected items message to display in the UI
|
||||
*
|
||||
* @param selectedItemsMsg The message
|
||||
*/
|
||||
public void setSelectedItemsMsg(String selectedItemsMsg)
|
||||
{
|
||||
this.selectedItemsMsg = selectedItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to display for select an item, if one hasn't been
|
||||
* set it defaults to the message in the bundle under key 'search_select_item'.
|
||||
*
|
||||
* @return The message
|
||||
*/
|
||||
public String getSelectItemMsg()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("selectItemMsg");
|
||||
if (vb != null)
|
||||
{
|
||||
this.selectItemMsg = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.selectItemMsg == null)
|
||||
{
|
||||
this.selectItemMsg = Application.getMessage(getFacesContext(), MSG_SEARCH_SELECT_ITEM);
|
||||
}
|
||||
|
||||
return this.selectItemMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the select an item message to display in the UI
|
||||
*
|
||||
* @param selectedItemMsg The message
|
||||
*/
|
||||
public void setSelectItemMsg(String selectItemMsg)
|
||||
{
|
||||
this.selectItemMsg = selectItemMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the message to display for select items, if one hasn't been
|
||||
* set it defaults to the message in the bundle under key 'search_select_items'.
|
||||
*
|
||||
* @return The message
|
||||
*/
|
||||
public String getSelectItemsMsg()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("selectItemsMsg");
|
||||
if (vb != null)
|
||||
{
|
||||
this.selectItemsMsg = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
if (this.selectItemsMsg == null)
|
||||
{
|
||||
this.selectItemsMsg = Application.getMessage(getFacesContext(), MSG_SEARCH_SELECT_ITEMS);
|
||||
}
|
||||
|
||||
return this.selectItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the select items message to display in the UI
|
||||
*
|
||||
* @param selectedItemsMsg The message
|
||||
*/
|
||||
public void setSelectItemsMsg(String selectItemsMsg)
|
||||
{
|
||||
this.selectItemsMsg = selectItemsMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* Populates all the internal Maps with the appropriate association reference objects
|
||||
*
|
||||
* @param node The Node we are dealing with
|
||||
*/
|
||||
protected abstract void populateAssocationMaps(Node node);
|
||||
|
||||
/**
|
||||
* Renders the existing associations in a read-only form
|
||||
*
|
||||
* @param context FacesContext
|
||||
* @param out ResponseWriter
|
||||
* @param nodeService The NodeService
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract void renderReadOnlyAssociations(FacesContext context, ResponseWriter out,
|
||||
NodeService nodeService) throws IOException;
|
||||
|
||||
/**
|
||||
* Renders the existing associations in an editable form
|
||||
*
|
||||
* @param context FacesContext
|
||||
* @param out ResponseWriter
|
||||
* @param nodeService The NodeService
|
||||
* @param allowMany Whether multiple associations are allowed
|
||||
* @throws IOException
|
||||
*/
|
||||
protected abstract void renderExistingAssociations(FacesContext context, ResponseWriter out,
|
||||
NodeService nodeService, boolean allowMany) throws IOException;
|
||||
|
||||
/**
|
||||
* Updates the component and node state to reflect an association being removed
|
||||
*
|
||||
* @param node The node we are dealing with
|
||||
* @param targetId The id of the child to remove
|
||||
*/
|
||||
protected abstract void removeTarget(Node node, String targetId);
|
||||
|
||||
/**
|
||||
* Updates the component and node state to reflect an association being added
|
||||
*
|
||||
* @param node The node we are dealing with
|
||||
* @param childId The id of the child to add
|
||||
*/
|
||||
protected abstract void addTarget(Node node, String[] toAdd);
|
||||
|
||||
/**
|
||||
* Renders an existing association with the appropriate options
|
||||
*
|
||||
* @param context FacesContext
|
||||
* @param out Writer to write output to
|
||||
* @param nodeService The NodeService
|
||||
* @param targetRef The node at the end of the association being rendered
|
||||
* @param allowMany Whether the current association allows multiple children
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void renderExistingAssociation(FacesContext context, ResponseWriter out, NodeService nodeService,
|
||||
NodeRef targetRef, boolean allowMany) throws IOException
|
||||
{
|
||||
out.write("<tr><td class='");
|
||||
if (this.highlightedRow)
|
||||
{
|
||||
out.write("selectedItemsRowAlt");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write("selectedItemsRow");
|
||||
}
|
||||
out.write("'>");
|
||||
|
||||
out.write(Repository.getDisplayPath(nodeService.getPath(targetRef)));
|
||||
out.write("/");
|
||||
out.write(Repository.getNameForNode(nodeService, targetRef));
|
||||
out.write("</td><td class='");
|
||||
if (this.highlightedRow)
|
||||
{
|
||||
out.write("selectedItemsRowAlt");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write("selectedItemsRow");
|
||||
}
|
||||
out.write("'><a href='#' title='");
|
||||
out.write(Application.getMessage(context, MSG_REMOVE));
|
||||
out.write("' onclick=\"");
|
||||
out.write(generateFormSubmit(context, ACTION_REMOVE + ACTION_SEPARATOR + targetRef.getId()));
|
||||
out.write("\"><img src='");
|
||||
out.write(context.getExternalContext().getRequestContextPath());
|
||||
out.write("/images/icons/delete.gif' border='0' width='13' height='16'/></a>");
|
||||
|
||||
if (allowMany == false)
|
||||
{
|
||||
out.write(" <a href='#' title='");
|
||||
out.write(Application.getMessage(context, MSG_CHANGE));
|
||||
out.write("' onclick=\"");
|
||||
out.write(generateFormSubmit(context, ACTION_CHANGE + ACTION_SEPARATOR + targetRef.getId()));
|
||||
out.write("\"><img src='");
|
||||
out.write(context.getExternalContext().getRequestContextPath());
|
||||
out.write("/images/icons/edit_icon.gif' border='0' width='12' height='16'/></a>");
|
||||
}
|
||||
|
||||
out.write("</td></tr>");
|
||||
|
||||
this.highlightedRow = !this.highlightedRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the search fields
|
||||
*
|
||||
* @param context Faces Context
|
||||
* @param out The Response Writer
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void renderSearchField(FacesContext context, ResponseWriter out) throws IOException
|
||||
{
|
||||
// TODO: externalise the max and size attributes
|
||||
out.write("<tr><td colspan='2'><input type='text' maxlength='1024' size='32' name='");
|
||||
out.write(getClientId(context) + FIELD_CONTAINS);
|
||||
out.write("'/> <input type='submit' value='");
|
||||
out.write(Application.getMessage(context, MSG_SEARCH));
|
||||
out.write("' onclick=\"");
|
||||
out.write(generateFormSubmit(context, Integer.toString(ACTION_SEARCH)));
|
||||
out.write("\"/></td></tr>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the <None> message
|
||||
*
|
||||
* @param context Faces Context
|
||||
* @param out Response Writer
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void renderNone(FacesContext context, ResponseWriter out) throws IOException
|
||||
{
|
||||
out.write("<tr><td class='selectedItemsRow'>");
|
||||
out.write(getNoSelectedItemsMsg());
|
||||
out.write("</td></tr>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the list of available options for a new association
|
||||
*
|
||||
* @param context FacesContext
|
||||
* @param out Writer to write output to
|
||||
* @param nodeService The NodeService
|
||||
* @param targetType The type of the child at the end of the association
|
||||
* @param allowMany Whether the current association allows multiple children
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void renderAvailableOptions(FacesContext context, ResponseWriter out, NodeService nodeService,
|
||||
String targetType, boolean allowMany) throws IOException
|
||||
{
|
||||
boolean itemsPresent = (this.availableOptions != null && this.availableOptions.size() > 0);
|
||||
|
||||
out.write("<tr><td colspan='2'><select ");
|
||||
if (itemsPresent == false)
|
||||
{
|
||||
// rather than having a very slim select box set the width if there are no results
|
||||
out.write("style='width:240px;' ");
|
||||
}
|
||||
out.write("name='");
|
||||
out.write(getClientId(context) + FIELD_AVAILABLE);
|
||||
out.write("' size='");
|
||||
if (allowMany)
|
||||
{
|
||||
out.write(getAvailableOptionsSize());
|
||||
out.write("' multiple");
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write("1'");
|
||||
}
|
||||
out.write(">");
|
||||
|
||||
if (itemsPresent)
|
||||
{
|
||||
Node currentNode = (Node)getValue();
|
||||
for (NodeRef item : this.availableOptions)
|
||||
{
|
||||
// NOTE: only show the items that are not already associated to and don't show the current node
|
||||
if ((this.originalAssocs.containsKey(item.getId()) == false && this.added.containsKey(item.getId()) == false &&
|
||||
item.getId().equals(currentNode.getId()) == false) ||
|
||||
this.removed.containsKey(item.getId()))
|
||||
{
|
||||
out.write("<option value='");
|
||||
out.write(item.getId());
|
||||
out.write("'>");
|
||||
out.write(Repository.getDisplayPath(nodeService.getPath(item)));
|
||||
out.write("/");
|
||||
out.write(Repository.getNameForNode(nodeService, item));
|
||||
out.write("</option>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
out.write("</select></td></tr>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the AssociationDefinition for the association we are representing
|
||||
*
|
||||
* @param context Faces Context
|
||||
* @return The AssociationDefinition for the association, null if a definition does not exist
|
||||
*/
|
||||
protected AssociationDefinition getAssociationDefinition(FacesContext context)
|
||||
{
|
||||
// get some metadata about the association from the data dictionary
|
||||
DataDictionary dd = (DataDictionary)FacesContextUtils.getRequiredWebApplicationContext(
|
||||
context).getBean(Application.BEAN_DATA_DICTIONARY);
|
||||
return dd.getAssociationDefinition((Node)getValue(), this.associationName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the available options for the current association
|
||||
*
|
||||
* @param context Faces Context
|
||||
* @param contains The contains part of the query
|
||||
*/
|
||||
protected void getAvailableOptions(FacesContext context, String contains)
|
||||
{
|
||||
AssociationDefinition assocDef = getAssociationDefinition(context);
|
||||
if (assocDef != null)
|
||||
{
|
||||
// find and show all the available options for the current association
|
||||
StringBuilder query = new StringBuilder("+TYPE:\"");
|
||||
query.append(assocDef.getTargetClass().getName().toString());
|
||||
query.append("\"");
|
||||
|
||||
if (contains != null && contains.length() > 0)
|
||||
{
|
||||
String safeContains = Utils.remove(contains.trim(), "\"");
|
||||
String nameAttr = Repository.escapeQName(QName.createQName(NamespaceService.CONTENT_MODEL_1_0_URI, "name"));
|
||||
|
||||
query.append(" AND +@");
|
||||
query.append(nameAttr);
|
||||
query.append(":*" + safeContains + "*");
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Query: " + query.toString());
|
||||
|
||||
ResultSet results = null;
|
||||
try
|
||||
{
|
||||
results = Repository.getServiceRegistry(context).getSearchService().query(
|
||||
Repository.getStoreRef(), SearchService.LANGUAGE_LUCENE, query.toString());
|
||||
this.availableOptions = results.getNodeRefs();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (results != null)
|
||||
{
|
||||
results.close();
|
||||
}
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Found " + this.availableOptions.size() + " available options");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We use a hidden field per picker instance on the page.
|
||||
*
|
||||
* @return hidden field name
|
||||
*/
|
||||
private String getHiddenFieldName()
|
||||
{
|
||||
return getClientId(getFacesContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate FORM submit JavaScript for the specified action
|
||||
*
|
||||
* @param context FacesContext
|
||||
* @param action Action string
|
||||
*
|
||||
* @return FORM submit JavaScript
|
||||
*/
|
||||
private String generateFormSubmit(FacesContext context, String action)
|
||||
{
|
||||
return Utils.generateFormSubmit(context, this, getHiddenFieldName(), action);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing an action relevant to the AssociationEditor component.
|
||||
*/
|
||||
public static class AssocEditorEvent extends ActionEvent
|
||||
{
|
||||
private static final long serialVersionUID = 7346758616063937703L;
|
||||
|
||||
public int Action;
|
||||
public String[] ToAdd;
|
||||
public String RemoveId;
|
||||
public String Contains;
|
||||
|
||||
public AssocEditorEvent(UIComponent component, int action, String[] toAdd, String removeId, String contains)
|
||||
{
|
||||
super(component);
|
||||
this.Action = action;
|
||||
this.ToAdd = toAdd;
|
||||
this.RemoveId = removeId;
|
||||
this.Contains = contains;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,247 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.property;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.component.UIOutput;
|
||||
import javax.faces.component.UIPanel;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.DataDictionary;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for all items that can appear in a property sheet component
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public abstract class PropertySheetItem extends UIPanel implements NamingContainer
|
||||
{
|
||||
private static Log logger = LogFactory.getLog(PropertySheetItem.class);
|
||||
|
||||
protected String name;
|
||||
protected String displayLabel;
|
||||
protected String converter;
|
||||
protected Boolean readOnly;
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
// get the variable being used from the parent
|
||||
UIComponent parent = this.getParent();
|
||||
if ((parent instanceof UIPropertySheet) == false)
|
||||
{
|
||||
throw new IllegalStateException(getIncorrectParentMsg());
|
||||
}
|
||||
|
||||
// only build the components if there are currently no children
|
||||
int howManyKids = getChildren().size();
|
||||
if (howManyKids == 0)
|
||||
{
|
||||
UIPropertySheet propSheet = (UIPropertySheet)parent;
|
||||
generateItem(context, propSheet.getNode(), propSheet.getVar());
|
||||
}
|
||||
|
||||
super.encodeBegin(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the display label
|
||||
*/
|
||||
public String getDisplayLabel()
|
||||
{
|
||||
if (this.displayLabel == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("displayLabel");
|
||||
if (vb != null)
|
||||
{
|
||||
this.displayLabel = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
return this.displayLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param displayLabel Sets the display label
|
||||
*/
|
||||
public void setDisplayLabel(String displayLabel)
|
||||
{
|
||||
this.displayLabel = displayLabel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the name
|
||||
*/
|
||||
public String getName()
|
||||
{
|
||||
if (this.name == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("name");
|
||||
if (vb != null)
|
||||
{
|
||||
this.name = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param name Sets the name
|
||||
*/
|
||||
public void setName(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the converter
|
||||
*/
|
||||
public String getConverter()
|
||||
{
|
||||
if (this.converter == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("converter");
|
||||
if (vb != null)
|
||||
{
|
||||
this.converter = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
return this.converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param converter Sets the converter
|
||||
*/
|
||||
public void setConverter(String converter)
|
||||
{
|
||||
this.converter = converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns whether the property is read only
|
||||
*/
|
||||
public boolean isReadOnly()
|
||||
{
|
||||
if (this.readOnly == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("readOnly");
|
||||
if (vb != null)
|
||||
{
|
||||
this.readOnly = (Boolean)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.readOnly == null)
|
||||
{
|
||||
this.readOnly = Boolean.FALSE;
|
||||
}
|
||||
|
||||
return this.readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param readOnly Sets the read only flag for the component
|
||||
*/
|
||||
public void setReadOnly(boolean readOnly)
|
||||
{
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.name = (String)values[1];
|
||||
this.displayLabel = (String)values[2];
|
||||
this.readOnly = (Boolean)values[3];
|
||||
this.converter = (String)values[4];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[5];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.name;
|
||||
values[2] = this.displayLabel;
|
||||
values[3] = this.readOnly;
|
||||
values[4] = this.converter;
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the label and control for the item
|
||||
*
|
||||
* @param context FacesContext
|
||||
* @param node The Node we are displaying the property sheet for
|
||||
* @param var The variable name used to store the Node in the session
|
||||
*/
|
||||
protected abstract void generateItem(FacesContext context, Node node, String var)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Returns the message to use in the exception that is thrown if the component
|
||||
* is not nested inside a PropertySheet component
|
||||
*
|
||||
* @return The message
|
||||
*/
|
||||
protected abstract String getIncorrectParentMsg();
|
||||
|
||||
/**
|
||||
* Generates a JSF OutputText component/renderer
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param displayLabel The display label text
|
||||
* @param parent The parent component for the label
|
||||
*/
|
||||
protected void generateLabel(FacesContext context, String displayLabel)
|
||||
{
|
||||
UIOutput label = (UIOutput)context.getApplication().
|
||||
createComponent("javax.faces.Output");
|
||||
label.setId(context.getViewRoot().createUniqueId());
|
||||
label.setRendererType("javax.faces.Text");
|
||||
label.setValue(displayLabel + ": ");
|
||||
this.getChildren().add(label);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created label " + label.getClientId(context) +
|
||||
" for '" + displayLabel + "' and added it to component " + this);
|
||||
}
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.property;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.component.UIOutput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.convert.Converter;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.DataDictionary;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* Component to represent an individual association within a property sheet
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UIAssociation extends PropertySheetItem
|
||||
{
|
||||
private static final String MSG_ERROR_ASSOC = "error_association";
|
||||
private static final String MSG_ERROR_NOT_ASSOC = "error_not_association";
|
||||
|
||||
private static Log logger = LogFactory.getLog(UIAssociation.class);
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public UIAssociation()
|
||||
{
|
||||
// set the default renderer
|
||||
setRendererType("org.alfresco.faces.AssociationRenderer");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.Association";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.PropertySheetItem#getIncorrectParentMsg()
|
||||
*/
|
||||
protected String getIncorrectParentMsg()
|
||||
{
|
||||
return "The association component must be nested within a property sheet component";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.PropertySheetItem#generateItem(javax.faces.context.FacesContext, org.alfresco.web.bean.repository.Node, java.lang.String)
|
||||
*/
|
||||
protected void generateItem(FacesContext context, Node node, String var) throws IOException
|
||||
{
|
||||
String associationName = (String)getName();
|
||||
|
||||
// get details of the association
|
||||
DataDictionary dd = (DataDictionary)FacesContextUtils.getRequiredWebApplicationContext(
|
||||
context).getBean(Application.BEAN_DATA_DICTIONARY);
|
||||
AssociationDefinition assocDef = dd.getAssociationDefinition(node, associationName);
|
||||
|
||||
if (assocDef == null)
|
||||
{
|
||||
logger.warn("Failed to find association definition for association '" + associationName + "'");
|
||||
|
||||
// add an error message as the property is not defined in the data dictionary and
|
||||
// not in the node's set of properties
|
||||
String msg = MessageFormat.format(Application.getMessage(context, MSG_ERROR_ASSOC), new Object[] {associationName});
|
||||
Utils.addErrorMessage(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we've found the association definition but we also need to check
|
||||
// that the association is a parent child one
|
||||
if (assocDef.isChild())
|
||||
{
|
||||
String msg = MessageFormat.format(Application.getMessage(context, MSG_ERROR_NOT_ASSOC), new Object[] {associationName});
|
||||
Utils.addErrorMessage(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
String displayLabel = (String)getDisplayLabel();
|
||||
if (displayLabel == null)
|
||||
{
|
||||
// try and get the repository assigned label
|
||||
displayLabel = assocDef.getTitle();
|
||||
|
||||
// if the label is still null default to the local name of the property
|
||||
if (displayLabel == null)
|
||||
{
|
||||
displayLabel = assocDef.getName().getLocalName();
|
||||
}
|
||||
}
|
||||
|
||||
// generate the label and type specific control
|
||||
generateLabel(context, displayLabel);
|
||||
generateControl(context, assocDef, var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an appropriate control for the given property
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param propDef The definition of the association to create the control for
|
||||
* @param varName Name of the variable the node is stored in the session as
|
||||
* (used for value binding expression)
|
||||
* @param parent The parent component for the control
|
||||
*/
|
||||
private void generateControl(FacesContext context, AssociationDefinition assocDef,
|
||||
String varName)
|
||||
{
|
||||
UIPropertySheet propSheet = (UIPropertySheet)this.getParent();
|
||||
ValueBinding vb = context.getApplication().createValueBinding("#{" + varName + "}");
|
||||
|
||||
UIAssociationEditor control = (UIAssociationEditor)context.
|
||||
getApplication().createComponent("org.alfresco.faces.AssociationEditor");
|
||||
control.setAssociationName(assocDef.getName().toString());
|
||||
|
||||
// set up the common aspects of the control
|
||||
control.setId(context.getViewRoot().createUniqueId());
|
||||
control.setValueBinding("value", vb);
|
||||
|
||||
// disable the component if necessary
|
||||
if (propSheet.getMode().equalsIgnoreCase(UIPropertySheet.VIEW_MODE) || isReadOnly() || assocDef.isProtected())
|
||||
{
|
||||
control.setDisabled(true);
|
||||
}
|
||||
|
||||
// add the control itself
|
||||
this.getChildren().add(control);
|
||||
}
|
||||
}
|
@@ -0,0 +1,242 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version
|
||||
* 2.1 of the License, or (at your option) any later version.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.gnu.org/licenses/lgpl.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.property;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Component that allows associations to be edited
|
||||
* i.e. new associations to be added, existing ones to be
|
||||
* removed whilst following the rules in the data dictionary
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UIAssociationEditor extends BaseAssociationEditor
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(UIAssociationEditor.class);
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.AssociationEditor";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.BaseAssociationEditor#populateAssocationMaps(org.alfresco.web.bean.repository.Node)
|
||||
*/
|
||||
protected void populateAssocationMaps(Node node)
|
||||
{
|
||||
// we need to remember the original set of associations (if there are any)
|
||||
// and place them in a map keyed by the id of the child node
|
||||
if (this.originalAssocs == null)
|
||||
{
|
||||
this.originalAssocs = new HashMap<String, Object>();
|
||||
|
||||
List assocs = (List)node.getAssociations().get(this.associationName);
|
||||
if (assocs != null)
|
||||
{
|
||||
Iterator iter = assocs.iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
AssociationRef assoc = (AssociationRef)iter.next();
|
||||
|
||||
// add the association to the map
|
||||
this.originalAssocs.put(assoc.getTargetRef().getId(), assoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the map of added associations for this node and association type
|
||||
this.added = (Map)node.getAddedAssociations().get(this.associationName);
|
||||
if (added == null)
|
||||
{
|
||||
// if there aren't any added associations for 'associationName' create a map and add it
|
||||
added = new HashMap<String, Object>();
|
||||
node.getAddedAssociations().put(this.associationName, (Map)added);
|
||||
}
|
||||
|
||||
// get the map of removed associations for this node and association type
|
||||
this.removed = (Map)node.getRemovedAssociations().get(this.associationName);
|
||||
if (removed == null)
|
||||
{
|
||||
// if there aren't any added associations for 'associationName' create a map and add it
|
||||
removed = new HashMap<String, Object>();
|
||||
node.getRemovedAssociations().put(this.associationName, (Map)removed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.BaseAssociationEditor#renderExistingAssociations(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, org.alfresco.service.cmr.repository.NodeService, boolean)
|
||||
*/
|
||||
protected void renderExistingAssociations(FacesContext context, ResponseWriter out,
|
||||
NodeService nodeService, boolean allowManyChildren) throws IOException
|
||||
{
|
||||
boolean itemsRendered = false;
|
||||
|
||||
// show the associations from the original list if they are not in the removed list
|
||||
Iterator iter = this.originalAssocs.values().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
AssociationRef assoc = (AssociationRef)iter.next();
|
||||
if (removed.containsKey(assoc.getTargetRef().getId()) == false)
|
||||
{
|
||||
renderExistingAssociation(context, out, nodeService, assoc.getTargetRef(), allowManyChildren);
|
||||
itemsRendered = true;
|
||||
}
|
||||
}
|
||||
|
||||
// also show any associations added in this session
|
||||
iter = this.added.values().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
AssociationRef assoc = (AssociationRef)iter.next();
|
||||
renderExistingAssociation(context, out, nodeService, assoc.getTargetRef(), allowManyChildren);
|
||||
itemsRendered = true;
|
||||
}
|
||||
|
||||
// show the none selected message if no items were rendered
|
||||
if (itemsRendered == false && allowManyChildren == true)
|
||||
{
|
||||
renderNone(context, out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.BaseAssociationEditor#renderReadOnlyAssociations(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, org.alfresco.service.cmr.repository.NodeService)
|
||||
*/
|
||||
protected void renderReadOnlyAssociations(FacesContext context, ResponseWriter out, NodeService nodeService) throws IOException
|
||||
{
|
||||
if (this.originalAssocs.size() > 0)
|
||||
{
|
||||
out.write("<table cellspacing='0' cellpadding='2' border='0'>");
|
||||
|
||||
Iterator iter = this.originalAssocs.values().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
out.write("<tr><td>");
|
||||
AssociationRef assoc = (AssociationRef)iter.next();
|
||||
out.write(Repository.getDisplayPath(nodeService.getPath(assoc.getTargetRef())));
|
||||
out.write("/");
|
||||
out.write(Repository.getNameForNode(nodeService, assoc.getTargetRef()));
|
||||
out.write("</td></tr>");
|
||||
}
|
||||
|
||||
out.write("</table>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the component and node state to reflect an association being removed
|
||||
*
|
||||
* @param node The node we are dealing with
|
||||
* @param targetId The id of the child to remove
|
||||
*/
|
||||
protected void removeTarget(Node node, String targetId)
|
||||
{
|
||||
if (node != null && targetId != null)
|
||||
{
|
||||
QName assocQName = Repository.resolveToQName(this.associationName);
|
||||
AssociationRef newAssoc = new AssociationRef(node.getNodeRef(), assocQName, new NodeRef(Repository.getStoreRef(), targetId));
|
||||
|
||||
// update the node so it knows to remove the association, but only if the association
|
||||
// was one of the original ones
|
||||
if (this.originalAssocs.containsKey(targetId))
|
||||
{
|
||||
Map<String, AssociationRef> removed = node.getRemovedAssociations().get(this.associationName);
|
||||
removed.put(targetId, newAssoc);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Added association to " + targetId + " to the removed list");
|
||||
}
|
||||
|
||||
// if this association was previously added in this session it will still be
|
||||
// in the added list so remove it if it is
|
||||
Map<String, AssociationRef> added = node.getAddedAssociations().get(this.associationName);
|
||||
if (added.containsKey(targetId))
|
||||
{
|
||||
added.remove(targetId);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Removed association to " + targetId + " from the added list");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the component and node state to reflect an association being added
|
||||
*
|
||||
* @param node The node we are dealing with
|
||||
* @param childId The id of the child to add
|
||||
*/
|
||||
protected void addTarget(Node node, String[] toAdd)
|
||||
{
|
||||
if (node != null && toAdd != null && toAdd.length > 0)
|
||||
{
|
||||
for (int x = 0; x < toAdd.length; x++)
|
||||
{
|
||||
String targetId = toAdd[x];
|
||||
|
||||
// update the node so it knows to add the association (if it wasn't there originally)
|
||||
if (this.originalAssocs.containsKey(targetId) == false)
|
||||
{
|
||||
QName assocQName = Repository.resolveToQName(this.associationName);
|
||||
AssociationRef newAssoc = new AssociationRef(node.getNodeRef(), assocQName,
|
||||
new NodeRef(Repository.getStoreRef(), targetId));
|
||||
|
||||
Map<String, AssociationRef> added = node.getAddedAssociations().get(this.associationName);
|
||||
added.put(targetId, newAssoc);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Added association to " + targetId + " to the added list");
|
||||
}
|
||||
|
||||
// if the association was previously removed and has now been re-added it
|
||||
// will still be in the "to be removed" list so remove it if it is
|
||||
Map<String, AssociationRef> removed = node.getRemovedAssociations().get(this.associationName);
|
||||
if (removed.containsKey(targetId))
|
||||
{
|
||||
removed.remove(targetId);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Removed association to " + targetId + " from the removed list");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.property;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.component.UIOutput;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.convert.Converter;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.AssociationDefinition;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.DataDictionary;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* Component to represent an individual child association within a property sheet
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UIChildAssociation extends PropertySheetItem
|
||||
{
|
||||
private static final String MSG_ERROR_CHILD_ASSOC = "error_child_association";
|
||||
private static final String MSG_ERROR_NOT_CHILD_ASSOC = "error_not_child_association";
|
||||
|
||||
private static Log logger = LogFactory.getLog(UIChildAssociation.class);
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public UIChildAssociation()
|
||||
{
|
||||
// set the default renderer
|
||||
setRendererType("org.alfresco.faces.ChildAssociationRenderer");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.ChildAssociation";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.PropertySheetItem#getIncorrectParentMsg()
|
||||
*/
|
||||
protected String getIncorrectParentMsg()
|
||||
{
|
||||
return "The childAssociation component must be nested within a property sheet component";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.PropertySheetItem#generateItem(javax.faces.context.FacesContext, org.alfresco.web.bean.repository.Node, java.lang.String)
|
||||
*/
|
||||
protected void generateItem(FacesContext context, Node node, String var) throws IOException
|
||||
{
|
||||
String associationName = (String)getName();
|
||||
|
||||
// get details of the association
|
||||
DataDictionary dd = (DataDictionary)FacesContextUtils.getRequiredWebApplicationContext(
|
||||
context).getBean(Application.BEAN_DATA_DICTIONARY);
|
||||
AssociationDefinition assocDef = dd.getAssociationDefinition(node, associationName);
|
||||
|
||||
if (assocDef == null)
|
||||
{
|
||||
logger.warn("Failed to find child association definition for association '" + associationName + "'");
|
||||
|
||||
// add an error message as the property is not defined in the data dictionary
|
||||
String msg = MessageFormat.format(Application.getMessage(context, MSG_ERROR_CHILD_ASSOC), new Object[] {associationName});
|
||||
Utils.addErrorMessage(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
// we've found the association definition but we also need to check
|
||||
// that the association is a parent child one
|
||||
if (assocDef.isChild() == false)
|
||||
{
|
||||
String msg = MessageFormat.format(Application.getMessage(context, MSG_ERROR_NOT_CHILD_ASSOC), new Object[] {associationName});
|
||||
Utils.addErrorMessage(msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
String displayLabel = (String)getDisplayLabel();
|
||||
if (displayLabel == null)
|
||||
{
|
||||
// try and get the repository assigned label
|
||||
displayLabel = assocDef.getTitle();
|
||||
|
||||
// if the label is still null default to the local name of the property
|
||||
if (displayLabel == null)
|
||||
{
|
||||
displayLabel = assocDef.getName().getLocalName();
|
||||
}
|
||||
}
|
||||
|
||||
// generate the label and type specific control
|
||||
generateLabel(context, displayLabel);
|
||||
generateControl(context, assocDef, var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an appropriate control for the given property
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param propDef The definition of the association to create the control for
|
||||
* @param varName Name of the variable the node is stored in the session as
|
||||
* (used for value binding expression)
|
||||
* @param parent The parent component for the control
|
||||
*/
|
||||
private void generateControl(FacesContext context, AssociationDefinition assocDef,
|
||||
String varName)
|
||||
{
|
||||
UIPropertySheet propSheet = (UIPropertySheet)this.getParent();
|
||||
ValueBinding vb = context.getApplication().createValueBinding("#{" + varName + "}");
|
||||
|
||||
UIChildAssociationEditor control = (UIChildAssociationEditor)context.
|
||||
getApplication().createComponent("org.alfresco.faces.ChildAssociationEditor");
|
||||
control.setAssociationName(assocDef.getName().toString());
|
||||
|
||||
// set up the common aspects of the control
|
||||
control.setId(context.getViewRoot().createUniqueId());
|
||||
control.setValueBinding("value", vb);
|
||||
|
||||
// disable the component if necessary
|
||||
if (propSheet.getMode().equalsIgnoreCase(UIPropertySheet.VIEW_MODE) || isReadOnly() || assocDef.isProtected())
|
||||
{
|
||||
control.setDisabled(true);
|
||||
}
|
||||
|
||||
// add the control itself
|
||||
this.getChildren().add(control);
|
||||
}
|
||||
}
|
@@ -0,0 +1,237 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version
|
||||
* 2.1 of the License, or (at your option) any later version.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.gnu.org/licenses/lgpl.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.property;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
|
||||
import org.alfresco.service.cmr.repository.ChildAssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.NodeService;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Component that allows child associations to be edited
|
||||
* i.e. new associations to be added, existing ones to be
|
||||
* removed whilst following the rules in the data dictionary
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UIChildAssociationEditor extends BaseAssociationEditor
|
||||
{
|
||||
private static final Log logger = LogFactory.getLog(UIChildAssociationEditor.class);
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.ChildAssociationEditor";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.BaseAssociationEditor#populateAssocationMaps(org.alfresco.web.bean.repository.Node)
|
||||
*/
|
||||
protected void populateAssocationMaps(Node node)
|
||||
{
|
||||
// we need to remember the original set of associations (if there are any)
|
||||
// and place them in a map keyed by the id of the child node
|
||||
if (this.originalAssocs == null)
|
||||
{
|
||||
this.originalAssocs = new HashMap<String, Object>();
|
||||
|
||||
List assocs = (List)node.getChildAssociations().get(this.associationName);
|
||||
if (assocs != null)
|
||||
{
|
||||
Iterator iter = assocs.iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
ChildAssociationRef assoc = (ChildAssociationRef)iter.next();
|
||||
|
||||
// add the association to the map
|
||||
this.originalAssocs.put(assoc.getChildRef().getId(), assoc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get the map of added associations for this node and association type
|
||||
this.added = (Map)node.getAddedChildAssociations().get(this.associationName);
|
||||
if (added == null)
|
||||
{
|
||||
// if there aren't any added associations for 'associationName' create a map and add it
|
||||
added = new HashMap<String, Object>();
|
||||
node.getAddedChildAssociations().put(this.associationName, (Map)added);
|
||||
}
|
||||
|
||||
// get the map of removed associations for this node and association type
|
||||
this.removed = (Map)node.getRemovedChildAssociations().get(this.associationName);
|
||||
if (removed == null)
|
||||
{
|
||||
// if there aren't any added associations for 'associationName' create a map and add it
|
||||
removed = new HashMap<String, Object>();
|
||||
node.getRemovedChildAssociations().put(this.associationName, (Map)removed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.BaseAssociationEditor#renderExistingAssociations(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, org.alfresco.service.cmr.repository.NodeService, boolean)
|
||||
*/
|
||||
protected void renderExistingAssociations(FacesContext context, ResponseWriter out,
|
||||
NodeService nodeService, boolean allowMany) throws IOException
|
||||
{
|
||||
boolean itemsRendered = false;
|
||||
|
||||
// show the associations from the original list if they are not in the removed list
|
||||
Iterator iter = this.originalAssocs.values().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
ChildAssociationRef assoc = (ChildAssociationRef)iter.next();
|
||||
if (removed.containsKey(assoc.getChildRef().getId()) == false)
|
||||
{
|
||||
renderExistingAssociation(context, out, nodeService, assoc.getChildRef(), allowMany);
|
||||
itemsRendered = true;
|
||||
}
|
||||
}
|
||||
|
||||
// also show any associations added in this session
|
||||
iter = this.added.values().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
ChildAssociationRef assoc = (ChildAssociationRef)iter.next();
|
||||
renderExistingAssociation(context, out, nodeService, assoc.getChildRef(), allowMany);
|
||||
itemsRendered = true;
|
||||
}
|
||||
|
||||
// show the none selected message if no items were rendered
|
||||
if (itemsRendered == false && allowMany == true)
|
||||
{
|
||||
renderNone(context, out);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.BaseAssociationEditor#renderReadOnlyAssociations(javax.faces.context.FacesContext, javax.faces.context.ResponseWriter, org.alfresco.service.cmr.repository.NodeService)
|
||||
*/
|
||||
protected void renderReadOnlyAssociations(FacesContext context, ResponseWriter out, NodeService nodeService) throws IOException
|
||||
{
|
||||
if (this.originalAssocs.size() > 0)
|
||||
{
|
||||
out.write("<table cellspacing='0' cellpadding='2' border='0'>");
|
||||
|
||||
Iterator iter = this.originalAssocs.values().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
out.write("<tr><td>");
|
||||
ChildAssociationRef assoc = (ChildAssociationRef)iter.next();
|
||||
out.write(Repository.getDisplayPath(nodeService.getPath(assoc.getChildRef())));
|
||||
out.write("/");
|
||||
out.write(Repository.getNameForNode(nodeService, assoc.getChildRef()));
|
||||
out.write("</tr></td>");
|
||||
}
|
||||
|
||||
out.write("</table>");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.BaseAssociationEditor#removeTarget(org.alfresco.web.bean.repository.Node, java.lang.String)
|
||||
*/
|
||||
protected void removeTarget(Node node, String childId)
|
||||
{
|
||||
if (node != null && childId != null)
|
||||
{
|
||||
QName assocQName = Repository.resolveToQName(this.associationName);
|
||||
ChildAssociationRef childAssoc = new ChildAssociationRef(assocQName,
|
||||
node.getNodeRef(), assocQName, new NodeRef(Repository.getStoreRef(), childId));
|
||||
|
||||
// update the node so it knows to remove the association, but only if the association
|
||||
// was one of the original ones
|
||||
if (this.originalAssocs.containsKey(childId))
|
||||
{
|
||||
Map<String, ChildAssociationRef> removed = node.getRemovedChildAssociations().get(this.associationName);
|
||||
removed.put(childId, childAssoc);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Added association to " + childId + " to the removed list");
|
||||
}
|
||||
|
||||
// if this association was previously added in this session it will still be
|
||||
// in the added list so remove it if it is
|
||||
Map<String, ChildAssociationRef> added = node.getAddedChildAssociations().get(this.associationName);
|
||||
if (added.containsKey(childId))
|
||||
{
|
||||
added.remove(childId);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Removed association to " + childId + " from the added list");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.BaseAssociationEditor#addTarget(org.alfresco.web.bean.repository.Node, java.lang.String[])
|
||||
*/
|
||||
protected void addTarget(Node node, String[] toAdd)
|
||||
{
|
||||
if (node != null && toAdd != null && toAdd.length > 0)
|
||||
{
|
||||
for (int x = 0; x < toAdd.length; x++)
|
||||
{
|
||||
String childId = toAdd[x];
|
||||
|
||||
// update the node so it knows to add the association
|
||||
if (this.originalAssocs.containsKey(childId) == false)
|
||||
{
|
||||
QName assocQName = Repository.resolveToQName(this.associationName);
|
||||
ChildAssociationRef childAssoc = new ChildAssociationRef(assocQName,
|
||||
node.getNodeRef(), assocQName, new NodeRef(Repository.getStoreRef(), childId));
|
||||
|
||||
Map<String, ChildAssociationRef> added = node.getAddedChildAssociations().get(this.associationName);
|
||||
added.put(childId, childAssoc);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Added association to " + childId + " to the added list");
|
||||
}
|
||||
|
||||
// if the association was previously removed and has now been re-added it
|
||||
// will still be in the "to be removed" list so remove it if it is
|
||||
Map<String, ChildAssociationRef> removed = node.getRemovedChildAssociations().get(this.associationName);
|
||||
if (removed.containsKey(childId))
|
||||
{
|
||||
removed.remove(childId);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Removed association to " + childId + " from the removed list");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.property;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
|
||||
import javax.faces.FacesException;
|
||||
import javax.faces.component.UIInput;
|
||||
import javax.faces.component.UIOutput;
|
||||
import javax.faces.component.UISelectBoolean;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.convert.Converter;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
|
||||
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
|
||||
import org.alfresco.service.namespace.QName;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.DataDictionary;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.ui.common.ComponentConstants;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.common.converter.XMLDateConverter;
|
||||
import org.alfresco.web.ui.repo.RepoConstants;
|
||||
import org.alfresco.web.ui.repo.component.UICategorySelector;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* Component to represent an individual property within a property sheet
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UIProperty extends PropertySheetItem
|
||||
{
|
||||
private static final String MSG_ERROR_PROPERTY = "error_property";
|
||||
private static final String MSG_DATE_TIME = "date_time_pattern";
|
||||
private static final String MSG_DATE = "date_pattern";
|
||||
|
||||
private static Log logger = LogFactory.getLog(UIProperty.class);
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public UIProperty()
|
||||
{
|
||||
// set the default renderer
|
||||
setRendererType("org.alfresco.faces.PropertyRenderer");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.Property";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.PropertySheetItem#getIncorrectParentMsg()
|
||||
*/
|
||||
protected String getIncorrectParentMsg()
|
||||
{
|
||||
return "The property component must be nested within a property sheet component";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see org.alfresco.web.ui.repo.component.property.PropertySheetItem#generateItem(javax.faces.context.FacesContext, org.alfresco.web.bean.repository.Node, java.lang.String)
|
||||
*/
|
||||
protected void generateItem(FacesContext context, Node node, String var) throws IOException
|
||||
{
|
||||
String propertyName = (String)getName();
|
||||
|
||||
DataDictionary dd = (DataDictionary)FacesContextUtils.getRequiredWebApplicationContext(
|
||||
context).getBean(Application.BEAN_DATA_DICTIONARY);
|
||||
PropertyDefinition propDef = dd.getPropertyDefinition(node, propertyName);
|
||||
|
||||
if (propDef == null)
|
||||
{
|
||||
// there is no definition for the node, so it may have been added to
|
||||
// the node as an additional property, so look for it in the node itself
|
||||
if (node.hasProperty(propertyName))
|
||||
{
|
||||
String displayLabel = (String)getDisplayLabel();
|
||||
if (displayLabel == null)
|
||||
{
|
||||
displayLabel = propertyName;
|
||||
}
|
||||
|
||||
// generate the label and generic control
|
||||
generateLabel(context, displayLabel);
|
||||
generateControl(context, propertyName, var);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Failed to find property definition for property '" + propertyName + "'");
|
||||
|
||||
// add an error message as the property is not defined in the data dictionary and
|
||||
// not in the node's set of properties
|
||||
String msg = MessageFormat.format(Application.getMessage(context, MSG_ERROR_PROPERTY), new Object[] {propertyName});
|
||||
Utils.addErrorMessage(msg);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Added global error message: " + msg);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String displayLabel = (String)getDisplayLabel();
|
||||
if (displayLabel == null)
|
||||
{
|
||||
// try and get the repository assigned label
|
||||
displayLabel = propDef.getTitle();
|
||||
|
||||
// if the label is still null default to the local name of the property
|
||||
if (displayLabel == null)
|
||||
{
|
||||
displayLabel = propDef.getName().getLocalName();
|
||||
}
|
||||
}
|
||||
|
||||
// generate the label and type specific control
|
||||
generateLabel(context, displayLabel);
|
||||
generateControl(context, propDef, var);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an appropriate control for the given property
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param propDef The definition of the property to create the control for
|
||||
* @param varName Name of the variable the node is stored in the session as
|
||||
* (used for value binding expression)
|
||||
* @param parent The parent component for the control
|
||||
*/
|
||||
private void generateControl(FacesContext context, PropertyDefinition propDef,
|
||||
String varName)
|
||||
{
|
||||
UIOutput control = null;
|
||||
ValueBinding vb = context.getApplication().
|
||||
createValueBinding("#{" + varName + ".properties[\"" +
|
||||
propDef.getName().toString() + "\"]}");
|
||||
|
||||
UIPropertySheet propSheet = (UIPropertySheet)this.getParent();
|
||||
|
||||
DataTypeDefinition dataTypeDef = propDef.getDataType();
|
||||
QName typeName = dataTypeDef.getName();
|
||||
|
||||
if (propSheet.getMode().equalsIgnoreCase(UIPropertySheet.VIEW_MODE))
|
||||
{
|
||||
// if we are in view mode simply output the text to the screen unless the type
|
||||
// of the property is a category
|
||||
if (typeName.equals(DataTypeDefinition.CATEGORY))
|
||||
{
|
||||
control = (UICategorySelector)context.getApplication().
|
||||
createComponent(RepoConstants.ALFRESCO_FACES_CATEGORY_SELECTOR);
|
||||
((UICategorySelector)control).setDisabled(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
control = (UIOutput)context.getApplication().createComponent(ComponentConstants.JAVAX_FACES_OUTPUT);
|
||||
control.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
}
|
||||
|
||||
// if it is a date or datetime property add the converter
|
||||
if (typeName.equals(DataTypeDefinition.DATETIME) )
|
||||
{
|
||||
XMLDateConverter conv = (XMLDateConverter)context.getApplication().
|
||||
createConverter(RepoConstants.ALFRESCO_FACES_XMLDATA_CONVERTER);
|
||||
conv.setType("both");
|
||||
conv.setPattern(Application.getMessage(context, MSG_DATE_TIME));
|
||||
control.setConverter(conv);
|
||||
}
|
||||
else if (typeName.equals(DataTypeDefinition.DATE))
|
||||
{
|
||||
XMLDateConverter conv = (XMLDateConverter)context.getApplication().
|
||||
createConverter(RepoConstants.ALFRESCO_FACES_XMLDATA_CONVERTER);
|
||||
conv.setType("date");
|
||||
conv.setPattern(Application.getMessage(context, MSG_DATE));
|
||||
control.setConverter(conv);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// generate the appropriate input field
|
||||
if (typeName.equals(DataTypeDefinition.BOOLEAN))
|
||||
{
|
||||
control = (UISelectBoolean)context.getApplication().
|
||||
createComponent(ComponentConstants.JAVAX_FACES_SELECT_BOOLEAN);
|
||||
control.setRendererType(ComponentConstants.JAVAX_FACES_CHECKBOX);
|
||||
}
|
||||
else if (typeName.equals(DataTypeDefinition.CATEGORY))
|
||||
{
|
||||
control = (UICategorySelector)context.getApplication().
|
||||
createComponent(RepoConstants.ALFRESCO_FACES_CATEGORY_SELECTOR);
|
||||
}
|
||||
else if (typeName.equals(DataTypeDefinition.DATETIME))
|
||||
{
|
||||
control = (UIInput)context.getApplication().
|
||||
createComponent(ComponentConstants.JAVAX_FACES_INPUT);
|
||||
control.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
|
||||
control.getAttributes().put("startYear", new Integer(1970));
|
||||
control.getAttributes().put("yearCount", new Integer(50));
|
||||
control.getAttributes().put("showTime", Boolean.valueOf(true));
|
||||
control.getAttributes().put("style", "margin-right: 7px;");
|
||||
}
|
||||
else if (typeName.equals(DataTypeDefinition.DATE))
|
||||
{
|
||||
control = (UIInput)context.getApplication().
|
||||
createComponent(ComponentConstants.JAVAX_FACES_INPUT);
|
||||
control.setRendererType(RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER);
|
||||
control.getAttributes().put("startYear", new Integer(1970));
|
||||
control.getAttributes().put("yearCount", new Integer(50));
|
||||
control.getAttributes().put("style", "margin-right: 7px;");
|
||||
}
|
||||
else
|
||||
{
|
||||
// any other type is represented as an input text field
|
||||
control = (UIInput)context.getApplication().
|
||||
createComponent(ComponentConstants.JAVAX_FACES_INPUT);
|
||||
control.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
control.getAttributes().put("size", "35");
|
||||
control.getAttributes().put("maxlength", "1024");
|
||||
}
|
||||
|
||||
// if we are trying to edit a NodeRef or Path property type set it to read-only as
|
||||
// these are internal properties that shouldn't be edited.
|
||||
if (typeName.equals(DataTypeDefinition.NODE_REF) || typeName.equals(DataTypeDefinition.PATH))
|
||||
{
|
||||
logger.warn("Setting property " + propDef.getName().toString() + " to read-only as it can not be edited");
|
||||
control.getAttributes().put("disabled", Boolean.TRUE);
|
||||
}
|
||||
|
||||
// set control to disabled state if set to read only or if the
|
||||
// property definition says it is protected
|
||||
if (isReadOnly() || propDef.isProtected())
|
||||
{
|
||||
control.getAttributes().put("disabled", Boolean.TRUE);
|
||||
}
|
||||
|
||||
// add a validator if the field is required
|
||||
// if (propDef.isMandatory())
|
||||
// {
|
||||
// control.setRequired(true);
|
||||
// LengthValidator val = (LengthValidator)context.getApplication().
|
||||
// createValidator("javax.faces.Length");
|
||||
// val.setMinimum(1);
|
||||
// control.addValidator(val);
|
||||
// }
|
||||
}
|
||||
|
||||
// set up the common aspects of the control
|
||||
control.setId(context.getViewRoot().createUniqueId());
|
||||
control.setValueBinding("value", vb);
|
||||
|
||||
// if a converter has been specified we need to instantiate it
|
||||
// and apply it to the control
|
||||
if (getConverter() != null)
|
||||
{
|
||||
// catch null pointer exception to workaround bug in myfaces
|
||||
try
|
||||
{
|
||||
Converter conv = context.getApplication().createConverter(getConverter());
|
||||
control.setConverter(conv);
|
||||
}
|
||||
catch (FacesException fe)
|
||||
{
|
||||
logger.warn("Converter " + getConverter() + " could not be applied");
|
||||
}
|
||||
}
|
||||
|
||||
// add the control itself
|
||||
this.getChildren().add(control);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created control " + control + "(" +
|
||||
control.getClientId(context) +
|
||||
") for '" + propDef.getName().toString() +
|
||||
"' and added it to component " + this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an appropriate control for the given property name
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param propName The name of the property to create a control for
|
||||
* @param varName Name of the variable the node is stored in the session as
|
||||
* (used for value binding expression)
|
||||
* @param parent The parent component for the control
|
||||
*/
|
||||
private void generateControl(FacesContext context, String propName,
|
||||
String varName)
|
||||
{
|
||||
ValueBinding vb = context.getApplication().
|
||||
createValueBinding("#{" + varName + ".properties[\"" +
|
||||
propName + "\"]}");
|
||||
|
||||
UIOutput control = null;
|
||||
UIPropertySheet propSheet = (UIPropertySheet)this.getParent();
|
||||
if (propSheet.getMode().equalsIgnoreCase(UIPropertySheet.VIEW_MODE))
|
||||
{
|
||||
// if we are in view mode simply output the text to the screen
|
||||
control = (UIOutput)context.getApplication().createComponent(ComponentConstants.JAVAX_FACES_OUTPUT);
|
||||
control.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
}
|
||||
else
|
||||
{
|
||||
// as we don't know the type of the property we can only output a text field
|
||||
control = (UIInput)context.getApplication().createComponent(ComponentConstants.JAVAX_FACES_INPUT);
|
||||
control.setRendererType(ComponentConstants.JAVAX_FACES_TEXT);
|
||||
control.getAttributes().put("size", "35");
|
||||
control.getAttributes().put("maxlength", "1024");
|
||||
|
||||
// set control to disabled state if set to read only
|
||||
if (isReadOnly())
|
||||
{
|
||||
control.getAttributes().put("disabled", Boolean.TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
// set the common attributes
|
||||
control.setId(context.getViewRoot().createUniqueId());
|
||||
control.setValueBinding("value", vb);
|
||||
|
||||
// if a converter has been specified we need to instantiate it
|
||||
// and apply it to the control
|
||||
if (getConverter() != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
Converter conv = context.getApplication().createConverter(getConverter());
|
||||
control.setConverter(conv);
|
||||
}
|
||||
catch (FacesException fe)
|
||||
{
|
||||
logger.warn("Converter " + getConverter() + " could not be applied");
|
||||
}
|
||||
}
|
||||
|
||||
// add the control itself
|
||||
this.getChildren().add(control);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created control " + control + "(" +
|
||||
control.getClientId(context) +
|
||||
") for '" + propName +
|
||||
"' and added it to component " + this);
|
||||
}
|
||||
}
|
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.property;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UIPanel;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.config.Config;
|
||||
import org.alfresco.config.ConfigLookupContext;
|
||||
import org.alfresco.config.ConfigService;
|
||||
import org.alfresco.service.cmr.repository.AssociationRef;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.config.PropertySheetConfigElement;
|
||||
import org.alfresco.web.config.PropertySheetConfigElement.AssociationConfig;
|
||||
import org.alfresco.web.config.PropertySheetConfigElement.ChildAssociationConfig;
|
||||
import org.alfresco.web.config.PropertySheetConfigElement.ItemConfig;
|
||||
import org.alfresco.web.config.PropertySheetConfigElement.PropertyConfig;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.web.jsf.FacesContextUtils;
|
||||
|
||||
/**
|
||||
* Component that represents the properties of a Node
|
||||
*
|
||||
* @author gavinc
|
||||
*/
|
||||
public class UIPropertySheet extends UIPanel implements NamingContainer
|
||||
{
|
||||
public static final String VIEW_MODE = "view";
|
||||
public static final String EDIT_MODE = "edit";
|
||||
|
||||
private static Log logger = LogFactory.getLog(UIPropertySheet.class);
|
||||
private static String DEFAULT_VAR_NAME = "node";
|
||||
|
||||
private String variable;
|
||||
private NodeRef nodeRef;
|
||||
private Node node;
|
||||
private Boolean readOnly;
|
||||
private String mode;
|
||||
private String configArea;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public UIPropertySheet()
|
||||
{
|
||||
// set the default renderer for a property sheet
|
||||
setRendererType("javax.faces.Grid");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "javax.faces.Panel";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
String var = null;
|
||||
int howManyKids = getChildren().size();
|
||||
Boolean externalConfig = (Boolean)getAttributes().get("externalConfig");
|
||||
|
||||
// generate a variable name to use if necessary
|
||||
if (this.variable == null)
|
||||
{
|
||||
this.variable = DEFAULT_VAR_NAME;
|
||||
}
|
||||
|
||||
// force retrieval of node info
|
||||
Node node = getNode();
|
||||
|
||||
if (howManyKids == 0)
|
||||
{
|
||||
if (externalConfig != null && externalConfig.booleanValue())
|
||||
{
|
||||
// configure the component using the config service
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Configuring property sheet using ConfigService");
|
||||
|
||||
// get the properties to display
|
||||
ConfigService configSvc = (ConfigService)FacesContextUtils.getRequiredWebApplicationContext(
|
||||
context).getBean(Application.BEAN_CONFIG_SERVICE);
|
||||
Config configProps = null;
|
||||
if (getConfigArea() == null)
|
||||
{
|
||||
configProps = configSvc.getConfig(node);
|
||||
}
|
||||
else
|
||||
{
|
||||
// only look within the given area
|
||||
configProps = configSvc.getConfig(node, new ConfigLookupContext(getConfigArea()));
|
||||
}
|
||||
|
||||
PropertySheetConfigElement itemsToDisplay = (PropertySheetConfigElement)configProps.
|
||||
getConfigElement("property-sheet");
|
||||
|
||||
if (itemsToDisplay != null)
|
||||
{
|
||||
List<ItemConfig> itemsToRender = itemsToDisplay.getItemsToShow();
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Items to render: " + itemsToDisplay.getItemNamesToShow());
|
||||
|
||||
createComponentsFromConfig(context, itemsToRender);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("There are no items to render!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// show all the properties for the current node
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Configuring property sheet using node's current state");
|
||||
|
||||
createComponentsFromNode(context, node);
|
||||
}
|
||||
}
|
||||
|
||||
// put the node in the session if it is not there already
|
||||
Map sessionMap = getFacesContext().getExternalContext().getSessionMap();
|
||||
sessionMap.put(this.variable, node);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Put node into session with key '" + this.variable + "': " + node);
|
||||
|
||||
super.encodeBegin(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.nodeRef = (NodeRef)values[1];
|
||||
this.node = (Node)values[2];
|
||||
this.variable = (String)values[3];
|
||||
this.readOnly = (Boolean)values[4];
|
||||
this.mode = (String)values[5];
|
||||
this.configArea = (String)values[6];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[7];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.nodeRef;
|
||||
values[2] = this.node;
|
||||
values[3] = this.variable;
|
||||
values[4] = this.readOnly;
|
||||
values[5] = this.mode;
|
||||
values[6] = this.configArea;
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the node
|
||||
*/
|
||||
public Node getNode()
|
||||
{
|
||||
Node node = null;
|
||||
|
||||
if (this.node == null)
|
||||
{
|
||||
// use the value to get hold of the actual object
|
||||
Object value = getAttributes().get("value");
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("value");
|
||||
if (vb != null)
|
||||
{
|
||||
value = vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: for now we presume the object is a Node, but we need to support id's too
|
||||
if (value instanceof Node)
|
||||
{
|
||||
node = (Node)value;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node = this.node;
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param node The node
|
||||
*/
|
||||
public void setNode(Node node)
|
||||
{
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the variable.
|
||||
*/
|
||||
public String getVar()
|
||||
{
|
||||
return this.variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param variable The variable to set.
|
||||
*/
|
||||
public void setVar(String variable)
|
||||
{
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns whether the property sheet is read only
|
||||
*/
|
||||
public boolean isReadOnly()
|
||||
{
|
||||
if (this.readOnly == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("readOnly");
|
||||
if (vb != null)
|
||||
{
|
||||
this.readOnly = (Boolean)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.readOnly == null)
|
||||
{
|
||||
this.readOnly = Boolean.FALSE;
|
||||
}
|
||||
|
||||
return this.readOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param readOnly Sets the read only flag for the property sheet
|
||||
*/
|
||||
public void setReadOnly(boolean readOnly)
|
||||
{
|
||||
this.readOnly = Boolean.valueOf(readOnly);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the mode
|
||||
*/
|
||||
public String getMode()
|
||||
{
|
||||
if (this.mode == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("mode");
|
||||
if (vb != null)
|
||||
{
|
||||
this.mode = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
if (this.mode == null)
|
||||
{
|
||||
mode = EDIT_MODE;
|
||||
}
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mode Sets the mode
|
||||
*/
|
||||
public void setMode(String mode)
|
||||
{
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the config area to use
|
||||
*/
|
||||
public String getConfigArea()
|
||||
{
|
||||
if (this.configArea == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("configArea");
|
||||
if (vb != null)
|
||||
{
|
||||
this.configArea = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
|
||||
return configArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param configArea Sets the config area to use
|
||||
*/
|
||||
public void setConfigArea(String configArea)
|
||||
{
|
||||
this.configArea = configArea;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates all the property components required to display the properties held by the node.
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param node The Node to show all the properties for
|
||||
* @throws IOException
|
||||
*/
|
||||
private void createComponentsFromNode(FacesContext context, Node node)
|
||||
throws IOException
|
||||
{
|
||||
// add all the properties of the node to the UI
|
||||
Map<String, Object> props = node.getProperties();
|
||||
for (String propertyName : props.keySet())
|
||||
{
|
||||
// create the property component
|
||||
UIProperty propComp = (UIProperty)context.getApplication().createComponent("org.alfresco.faces.Property");
|
||||
propComp.setId(context.getViewRoot().createUniqueId());
|
||||
propComp.setName(propertyName);
|
||||
|
||||
// if this property sheet is set as read only, set all properties to read only
|
||||
if (isReadOnly())
|
||||
{
|
||||
propComp.setReadOnly(true);
|
||||
}
|
||||
|
||||
// NOTE: we don't know what the display label is so don't set it
|
||||
|
||||
this.getChildren().add(propComp);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created property component " + propComp + "(" +
|
||||
propComp.getClientId(context) +
|
||||
") for '" + propertyName +
|
||||
"' and added it to property sheet " + this);
|
||||
}
|
||||
|
||||
// add all the associations of the node to the UI
|
||||
Map associations = node.getAssociations();
|
||||
Iterator iter = associations.keySet().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
String assocName = (String)iter.next();
|
||||
UIAssociation assocComp = (UIAssociation)context.getApplication().createComponent("org.alfresco.faces.Association");
|
||||
assocComp.setId(context.getViewRoot().createUniqueId());
|
||||
assocComp.setName(assocName);
|
||||
|
||||
// if this property sheet is set as read only, set all properties to read only
|
||||
if (isReadOnly())
|
||||
{
|
||||
assocComp.setReadOnly(true);
|
||||
}
|
||||
|
||||
// NOTE: we don't know what the display label is so don't set it
|
||||
|
||||
this.getChildren().add(assocComp);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created association component " + assocComp + "(" +
|
||||
assocComp.getClientId(context) +
|
||||
") for '" + assocName +
|
||||
"' and added it to property sheet " + this);
|
||||
}
|
||||
|
||||
// add all the child associations of the node to the UI
|
||||
Map childAssociations = node.getChildAssociations();
|
||||
iter = childAssociations.keySet().iterator();
|
||||
while (iter.hasNext())
|
||||
{
|
||||
String assocName = (String)iter.next();
|
||||
UIChildAssociation childAssocComp = (UIChildAssociation)context.getApplication().createComponent(
|
||||
"org.alfresco.faces.ChildAssociation");
|
||||
childAssocComp.setId(context.getViewRoot().createUniqueId());
|
||||
childAssocComp.setName(assocName);
|
||||
|
||||
// if this property sheet is set as read only, set all properties to read only
|
||||
if (isReadOnly())
|
||||
{
|
||||
childAssocComp.setReadOnly(true);
|
||||
}
|
||||
|
||||
// NOTE: we don't know what the display label is so don't set it
|
||||
|
||||
this.getChildren().add(childAssocComp);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created child association component " + childAssocComp + "(" +
|
||||
childAssocComp.getClientId(context) +
|
||||
") for '" + assocName +
|
||||
"' and added it to property sheet " + this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates all the property components required to display the properties specified
|
||||
* in an external config file.
|
||||
*
|
||||
* @param context JSF context
|
||||
* @param properties List of properties to render (driven from configuration)
|
||||
* @throws IOException
|
||||
*/
|
||||
private void createComponentsFromConfig(FacesContext context, List<ItemConfig> items)
|
||||
throws IOException
|
||||
{
|
||||
// **********************************
|
||||
// TODO: Make a common base class for the UIxxx components so we can
|
||||
// reduce the code below...
|
||||
// **********************************
|
||||
|
||||
for (ItemConfig item : items)
|
||||
{
|
||||
// create the appropriate component
|
||||
if (item instanceof PropertyConfig)
|
||||
{
|
||||
UIProperty propComp = (UIProperty)context.getApplication().createComponent("org.alfresco.faces.Property");
|
||||
propComp.setId(context.getViewRoot().createUniqueId());
|
||||
propComp.setName(item.getName());
|
||||
propComp.setConverter(item.getConverter());
|
||||
|
||||
String displayLabel = item.getDisplayLabel();
|
||||
if (item.getDisplayLabelId() != null)
|
||||
{
|
||||
String label = Application.getMessage(context, item.getDisplayLabelId());
|
||||
if (label != null)
|
||||
{
|
||||
displayLabel = label;
|
||||
}
|
||||
}
|
||||
propComp.setDisplayLabel(displayLabel);
|
||||
|
||||
// if this property sheet is set as read only or the config says the property
|
||||
// should be read only set it as such
|
||||
if (isReadOnly() || item.isReadOnly())
|
||||
{
|
||||
propComp.setReadOnly(true);
|
||||
}
|
||||
|
||||
this.getChildren().add(propComp);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created property component " + propComp + "(" +
|
||||
propComp.getClientId(context) +
|
||||
") for '" + item.getName() +
|
||||
"' and added it to property sheet " + this);
|
||||
}
|
||||
else if (item instanceof AssociationConfig)
|
||||
{
|
||||
UIAssociation assocComp = (UIAssociation)context.getApplication().createComponent("org.alfresco.faces.Association");
|
||||
assocComp.setId(context.getViewRoot().createUniqueId());
|
||||
assocComp.setName(item.getName());
|
||||
assocComp.setConverter(item.getConverter());
|
||||
|
||||
String displayLabel = item.getDisplayLabel();
|
||||
if (item.getDisplayLabelId() != null)
|
||||
{
|
||||
String label = Application.getMessage(context, item.getDisplayLabelId());
|
||||
if (label != null)
|
||||
{
|
||||
displayLabel = label;
|
||||
}
|
||||
}
|
||||
assocComp.setDisplayLabel(displayLabel);
|
||||
|
||||
// if this property sheet is set as read only or the config says the property
|
||||
// should be read only set it as such
|
||||
if (isReadOnly() || item.isReadOnly())
|
||||
{
|
||||
assocComp.setReadOnly(true);
|
||||
}
|
||||
|
||||
this.getChildren().add(assocComp);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created association component " + assocComp + "(" +
|
||||
assocComp.getClientId(context) +
|
||||
") for '" + item.getName() +
|
||||
"' and added it to property sheet " + this);
|
||||
}
|
||||
else if (item instanceof ChildAssociationConfig)
|
||||
{
|
||||
UIChildAssociation assocComp = (UIChildAssociation)context.getApplication().createComponent("org.alfresco.faces.ChildAssociation");
|
||||
assocComp.setId(context.getViewRoot().createUniqueId());
|
||||
assocComp.setName(item.getName());
|
||||
assocComp.setConverter(item.getConverter());
|
||||
|
||||
String displayLabel = item.getDisplayLabel();
|
||||
if (item.getDisplayLabelId() != null)
|
||||
{
|
||||
String label = Application.getMessage(context, item.getDisplayLabelId());
|
||||
if (label != null)
|
||||
{
|
||||
displayLabel = label;
|
||||
}
|
||||
}
|
||||
assocComp.setDisplayLabel(displayLabel);
|
||||
|
||||
// if this property sheet is set as read only or the config says the property
|
||||
// should be read only set it as such
|
||||
if (isReadOnly() || item.isReadOnly())
|
||||
{
|
||||
assocComp.setReadOnly(true);
|
||||
}
|
||||
|
||||
this.getChildren().add(assocComp);
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
logger.debug("Created child association component " + assocComp + "(" +
|
||||
assocComp.getClientId(context) +
|
||||
") for '" + item.getName() +
|
||||
"' and added it to property sheet " + this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.shelf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.MethodBinding;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import javax.faces.event.FacesEvent;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.clipboard.ClipboardItem;
|
||||
import org.alfresco.web.bean.clipboard.ClipboardStatus;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.repo.WebResources;
|
||||
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UIClipboardShelfItem extends UIShelfItem
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.collections = (List)values[1];
|
||||
this.pasteActionListener = (MethodBinding)values[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[3];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.collections;
|
||||
values[2] = this.pasteActionListener;
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void decode(FacesContext context)
|
||||
{
|
||||
Map requestMap = context.getExternalContext().getRequestParameterMap();
|
||||
String fieldId = getHiddenFieldName();
|
||||
String value = (String)requestMap.get(fieldId);
|
||||
|
||||
if (value != null && value.length() != 0)
|
||||
{
|
||||
// decode the values - we are expecting an action identifier and an index
|
||||
int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR);
|
||||
int action = Integer.parseInt(value.substring(0, sepIndex));
|
||||
int index = Integer.parseInt(value.substring(sepIndex + 1));
|
||||
|
||||
// raise an event to process the action later in the lifecycle
|
||||
ClipboardEvent event = new ClipboardEvent(this, action, index);
|
||||
this.queueEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#broadcast(javax.faces.event.FacesEvent)
|
||||
*/
|
||||
public void broadcast(FacesEvent event) throws AbortProcessingException
|
||||
{
|
||||
if (event instanceof ClipboardEvent)
|
||||
{
|
||||
// found an event we should handle
|
||||
ClipboardEvent clipEvent = (ClipboardEvent)event;
|
||||
|
||||
List<ClipboardItem> items = getCollections();
|
||||
if (items.size() > clipEvent.Index)
|
||||
{
|
||||
// process the action
|
||||
switch (clipEvent.Action)
|
||||
{
|
||||
case ACTION_REMOVE_ALL:
|
||||
items.clear();
|
||||
break;
|
||||
|
||||
case ACTION_REMOVE_ITEM:
|
||||
items.remove(clipEvent.Index);
|
||||
break;
|
||||
|
||||
case ACTION_PASTE_ALL:
|
||||
case ACTION_PASTE_ITEM:
|
||||
Utils.processActionMethod(getFacesContext(), getPasteActionListener(), clipEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
List<ClipboardItem> items = getCollections();
|
||||
out.write(SHELF_START);
|
||||
if (items.size() != 0)
|
||||
{
|
||||
DictionaryService dd = Repository.getServiceRegistry(context).getDictionaryService();
|
||||
|
||||
ResourceBundle bundle = Application.getBundle(context);
|
||||
|
||||
for (int i=0; i<items.size(); i++)
|
||||
{
|
||||
ClipboardItem item = items.get(i);
|
||||
|
||||
// start row with cut/copy state icon
|
||||
out.write("<tr><td>");
|
||||
if (item.Mode == ClipboardStatus.COPY)
|
||||
{
|
||||
out.write(Utils.buildImageTag(context, WebResources.IMAGE_COPY, 14, 16, bundle.getString(MSG_COPY), null, "absmiddle"));
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write(Utils.buildImageTag(context, WebResources.IMAGE_CUT, 13, 16, bundle.getString(MSG_CUT), null, "absmiddle"));
|
||||
}
|
||||
out.write("</td><td>");
|
||||
|
||||
if (dd.isSubClass(item.Node.getType(), ContentModel.TYPE_FOLDER))
|
||||
{
|
||||
// start row with Space icon
|
||||
out.write(Utils.buildImageTag(context, WebResources.IMAGE_SPACE, 16, 16, null, null, "absmiddle"));
|
||||
}
|
||||
else if (dd.isSubClass(item.Node.getType(), ContentModel.TYPE_CONTENT))
|
||||
{
|
||||
String image = Utils.getFileTypeImage(item.Node.getName(), true);
|
||||
out.write(Utils.buildImageTag(context, image, 16, 16, null, null, "absmiddle"));
|
||||
}
|
||||
|
||||
// output cropped item label - we also output with no breaks, this is ok
|
||||
// as the copped label will ensure a sensible maximum width
|
||||
out.write("</td><td width=100%><nobr> ");
|
||||
out.write(Utils.cropEncode(item.Node.getName()));
|
||||
|
||||
// output actions
|
||||
out.write("</nobr></td><td align=right><nobr>");
|
||||
out.write(buildActionLink(ACTION_REMOVE_ITEM, i, bundle.getString(MSG_REMOVE_ITEM), WebResources.IMAGE_REMOVE));
|
||||
out.write(" ");
|
||||
out.write(buildActionLink(ACTION_PASTE_ITEM, i, bundle.getString(MSG_PASTE_ITEM), WebResources.IMAGE_PASTE));
|
||||
|
||||
// end actions cell and end row
|
||||
out.write("</nobr></td></tr>");
|
||||
}
|
||||
|
||||
// output general actions if any clipboard items are present
|
||||
out.write("<tr><td colspan=3><nobr>");
|
||||
out.write(buildActionLink(ACTION_PASTE_ALL, -1, bundle.getString(MSG_PASTE_ALL), null));
|
||||
out.write(" ");
|
||||
out.write(buildActionLink(ACTION_REMOVE_ALL, -1, bundle.getString(MSG_REMOVE_ALL), null));
|
||||
out.write("</nobr></td><td></td></tr>");
|
||||
}
|
||||
|
||||
out.write(SHELF_END);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Strongly typed component property accessors
|
||||
|
||||
/**
|
||||
* @param collections Set the clipboard item collections to use
|
||||
*/
|
||||
public void setCollections(List<ClipboardItem> collections)
|
||||
{
|
||||
this.collections = collections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The clipboard item collections to use
|
||||
*/
|
||||
public List<ClipboardItem> getCollections()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("collections");
|
||||
if (vb != null)
|
||||
{
|
||||
this.collections = (List<ClipboardItem>)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.collections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param binding The MethodBinding to call when Paste is selected by the user
|
||||
*/
|
||||
public void setPasteActionListener(MethodBinding binding)
|
||||
{
|
||||
this.pasteActionListener = binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The MethodBinding to call when Paste is selected by the user
|
||||
*/
|
||||
public MethodBinding getPasteActionListener()
|
||||
{
|
||||
return this.pasteActionListener;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
|
||||
/**
|
||||
* We use a hidden field name on the assumption that only one clipboard shelf item instance
|
||||
* is present on a single page.
|
||||
*
|
||||
* @return hidden field name
|
||||
*/
|
||||
private String getHiddenFieldName()
|
||||
{
|
||||
return getClientId(getFacesContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the specified values for output to a hidden field
|
||||
*
|
||||
* @param action Action identifer
|
||||
* @param index Index of the clipboard item the action is for
|
||||
*
|
||||
* @return encoded values
|
||||
*/
|
||||
private static String encodeValues(int action, int index)
|
||||
{
|
||||
return Integer.toString(action) + NamingContainer.SEPARATOR_CHAR + Integer.toString(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build HTML for an link representing a clipboard action
|
||||
*
|
||||
* @param action action indentifier to represent
|
||||
* @param index index of the clipboard item this action relates too
|
||||
* @param text of the action to display
|
||||
* @param image image icon to display
|
||||
*
|
||||
* @return HTML for action link
|
||||
*/
|
||||
private String buildActionLink(int action, int index, String text, String image)
|
||||
{
|
||||
FacesContext context = getFacesContext();
|
||||
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
|
||||
buf.append("<a href='#' onclick=\"");
|
||||
// generate JavaScript to set a hidden form field and submit
|
||||
// a form which request attributes that we can decode
|
||||
buf.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), encodeValues(action, index)));
|
||||
buf.append("\">");
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
buf.append(Utils.buildImageTag(context, image, text));
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.append(Utils.encode(text));
|
||||
}
|
||||
|
||||
buf.append("</a>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing the an action relevant to the Clipboard element.
|
||||
*/
|
||||
public static class ClipboardEvent extends ActionEvent
|
||||
{
|
||||
public ClipboardEvent(UIComponent component, int action, int index)
|
||||
{
|
||||
super(component);
|
||||
Action = action;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public int Action;
|
||||
public int Index;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private data
|
||||
|
||||
/** I18N messages */
|
||||
private static final String MSG_REMOVE_ALL = "remove_all";
|
||||
private static final String MSG_PASTE_ALL = "paste_all";
|
||||
private static final String MSG_PASTE_ITEM = "paste_item";
|
||||
private static final String MSG_REMOVE_ITEM = "remove_item";
|
||||
private static final String MSG_CUT = "cut";
|
||||
private static final String MSG_COPY = "copy";
|
||||
|
||||
private final static int ACTION_REMOVE_ITEM = 0;
|
||||
private final static int ACTION_REMOVE_ALL = 1;
|
||||
private final static int ACTION_PASTE_ITEM = 2;
|
||||
private final static int ACTION_PASTE_ALL = 3;
|
||||
|
||||
/** the current list of clipboard items */
|
||||
private List<ClipboardItem> collections;
|
||||
|
||||
/** action listener called when a paste action occurs */
|
||||
private MethodBinding pasteActionListener;
|
||||
}
|
@@ -0,0 +1,287 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.shelf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.MethodBinding;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import javax.faces.event.FacesEvent;
|
||||
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.repo.WebResources;
|
||||
|
||||
/**
|
||||
* JSF Component providing UI for a real-time updated list of the most recently visited Spaces.
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UIRecentSpacesShelfItem extends UIShelfItem
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.value = values[1];
|
||||
this.navigateActionListener = (MethodBinding)values[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[3];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.value;
|
||||
values[2] = this.navigateActionListener;
|
||||
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value (for this component the value is used as the List of recent Space Nodes)
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
public Object getValue()
|
||||
{
|
||||
if (this.value == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("value");
|
||||
if (vb != null)
|
||||
{
|
||||
this.value = vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value (for this component the value is used as the List of recent Space Nodes)
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
public void setValue(Object value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param binding The MethodBinding to call when Navigate is performed by the user
|
||||
*/
|
||||
public void setNavigateActionListener(MethodBinding binding)
|
||||
{
|
||||
this.navigateActionListener = binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The MethodBinding to call when Navigate is performed by the user
|
||||
*/
|
||||
public MethodBinding getNavigateActionListener()
|
||||
{
|
||||
return this.navigateActionListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void decode(FacesContext context)
|
||||
{
|
||||
Map requestMap = context.getExternalContext().getRequestParameterMap();
|
||||
String fieldId = getHiddenFieldName();
|
||||
String value = (String)requestMap.get(fieldId);
|
||||
|
||||
if (value != null && value.length() != 0)
|
||||
{
|
||||
// decode the values - we are expecting an action identifier and an index
|
||||
int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR);
|
||||
int action = Integer.parseInt(value.substring(0, sepIndex));
|
||||
int index = Integer.parseInt(value.substring(sepIndex + 1));
|
||||
|
||||
// raise an event to process the action later in the lifecycle
|
||||
RecentSpacesEvent event = new RecentSpacesEvent(this, action, index);
|
||||
this.queueEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
List<Node> items = (List<Node>)getValue();
|
||||
out.write(SHELF_START);
|
||||
for (int i=0; i<items.size(); i++)
|
||||
{
|
||||
Node item = items.get(i);
|
||||
|
||||
// start row with Space icon
|
||||
out.write("<tr><td>");
|
||||
out.write(Utils.buildImageTag(context, WebResources.IMAGE_SPACE, 16, 16, null, null, "absmiddle"));
|
||||
|
||||
// output cropped item label - we also output with no breaks, this is ok
|
||||
// as the copped label will ensure a sensible maximum width
|
||||
out.write("</td><td width=100%><nobr> ");
|
||||
out.write(buildActionLink(ACTION_NAVIGATE_ITEM, i, item.getName()));
|
||||
|
||||
// end actions cell and end row
|
||||
out.write("</nobr></td></tr>");
|
||||
}
|
||||
|
||||
out.write(SHELF_END);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#broadcast(javax.faces.event.FacesEvent)
|
||||
*/
|
||||
public void broadcast(FacesEvent event) throws AbortProcessingException
|
||||
{
|
||||
if (event instanceof RecentSpacesEvent)
|
||||
{
|
||||
// found an event we should handle
|
||||
RecentSpacesEvent spaceEvent = (RecentSpacesEvent)event;
|
||||
|
||||
List<Node> items = (List<Node>)getValue();
|
||||
if (items.size() > spaceEvent.Index)
|
||||
{
|
||||
// process the action
|
||||
switch (spaceEvent.Action)
|
||||
{
|
||||
case ACTION_NAVIGATE_ITEM:
|
||||
Utils.processActionMethod(getFacesContext(), getNavigateActionListener(), spaceEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
|
||||
/**
|
||||
* We use a hidden field name on the assumption that only one Recent Spaces shelf item
|
||||
* instance is present on a single page.
|
||||
*
|
||||
* @return hidden field name
|
||||
*/
|
||||
private String getHiddenFieldName()
|
||||
{
|
||||
return getClientId(getFacesContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build HTML for an link representing a Recent Space action
|
||||
*
|
||||
* @param action action indentifier to represent
|
||||
* @param index index of the Node item this action relates too
|
||||
* @param text of the action to display
|
||||
*
|
||||
* @return HTML for action link
|
||||
*/
|
||||
private String buildActionLink(int action, int index, String text)
|
||||
{
|
||||
FacesContext context = getFacesContext();
|
||||
|
||||
StringBuilder buf = new StringBuilder(200);
|
||||
|
||||
buf.append("<a href='#' onclick=\"");
|
||||
// generate JavaScript to set a hidden form field and submit
|
||||
// a form which request attributes that we can decode
|
||||
buf.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), encodeValues(action, index)));
|
||||
buf.append("\">");
|
||||
|
||||
buf.append(Utils.cropEncode(text));
|
||||
|
||||
buf.append("</a>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the specified values for output to a hidden field
|
||||
*
|
||||
* @param action Action identifer
|
||||
* @param index Index of the Node item the action is for
|
||||
*
|
||||
* @return encoded values
|
||||
*/
|
||||
private static String encodeValues(int action, int index)
|
||||
{
|
||||
return Integer.toString(action) + NamingContainer.SEPARATOR_CHAR + Integer.toString(index);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing the an action relevant to the Recent Spaces element.
|
||||
*/
|
||||
public static class RecentSpacesEvent extends ActionEvent
|
||||
{
|
||||
public RecentSpacesEvent(UIComponent component, int action, int index)
|
||||
{
|
||||
super(component);
|
||||
Action = action;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public int Action;
|
||||
public int Index;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private data
|
||||
|
||||
private final static int ACTION_NAVIGATE_ITEM = 0;
|
||||
|
||||
/** for this component the value is used as the List of recent Space Nodes */
|
||||
private Object value = null;
|
||||
|
||||
/** action listener called when a Navigate action occurs */
|
||||
private MethodBinding navigateActionListener;
|
||||
}
|
@@ -0,0 +1,491 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.shelf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.MethodBinding;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import javax.faces.event.FacesEvent;
|
||||
|
||||
import org.alfresco.web.ui.common.PanelGenerator;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.common.WebResources;
|
||||
import org.alfresco.web.ui.common.component.SelfRenderingComponent;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UIShelf extends SelfRenderingComponent
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.Shelf";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.groupPanel = (String)values[1];
|
||||
this.groupBgcolor = (String)values[2];
|
||||
this.selectedGroupPanel = (String)values[3];
|
||||
this.selectedGroupBgcolor = (String)values[4];
|
||||
this.innerGroupPanel = (String)values[5];
|
||||
this.innerGroupBgcolor = (String)values[6];
|
||||
this.groupExpandedActionListener = (MethodBinding)values[7];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[8];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = this.groupPanel;
|
||||
values[2] = this.groupBgcolor;
|
||||
values[3] = this.selectedGroupPanel;
|
||||
values[4] = this.selectedGroupBgcolor;
|
||||
values[5] = this.innerGroupPanel;
|
||||
values[6] = this.innerGroupBgcolor;
|
||||
values[7] = this.groupExpandedActionListener;
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param binding The MethodBinding to call when the Group expand action is performed by the user
|
||||
*/
|
||||
public void setGroupExpandedActionListener(MethodBinding binding)
|
||||
{
|
||||
this.groupExpandedActionListener = binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The MethodBinding to call for the Group expand action
|
||||
*/
|
||||
public MethodBinding getGroupExpandedActionListener()
|
||||
{
|
||||
return this.groupExpandedActionListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void decode(FacesContext context)
|
||||
{
|
||||
Map requestMap = context.getExternalContext().getRequestParameterMap();
|
||||
String fieldId = getHiddenFieldName();
|
||||
String value = (String)requestMap.get(fieldId);
|
||||
|
||||
// we encoded the value to start with our Id
|
||||
if (value != null && value.length() != 0)
|
||||
{
|
||||
int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR);
|
||||
int groupIndex = Integer.parseInt( value.substring(0, sepIndex) );
|
||||
boolean expanded = Boolean.parseBoolean( value.substring(sepIndex + 1) );
|
||||
|
||||
// fire an event here to indicate the change that occured
|
||||
ShelfEvent event = new ShelfEvent(this, groupIndex, expanded);
|
||||
this.queueEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#broadcast(javax.faces.event.FacesEvent)
|
||||
*/
|
||||
public void broadcast(FacesEvent event) throws AbortProcessingException
|
||||
{
|
||||
if (event instanceof ShelfEvent)
|
||||
{
|
||||
ShelfEvent shelfEvent = (ShelfEvent)event;
|
||||
|
||||
// set the new expanded state of the appropriate shelf item
|
||||
int index = 0;
|
||||
for (Iterator i=this.getChildren().iterator(); i.hasNext(); index++)
|
||||
{
|
||||
UIComponent child = (UIComponent)i.next();
|
||||
if (index == shelfEvent.Index && child instanceof UIShelfGroup)
|
||||
{
|
||||
// found correct child - set the new state
|
||||
((UIShelfGroup)child).setExpanded(shelfEvent.Expanded);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if an action event is registered to be notified then fire that next
|
||||
if (getGroupExpandedActionListener() != null)
|
||||
{
|
||||
Utils.processActionMethod(getFacesContext(), getGroupExpandedActionListener(), shelfEvent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
// TODO: allow config of spacing between ShelfGroup components
|
||||
out.write("<table border=0 cellspacing=2 cellpadding=0 width=100%>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeChildren(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeChildren(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
// output each shelf group in turn
|
||||
int index = 0;
|
||||
for (Iterator i=this.getChildren().iterator(); i.hasNext(); index++)
|
||||
{
|
||||
UIComponent child = (UIComponent)i.next();
|
||||
if (child instanceof UIShelfGroup)
|
||||
{
|
||||
UIShelfGroup group = (UIShelfGroup)child;
|
||||
if (group.isRendered() == true)
|
||||
{
|
||||
// output the surrounding structure then call the component to render itself and children
|
||||
boolean isExpanded = group.isExpanded(); // TODO: get this from Shelf or ShelfGroup?
|
||||
out.write("<tr><td>");
|
||||
|
||||
String contextPath = context.getExternalContext().getRequestContextPath();
|
||||
|
||||
// output appropriate panel start section and bgcolor
|
||||
String groupPanel;
|
||||
String groupBgcolor;
|
||||
if (isExpanded == false)
|
||||
{
|
||||
groupPanel = getGroupPanel();
|
||||
groupBgcolor = getGroupBgcolor();
|
||||
}
|
||||
else
|
||||
{
|
||||
groupPanel = getSelectedGroupPanel();
|
||||
groupBgcolor = getSelectedGroupBgcolor();
|
||||
}
|
||||
if (groupBgcolor == null)
|
||||
{
|
||||
groupBgcolor = PanelGenerator.BGCOLOR_WHITE;
|
||||
}
|
||||
if (groupPanel != null)
|
||||
{
|
||||
PanelGenerator.generatePanelStart(out, contextPath, groupPanel, groupBgcolor);
|
||||
}
|
||||
|
||||
// output appropriate expanded icon state
|
||||
out.write("<div style='padding-top:2px;padding-bottom:4px'><nobr>");
|
||||
out.write("<a href='#' onclick=\"");
|
||||
// encode value as the index of the ShelfGroup clicked and the new state
|
||||
String value = Integer.toString(index) + NamingContainer.SEPARATOR_CHAR + Boolean.toString(!isExpanded);
|
||||
out.write(Utils.generateFormSubmit(context, this, getHiddenFieldName(), value));
|
||||
out.write("\">");
|
||||
if (isExpanded == true)
|
||||
{
|
||||
out.write(Utils.buildImageTag(context, WebResources.IMAGE_EXPANDED, 11, 11, ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
out.write(Utils.buildImageTag(context, WebResources.IMAGE_COLLAPSED, 11, 11, ""));
|
||||
}
|
||||
out.write("</a> ");
|
||||
|
||||
// output title label text
|
||||
String label = group.getLabel();
|
||||
out.write("<span");
|
||||
outputAttribute(out, group.getAttributes().get("style"), "style");
|
||||
outputAttribute(out, group.getAttributes().get("styleClass"), "class");
|
||||
out.write('>');
|
||||
out.write(Utils.encode(label));
|
||||
out.write("</span>");
|
||||
out.write("</nobr></div>");
|
||||
|
||||
if (isExpanded == true)
|
||||
{
|
||||
// if this is the expanded group, output the inner panel
|
||||
String innerGroupPanel = getInnerGroupPanel();
|
||||
String innerGroupBgcolor = getInnerGroupBgcolor();
|
||||
if (innerGroupBgcolor == null)
|
||||
{
|
||||
innerGroupBgcolor = PanelGenerator.BGCOLOR_WHITE;
|
||||
}
|
||||
if (innerGroupPanel != null)
|
||||
{
|
||||
PanelGenerator.generatePanelStart(out, contextPath, innerGroupPanel, innerGroupBgcolor);
|
||||
}
|
||||
|
||||
// allow child components to render themselves
|
||||
Utils.encodeRecursive(context, group);
|
||||
|
||||
if (innerGroupPanel != null)
|
||||
{
|
||||
PanelGenerator.generatePanelEnd(out, contextPath, innerGroupPanel);
|
||||
}
|
||||
}
|
||||
|
||||
// output panel and group end elements
|
||||
PanelGenerator.generatePanelEnd(out, contextPath, groupPanel);
|
||||
out.write("</td></tr>");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeEnd(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
out.write("</table>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#getRendersChildren()
|
||||
*/
|
||||
public boolean getRendersChildren()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Strongly typed component property accessors
|
||||
|
||||
/**
|
||||
* @return Returns the group panel name.
|
||||
*/
|
||||
public String getGroupPanel()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("groupPanel");
|
||||
if (vb != null)
|
||||
{
|
||||
this.groupPanel = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.groupPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param groupPanel The group panel name to set.
|
||||
*/
|
||||
public void setGroupPanel(String groupPanel)
|
||||
{
|
||||
this.groupPanel = groupPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the group background colour.
|
||||
*/
|
||||
public String getGroupBgcolor()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("groupBgcolor");
|
||||
if (vb != null)
|
||||
{
|
||||
this.groupBgcolor = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.groupBgcolor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param groupBgcolor The group background colour to set.
|
||||
*/
|
||||
public void setGroupBgcolor(String groupBgcolor)
|
||||
{
|
||||
this.groupBgcolor = groupBgcolor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the selected group panel name.
|
||||
*/
|
||||
public String getSelectedGroupPanel()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("selectedGroupPanel");
|
||||
if (vb != null)
|
||||
{
|
||||
this.selectedGroupPanel = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.selectedGroupPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selectedGroupPanel The selected group panel name to set.
|
||||
*/
|
||||
public void setSelectedGroupPanel(String selectedGroupPanel)
|
||||
{
|
||||
this.selectedGroupPanel = selectedGroupPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the selected group background colour.
|
||||
*/
|
||||
public String getSelectedGroupBgcolor()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("selectedGroupBgcolor");
|
||||
if (vb != null)
|
||||
{
|
||||
this.selectedGroupBgcolor = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.selectedGroupBgcolor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param selectedGroupBgcolor The selected group background colour to set.
|
||||
*/
|
||||
public void setSelectedGroupBgcolor(String selectedGroupBgcolor)
|
||||
{
|
||||
this.selectedGroupBgcolor = selectedGroupBgcolor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the inner group panel name.
|
||||
*/
|
||||
public String getInnerGroupPanel()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("innerGroupPanel");
|
||||
if (vb != null)
|
||||
{
|
||||
this.innerGroupPanel = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.innerGroupPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param innerGroupPanel The inner group panel name to set.
|
||||
*/
|
||||
public void setInnerGroupPanel(String innerGroupPanel)
|
||||
{
|
||||
this.innerGroupPanel = innerGroupPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the inner group background colour.
|
||||
*/
|
||||
public String getInnerGroupBgcolor()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("innerGroupBgcolor");
|
||||
if (vb != null)
|
||||
{
|
||||
this.innerGroupBgcolor = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.innerGroupBgcolor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param innerGroupBgcolor The inner group background colour to set.
|
||||
*/
|
||||
public void setInnerGroupBgcolor(String innerGroupBgcolor)
|
||||
{
|
||||
this.innerGroupBgcolor = innerGroupBgcolor;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
|
||||
/**
|
||||
* We use a hidden field name on the assumption that very few shelf instances will
|
||||
* be present on a single page.
|
||||
*
|
||||
* @return hidden field name
|
||||
*/
|
||||
private String getHiddenFieldName()
|
||||
{
|
||||
return getClientId(getFacesContext());
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing the an action event relevant to the Shelf.
|
||||
*/
|
||||
public static class ShelfEvent extends ActionEvent
|
||||
{
|
||||
public ShelfEvent(UIComponent component, int index, boolean expanded)
|
||||
{
|
||||
super(component);
|
||||
Expanded = expanded;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public boolean Expanded;
|
||||
public int Index;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private data
|
||||
|
||||
/** component properties */
|
||||
private String groupPanel;
|
||||
private String groupBgcolor;
|
||||
private String selectedGroupPanel;
|
||||
private String selectedGroupBgcolor;
|
||||
private String innerGroupPanel;
|
||||
private String innerGroupBgcolor;
|
||||
private MethodBinding groupExpandedActionListener;
|
||||
}
|
@@ -0,0 +1,162 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.shelf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.common.component.SelfRenderingComponent;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UIShelfGroup extends SelfRenderingComponent
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.Shelf";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.expanded = ((Boolean)values[1]).booleanValue();
|
||||
this.label = (String)values[2];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public Object saveState(FacesContext context)
|
||||
{
|
||||
Object values[] = new Object[3];
|
||||
// standard component attributes are saved by the super class
|
||||
values[0] = super.saveState(context);
|
||||
values[1] = (this.expanded ? Boolean.TRUE : Boolean.FALSE);
|
||||
values[2] = this.label;
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeChildren(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeChildren(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
|
||||
// output each shelf group component in turn
|
||||
out.write("<table cellspacing=0 cellpadding=0 border=0 width=100%>");
|
||||
for (Iterator i=this.getChildren().iterator(); i.hasNext(); /**/)
|
||||
{
|
||||
UIComponent child = (UIComponent)i.next();
|
||||
if (child instanceof UIShelfItem)
|
||||
{
|
||||
// render child items
|
||||
out.write("<tr><td>");
|
||||
Utils.encodeRecursive(context, child);
|
||||
out.write("</tr></td>");
|
||||
}
|
||||
}
|
||||
out.write("</table>");
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#getRendersChildren()
|
||||
*/
|
||||
public boolean getRendersChildren()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Strongly typed component property accessors
|
||||
|
||||
/**
|
||||
* @return Returns the label.
|
||||
*/
|
||||
public String getLabel()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("label");
|
||||
if (vb != null)
|
||||
{
|
||||
this.label = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.label;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param label The label to set.
|
||||
*/
|
||||
public void setLabel(String label)
|
||||
{
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the component show allow rendering of its child components.
|
||||
*/
|
||||
public boolean isExpanded()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("expanded");
|
||||
if (vb != null)
|
||||
{
|
||||
Boolean expanded = (Boolean)vb.getValue(getFacesContext());
|
||||
if (expanded != null)
|
||||
{
|
||||
this.expanded = expanded.booleanValue();
|
||||
}
|
||||
}
|
||||
|
||||
return this.expanded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the group is expanded
|
||||
*/
|
||||
public void setExpanded(boolean expanded)
|
||||
{
|
||||
this.expanded = expanded;
|
||||
}
|
||||
|
||||
|
||||
private String label = null;
|
||||
private boolean expanded = false;
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.shelf;
|
||||
|
||||
import org.alfresco.web.ui.common.component.SelfRenderingComponent;
|
||||
|
||||
/**
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UIShelfItem extends SelfRenderingComponent
|
||||
{
|
||||
protected final static String SHELF_START = "<table border=0 cellspacing=3 cellpadding=0 width=100% valign=top>";
|
||||
protected final static String SHELF_END = "</table>";
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.Shelf";
|
||||
}
|
||||
}
|
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.shelf;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.component.NamingContainer;
|
||||
import javax.faces.component.UIComponent;
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.context.ResponseWriter;
|
||||
import javax.faces.el.MethodBinding;
|
||||
import javax.faces.el.ValueBinding;
|
||||
import javax.faces.event.AbortProcessingException;
|
||||
import javax.faces.event.ActionEvent;
|
||||
import javax.faces.event.FacesEvent;
|
||||
|
||||
import org.alfresco.model.ContentModel;
|
||||
import org.alfresco.service.cmr.dictionary.DictionaryService;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Node;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.repo.WebResources;
|
||||
|
||||
/**
|
||||
* JSF Component providing UI for a list of user defined shortcuts to favorite nodes.
|
||||
*
|
||||
* @author Kevin Roast
|
||||
*/
|
||||
public class UIShortcutsShelfItem extends UIShelfItem
|
||||
{
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component Impl
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.value = values[1];
|
||||
this.clickActionListener = (MethodBinding)values[2];
|
||||
this.removeActionListener = (MethodBinding)values[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
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.value;
|
||||
values[2] = this.clickActionListener;
|
||||
values[3] = this.removeActionListener;
|
||||
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value (for this component the value is used as the List of shortcut nodes)
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
public Object getValue()
|
||||
{
|
||||
if (this.value == null)
|
||||
{
|
||||
ValueBinding vb = getValueBinding("value");
|
||||
if (vb != null)
|
||||
{
|
||||
this.value = vb.getValue(getFacesContext());
|
||||
}
|
||||
}
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value (for this component the value is used as the List of shortcut nodes)
|
||||
*
|
||||
* @param value the value
|
||||
*/
|
||||
public void setValue(Object value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param binding The MethodBinding to call when Click is performed by the user
|
||||
*/
|
||||
public void setClickActionListener(MethodBinding binding)
|
||||
{
|
||||
this.clickActionListener = binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The MethodBinding to call when Click is performed by the user
|
||||
*/
|
||||
public MethodBinding getClickActionListener()
|
||||
{
|
||||
return this.clickActionListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param binding The MethodBinding to call when Remove is performed by the user
|
||||
*/
|
||||
public void setRemoveActionListener(MethodBinding binding)
|
||||
{
|
||||
this.removeActionListener = binding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The MethodBinding to call when Remove is performed by the user
|
||||
*/
|
||||
public MethodBinding getRemoveActionListener()
|
||||
{
|
||||
return this.removeActionListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void decode(FacesContext context)
|
||||
{
|
||||
Map requestMap = context.getExternalContext().getRequestParameterMap();
|
||||
String fieldId = getHiddenFieldName();
|
||||
String value = (String)requestMap.get(fieldId);
|
||||
|
||||
if (value != null && value.length() != 0)
|
||||
{
|
||||
// decode the values - we are expecting an action identifier and an index
|
||||
int sepIndex = value.indexOf(NamingContainer.SEPARATOR_CHAR);
|
||||
int action = Integer.parseInt(value.substring(0, sepIndex));
|
||||
int index = Integer.parseInt(value.substring(sepIndex + 1));
|
||||
|
||||
// raise an event to process the action later in the lifecycle
|
||||
ShortcutEvent event = new ShortcutEvent(this, action, index);
|
||||
this.queueEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ResponseWriter out = context.getResponseWriter();
|
||||
List<Node> items = (List<Node>)getValue();
|
||||
out.write(SHELF_START);
|
||||
if (items != null)
|
||||
{
|
||||
DictionaryService dd = Repository.getServiceRegistry(FacesContext.getCurrentInstance()).getDictionaryService();
|
||||
|
||||
for (int i=0; i<items.size(); i++)
|
||||
{
|
||||
Node item = items.get(i);
|
||||
|
||||
out.write("<tr><td>");
|
||||
if (dd.isSubClass(item.getType(), ContentModel.TYPE_FOLDER))
|
||||
{
|
||||
// start row with Space icon
|
||||
out.write(Utils.buildImageTag(context, WebResources.IMAGE_SPACE, 16, 16, null, null, "absmiddle"));
|
||||
}
|
||||
else if (dd.isSubClass(item.getType(), ContentModel.TYPE_CONTENT))
|
||||
{
|
||||
String image = Utils.getFileTypeImage(item.getName(), true);
|
||||
out.write(Utils.buildImageTag(context, image, 16, 16, null, null, "absmiddle"));
|
||||
}
|
||||
|
||||
// output cropped item label - we also output with no breaks, this is ok
|
||||
// as the copped label will ensure a sensible maximum width
|
||||
out.write("</td><td width=100%><nobr> ");
|
||||
out.write(buildActionLink(ACTION_CLICK_ITEM, i, item.getName()));
|
||||
|
||||
// output actions
|
||||
out.write("</nobr></td><td align=right><nobr>");
|
||||
out.write(buildActionLink(ACTION_REMOVE_ITEM, i, Application.getMessage(context, MSG_REMOVE_ITEM), WebResources.IMAGE_REMOVE));
|
||||
// TODO: add view details action here?
|
||||
|
||||
// end actions cell and end row
|
||||
out.write("</nobr></td></tr>");
|
||||
}
|
||||
}
|
||||
|
||||
out.write(SHELF_END);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#broadcast(javax.faces.event.FacesEvent)
|
||||
*/
|
||||
public void broadcast(FacesEvent event) throws AbortProcessingException
|
||||
{
|
||||
if (event instanceof ShortcutEvent)
|
||||
{
|
||||
// found an event we should handle
|
||||
ShortcutEvent shortcutEvent = (ShortcutEvent)event;
|
||||
|
||||
List<Node> items = (List<Node>)getValue();
|
||||
if (items != null && items.size() > shortcutEvent.Index)
|
||||
{
|
||||
// process the action
|
||||
switch (shortcutEvent.Action)
|
||||
{
|
||||
case ACTION_CLICK_ITEM:
|
||||
Utils.processActionMethod(getFacesContext(), getClickActionListener(), shortcutEvent);
|
||||
break;
|
||||
case ACTION_REMOVE_ITEM:
|
||||
Utils.processActionMethod(getFacesContext(), getRemoveActionListener(), shortcutEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
super.broadcast(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private helpers
|
||||
|
||||
/**
|
||||
* We use a hidden field name on the assumption that only one Shortcut Shelf item
|
||||
* instance is present on a single page.
|
||||
*
|
||||
* @return hidden field name
|
||||
*/
|
||||
private String getHiddenFieldName()
|
||||
{
|
||||
return getClientId(getFacesContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Build HTML for an link representing a Shortcut action
|
||||
*
|
||||
* @param action action indentifier to represent
|
||||
* @param index index of the Node item this action relates too
|
||||
* @param text of the action to display
|
||||
*
|
||||
* @return HTML for action link
|
||||
*/
|
||||
private String buildActionLink(int action, int index, String text)
|
||||
{
|
||||
FacesContext context = getFacesContext();
|
||||
|
||||
StringBuilder buf = new StringBuilder(200);
|
||||
|
||||
buf.append("<a href='#' onclick=\"");
|
||||
// generate JavaScript to set a hidden form field and submit
|
||||
// a form which request attributes that we can decode
|
||||
buf.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), encodeValues(action, index)));
|
||||
buf.append("\">");
|
||||
|
||||
buf.append(Utils.cropEncode(text));
|
||||
|
||||
buf.append("</a>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build HTML for an link representing a clipboard action
|
||||
*
|
||||
* @param action action indentifier to represent
|
||||
* @param index index of the clipboard item this action relates too
|
||||
* @param text of the action to display
|
||||
* @param image image icon to display
|
||||
*
|
||||
* @return HTML for action link
|
||||
*/
|
||||
private String buildActionLink(int action, int index, String text, String image)
|
||||
{
|
||||
FacesContext context = getFacesContext();
|
||||
|
||||
StringBuilder buf = new StringBuilder(256);
|
||||
|
||||
buf.append("<a href='#' onclick=\"");
|
||||
// generate JavaScript to set a hidden form field and submit
|
||||
// a form which request attributes that we can decode
|
||||
buf.append(Utils.generateFormSubmit(context, this, getHiddenFieldName(), encodeValues(action, index)));
|
||||
buf.append("\">");
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
buf.append(Utils.buildImageTag(context, image, text));
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.append(Utils.encode(text));
|
||||
}
|
||||
|
||||
buf.append("</a>");
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the specified values for output to a hidden field
|
||||
*
|
||||
* @param action Action identifer
|
||||
* @param index Index of the Node item the action is for
|
||||
*
|
||||
* @return encoded values
|
||||
*/
|
||||
private static String encodeValues(int action, int index)
|
||||
{
|
||||
return Integer.toString(action) + NamingContainer.SEPARATOR_CHAR + Integer.toString(index);
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Inner classes
|
||||
|
||||
/**
|
||||
* Class representing the an action relevant to the Shortcut element.
|
||||
*/
|
||||
public static class ShortcutEvent extends ActionEvent
|
||||
{
|
||||
public ShortcutEvent(UIComponent component, int action, int index)
|
||||
{
|
||||
super(component);
|
||||
Action = action;
|
||||
Index = index;
|
||||
}
|
||||
|
||||
public int Action;
|
||||
public int Index;
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Private data
|
||||
|
||||
/** I18N messages */
|
||||
private static final String MSG_REMOVE_ITEM = "remove_item";
|
||||
|
||||
private final static int ACTION_CLICK_ITEM = 0;
|
||||
private final static int ACTION_REMOVE_ITEM = 1;
|
||||
|
||||
/** for this component the value is used as the List of shortcut Nodes */
|
||||
private Object value = null;
|
||||
|
||||
/** action listener called when a Click action occurs */
|
||||
private MethodBinding clickActionListener;
|
||||
|
||||
/** action listener called when a Remove action occurs */
|
||||
private MethodBinding removeActionListener;
|
||||
}
|
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright (C) 2005 Alfresco, Inc.
|
||||
*
|
||||
* Licensed under the Mozilla Public License version 1.1
|
||||
* with a permitted attribution clause. You may obtain a
|
||||
* copy of the License at
|
||||
*
|
||||
* http://www.alfresco.org/legal/license.txt
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
||||
* either express or implied. See the License for the specific
|
||||
* language governing permissions and limitations under the
|
||||
* License.
|
||||
*/
|
||||
package org.alfresco.web.ui.repo.component.template;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.faces.context.FacesContext;
|
||||
import javax.faces.el.ValueBinding;
|
||||
|
||||
import org.alfresco.config.ConfigService;
|
||||
import org.alfresco.repo.template.DateCompareMethod;
|
||||
import org.alfresco.repo.template.HasAspectMethod;
|
||||
import org.alfresco.repo.template.I18NMessageMethod;
|
||||
import org.alfresco.service.ServiceRegistry;
|
||||
import org.alfresco.service.cmr.repository.NodeRef;
|
||||
import org.alfresco.service.cmr.repository.TemplateException;
|
||||
import org.alfresco.service.cmr.repository.TemplateImageResolver;
|
||||
import org.alfresco.service.cmr.repository.TemplateNode;
|
||||
import org.alfresco.service.cmr.repository.TemplateService;
|
||||
import org.alfresco.web.app.Application;
|
||||
import org.alfresco.web.bean.repository.Repository;
|
||||
import org.alfresco.web.bean.repository.User;
|
||||
import org.alfresco.web.config.ClientConfigElement;
|
||||
import org.alfresco.web.ui.common.Utils;
|
||||
import org.alfresco.web.ui.common.component.SelfRenderingComponent;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* @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 */
|
||||
private String template = null;
|
||||
|
||||
/** Data model reference */
|
||||
private Object model = null;
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Component implementation
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponent#getFamily()
|
||||
*/
|
||||
public String getFamily()
|
||||
{
|
||||
return "org.alfresco.faces.Template";
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#restoreState(javax.faces.context.FacesContext, java.lang.Object)
|
||||
*/
|
||||
public void restoreState(FacesContext context, Object state)
|
||||
{
|
||||
Object values[] = (Object[])state;
|
||||
// standard component attributes are restored by the super class
|
||||
super.restoreState(context, values[0]);
|
||||
this.engine = (String)values[1];
|
||||
this.template = (String)values[2];
|
||||
this.model = (Object)values[3];
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.StateHolder#saveState(javax.faces.context.FacesContext)
|
||||
*/
|
||||
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;
|
||||
return (values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see javax.faces.component.UIComponentBase#encodeBegin(javax.faces.context.FacesContext)
|
||||
*/
|
||||
public void encodeBegin(FacesContext context) throws IOException
|
||||
{
|
||||
if (isRendered() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// get the data model to use - building default if required
|
||||
Object model = getModel();
|
||||
|
||||
// get the configservice to find the appropriate processor
|
||||
ConfigService service = Application.getConfigService(context);
|
||||
ClientConfigElement clientConfig = (ClientConfigElement)service.getGlobalConfig().getConfigElement(
|
||||
ClientConfigElement.CONFIG_ELEMENT_ID);
|
||||
|
||||
// get the template to process
|
||||
String template = getTemplate();
|
||||
if (template != null && template.length() != 0)
|
||||
{
|
||||
// get the class name of the processor to instantiate
|
||||
String engine = getEngine();
|
||||
|
||||
long startTime = 0;
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("Using template processor name: " + engine);
|
||||
startTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
// process the template against the model
|
||||
try
|
||||
{
|
||||
TemplateService templateService = Repository.getServiceRegistry(context).getTemplateService();
|
||||
templateService.processTemplate(engine, getTemplate(), model, context.getResponseWriter());
|
||||
}
|
||||
catch (TemplateException err)
|
||||
{
|
||||
Utils.addErrorMessage(err.getMessage(), err);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
long endTime = System.currentTimeMillis();
|
||||
logger.debug("Time to process template: " + (endTime - startTime) + "ms");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Strongly typed component property accessors
|
||||
|
||||
/**
|
||||
* @return the name of the template engine to use.
|
||||
*/
|
||||
public String getEngine()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("engine");
|
||||
if (vb != null)
|
||||
{
|
||||
this.engine = (String)vb.getValue(getFacesContext());
|
||||
}
|
||||
|
||||
return this.engine != null ? this.engine : ENGINE_DEFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param engine the name of the template engine to use. A default is provided if none is set.
|
||||
*/
|
||||
public void setEngine(String engine)
|
||||
{
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Returns the 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());
|
||||
}
|
||||
|
||||
if (getEngine().equals(ENGINE_DEFAULT))
|
||||
{
|
||||
// create FreeMarker default model and merge
|
||||
Map root = new HashMap(11, 1.0f);
|
||||
|
||||
FacesContext context = FacesContext.getCurrentInstance();
|
||||
ServiceRegistry services = Repository.getServiceRegistry(context);
|
||||
|
||||
// supply the CompanyHome space as "companyhome"
|
||||
NodeRef companyRootRef = new NodeRef(Repository.getStoreRef(), Application.getCompanyRootId());
|
||||
TemplateNode companyRootNode = new TemplateNode(companyRootRef, services, imageResolver);
|
||||
root.put("companyhome", companyRootNode);
|
||||
|
||||
// supply the users Home Space as "userhome"
|
||||
User user = Application.getCurrentUser(context);
|
||||
NodeRef userRootRef = new NodeRef(Repository.getStoreRef(), user.getHomeSpaceId());
|
||||
TemplateNode userRootNode = new TemplateNode(userRootRef, services, imageResolver);
|
||||
root.put("userhome", userRootNode);
|
||||
|
||||
// supply the current user Node as "person"
|
||||
root.put("person", new TemplateNode(user.getPerson(), services, imageResolver));
|
||||
|
||||
// current date/time is useful to have and isn't supplied by FreeMarker by default
|
||||
root.put("date", new Date());
|
||||
|
||||
// add custom method objects
|
||||
root.put("hasAspect", new HasAspectMethod());
|
||||
root.put("message", new I18NMessageMethod());
|
||||
root.put("dateCompare", new DateCompareMethod());
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param model The model to set.
|
||||
*/
|
||||
public void setModel(Object model)
|
||||
{
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Returns the template name.
|
||||
*/
|
||||
public String getTemplate()
|
||||
{
|
||||
ValueBinding vb = getValueBinding("template");
|
||||
if (vb != null)
|
||||
{
|
||||
// convert object to string - then we can handle either a path, NodeRef instance or noderef string
|
||||
Object val = vb.getValue(getFacesContext());
|
||||
if (val != null)
|
||||
{
|
||||
this.template = val.toString();
|
||||
}
|
||||
}
|
||||
|
||||
return this.template;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param template The template name to set.
|
||||
*/
|
||||
public void setTemplate(String template)
|
||||
{
|
||||
this.template = template;
|
||||
}
|
||||
|
||||
/** Template Image resolver helper */
|
||||
private TemplateImageResolver imageResolver = new TemplateImageResolver()
|
||||
{
|
||||
public String resolveImagePathForName(String filename, boolean small)
|
||||
{
|
||||
return Utils.getFileTypeImage(filename, small);
|
||||
}
|
||||
};
|
||||
}
|
Reference in New Issue
Block a user