Introduced multi-value support in the client

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2648 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Gavin Cornwell
2006-04-11 22:15:43 +00:00
parent e5a0e58041
commit 0d45ce4c18
27 changed files with 1122 additions and 529 deletions

View File

@@ -1,13 +1,13 @@
# I18N message properties # I18N message properties
# Date Pattern # Date Pattern
date_pattern=MMMM, d yyyy date_pattern=d MMMM yyyy
date_time_pattern=MMMM, d yyyy HH:mm date_time_pattern=d MMMM yyyy HH:mm
time_pattern=HH:mm time_pattern=HH:mm
# General UI # General UI
product_name=Alfresco product_name=Alfresco
view_description=This view allows you to browse the items in your space. view_description=This view allows you to browse the items in this space.
search_description=This view allows you to see the results from your search. search_description=This view allows you to see the results from your search.
checkinfile_description=Check in your working copy for other team members to work with. checkinfile_description=Check in your working copy for other team members to work with.
checkoutfilelink_description=Edit the checked out file, undo the check out or carry on working. checkoutfilelink_description=Edit the checked out file, undo the check out or carry on working.

View File

@@ -0,0 +1,23 @@
package org.alfresco.web.bean;
import java.util.HashMap;
import java.util.Map;
/**
* Helper bean used to temporarily store the last item added in a multi
* value editor component.
*
* A Map is used so that multiple components on the same page can use the
* same backing bean.
*
* @author gavinc
*/
public class MultiValueEditorBean
{
private Map<String, Object> lastItemsAdded = new HashMap<String, Object>(10);
public Map<String, Object> getLastItemsAdded()
{
return lastItemsAdded;
}
}

View File

@@ -5,6 +5,7 @@ import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput; import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext; import javax.faces.context.FacesContext;
import javax.faces.convert.Converter; import javax.faces.convert.Converter;
import javax.faces.el.ValueBinding;
import org.alfresco.service.cmr.dictionary.AssociationDefinition; import org.alfresco.service.cmr.dictionary.AssociationDefinition;
import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.service.cmr.dictionary.PropertyDefinition;
@@ -13,9 +14,8 @@ import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.bean.repository.DataDictionary; import org.alfresco.web.bean.repository.DataDictionary;
import org.alfresco.web.bean.repository.Node; import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.ui.common.ComponentConstants; import org.alfresco.web.ui.common.ComponentConstants;
import org.alfresco.web.ui.repo.RepoConstants;
import org.alfresco.web.ui.repo.component.property.PropertySheetItem; import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
import org.alfresco.web.ui.repo.component.property.UIAssociation;
import org.alfresco.web.ui.repo.component.property.UIChildAssociation;
import org.alfresco.web.ui.repo.component.property.UIProperty; import org.alfresco.web.ui.repo.component.property.UIProperty;
import org.alfresco.web.ui.repo.component.property.UIPropertySheet; import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@@ -55,35 +55,57 @@ public abstract class BaseComponentGenerator implements IComponentGenerator
} }
/** /**
* Disables the given component if the item is read only or is defined in the * Creates a wrapper component around the given component to enable the user
* model as protected * to edit multiple values.
* *
* @param context FacesContext * @param context FacesContext
* @param propertySheet The property sheet being generated * @param propertySheet The property sheet being generated
* @param item The item being generated * @param item The item being generated
* @param component The component to disable * @param component The component to wrap if necessary
* @param field true if the property being enabled is a field style
* component i.e. text field or checkbox. false if the component
* is a selector style component i.e. category selector
*/ */
protected void disableIfReadOnlyOrProtected(FacesContext context, UIPropertySheet propertySheet, protected UIComponent enableForMultiValue(FacesContext context, UIPropertySheet propertySheet,
PropertySheetItem item, UIComponent component) PropertySheetItem item, UIComponent component, boolean field)
{ {
UIComponent multiValueComponent = component;
// NOTE: Associations have built in support for multiple values so we only deal
// with UIProperty instances in here currently
if (item instanceof UIProperty) if (item instanceof UIProperty)
{ {
PropertyDefinition propertyDef = getPropertyDefinition(context, // if the property is multi-valued create a multi value editor wrapper component
propertySheet.getNode(), item.getName()); String id = "multi_" + item.getName();
if (item.isReadOnly() || (propertyDef != null && propertyDef.isProtected())) multiValueComponent = context.getApplication().createComponent(
RepoConstants.ALFRESCO_FACES_MULTIVALUE_EDITOR);
FacesHelper.setupComponentId(context, multiValueComponent, id);
// set the renderer depending on whether the item is a 'field' or a 'selector'
if (field)
{ {
component.getAttributes().put("disabled", Boolean.TRUE); multiValueComponent.setRendererType(RepoConstants.ALFRESCO_FACES_FIELD_RENDERER);
} }
} else
else if (item instanceof UIAssociation || item instanceof UIChildAssociation)
{
AssociationDefinition assocDef = getAssociationDefinition(context,
propertySheet.getNode(), item.getName());
if (item.isReadOnly() || (assocDef != null && assocDef.isProtected()))
{ {
component.getAttributes().put("disabled", Boolean.TRUE); multiValueComponent.setRendererType(RepoConstants.ALFRESCO_FACES_SELECTOR_RENDERER);
// set the value binding for the wrapped component and the lastItemAdded attribute of
// the multi select component, needs to point somewhere that can hold any object, it
// will store the item last added by the user.
String expr = "#{MultiValueEditorBean.lastItemsAdded['" +
item.getName() + "']}";
ValueBinding vb = context.getApplication().createValueBinding(expr);
multiValueComponent.setValueBinding("lastItemAdded", vb);
component.setValueBinding("value", vb);
} }
// add the original component as a child of the wrapper
multiValueComponent.getChildren().add(component);
} }
return multiValueComponent;
} }
/** /**

View File

@@ -3,6 +3,7 @@ package org.alfresco.web.bean.generator;
import javax.faces.component.UIComponent; import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext; import javax.faces.context.FacesContext;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.ui.repo.RepoConstants; import org.alfresco.web.ui.repo.RepoConstants;
import org.alfresco.web.ui.repo.component.UICategorySelector; import org.alfresco.web.ui.repo.component.UICategorySelector;
@@ -29,10 +30,25 @@ public class CategoryPickerGenerator extends BaseComponentGenerator
PropertySheetItem item) PropertySheetItem item)
{ {
// create the standard component // create the standard component
UICategorySelector component = (UICategorySelector)generate(context, item.getName()); UIComponent component = generate(context, item.getName());
// make sure the property is not read only or protected // get the property definition
disableIfReadOnlyOrProtected(context, propertySheet, item, component); PropertyDefinition propertyDef = getPropertyDefinition(context,
propertySheet.getNode(), item.getName());
if (propertySheet.inEditMode() && propertyDef != null && propertyDef.isMultiValued())
{
// if the item is multi valued we need to wrap the standard component
// but only when the property sheet is in edit mode
component = enableForMultiValue(context, propertySheet, item, component, false);
}
else if (propertySheet.inEditMode() == false || item.isReadOnly() ||
(propertyDef != null && propertyDef.isProtected()))
{
// disable the component if it is read only or protected
// or if the property sheet is in view mode
component.getAttributes().put("disabled", Boolean.TRUE);
}
// setup the converter if one was specified // setup the converter if one was specified
setupConverter(context, propertySheet, item, component); setupConverter(context, propertySheet, item, component);

View File

@@ -1,11 +1,17 @@
package org.alfresco.web.bean.generator; package org.alfresco.web.bean.generator;
import javax.faces.component.UIComponent; import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UISelectBoolean; import javax.faces.component.UISelectBoolean;
import javax.faces.context.FacesContext; import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.ui.common.ComponentConstants; import org.alfresco.web.ui.common.ComponentConstants;
import org.alfresco.web.ui.common.converter.XMLDateConverter;
import org.alfresco.web.ui.repo.RepoConstants;
import org.alfresco.web.ui.repo.component.property.PropertySheetItem; import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
import org.alfresco.web.ui.repo.component.property.UIPropertySheet; import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
@@ -31,18 +37,48 @@ public class CheckboxGenerator extends BaseComponentGenerator
{ {
UIComponent component = null; UIComponent component = null;
// get the property definition
PropertyDefinition propertyDef = getPropertyDefinition(context,
propertySheet.getNode(), item.getName());
if (propertySheet.inEditMode()) if (propertySheet.inEditMode())
{ {
// use the standard component in edit mode // use the standard component in edit mode
component = generate(context, item.getName()); component = generate(context, item.getName());
// make sure the property is not read only or protected // disable the component if it is read only or protected
disableIfReadOnlyOrProtected(context, propertySheet, item, component); if (item.isReadOnly() || (propertyDef != null && propertyDef.isProtected()))
{
component.getAttributes().put("disabled", Boolean.TRUE);
}
else
{
// if the item is multi valued we need to wrap the standard component
if (propertyDef != null && propertyDef.isMultiValued())
{
component = enableForMultiValue(context, propertySheet, item, component, true);
}
}
} }
else else
{ {
// create an output text component in view mode // create an output text component in view mode
component = createOutputTextComponent(context, item.getName()); component = createOutputTextComponent(context, item.getName());
// if there is no overridden converter add a default
if (item.getConverter() == null)
{
if (propertyDef != null && propertyDef.isMultiValued())
{
// add multi-value converter if property is such
item.setConverter(RepoConstants.ALFRESCO_FACES_MULTIVALUE_CONVERTER);
}
else
{
// add the default boolean label converter
item.setConverter(RepoConstants.ALFRESCO_FACES_BOOLEAN_CONVERTER);
}
}
} }
// setup the converter if one was specified // setup the converter if one was specified

View File

@@ -6,6 +6,7 @@ import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext; import javax.faces.context.FacesContext;
import javax.faces.convert.Converter; import javax.faces.convert.Converter;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.web.app.Application; import org.alfresco.web.app.Application;
import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.ui.common.ComponentConstants; import org.alfresco.web.ui.common.ComponentConstants;
@@ -45,8 +46,23 @@ public class DatePickerGenerator extends BaseComponentGenerator
// use the standard date picker component // use the standard date picker component
component = generate(context, item.getName()); component = generate(context, item.getName());
// make sure the property is not read only or protected // get the property definition
disableIfReadOnlyOrProtected(context, propertySheet, item, component); PropertyDefinition propertyDef = getPropertyDefinition(context,
propertySheet.getNode(), item.getName());
// disable the component if it is read only or protected
if (item.isReadOnly() || (propertyDef != null && propertyDef.isProtected()))
{
component.getAttributes().put("disabled", Boolean.TRUE);
}
else
{
// if the item is multi valued we need to wrap the standard component
if (propertyDef != null && propertyDef.isMultiValued())
{
component = enableForMultiValue(context, propertySheet, item, component, true);
}
}
} }
else else
{ {
@@ -54,31 +70,31 @@ public class DatePickerGenerator extends BaseComponentGenerator
component = createOutputTextComponent(context, item.getName()); component = createOutputTextComponent(context, item.getName());
} }
// setup the converter if one was specified
setupConverter(context, propertySheet, item, component);
return component;
}
@Override
protected void setupConverter(FacesContext context, UIPropertySheet propertySheet,
PropertySheetItem item, UIComponent component)
{
if (item.getConverter() != null) if (item.getConverter() != null)
{ {
super.setupConverter(context, propertySheet, item, component); // setup the converter if one was specified
setupConverter(context, propertySheet, item, component);
} }
else else
{ {
// use the default converter for the date component
// we can cast this as we know it is an UIOutput type // we can cast this as we know it is an UIOutput type
((UIOutput)component).setConverter(getConverter(context)); ((UIOutput)component).setConverter(getDefaultConverter(context));
} }
return component;
} }
protected Converter getConverter(FacesContext context) /**
* Retrieves the default converter for the date component
*
* @param context FacesContext
* @return XMLDateConverter
*/
protected Converter getDefaultConverter(FacesContext context)
{ {
XMLDateConverter converter = (XMLDateConverter)context.getApplication(). XMLDateConverter converter = (XMLDateConverter)context.getApplication().
createConverter(RepoConstants.ALFRESCO_FACES_XMLDATA_CONVERTER); createConverter(RepoConstants.ALFRESCO_FACES_XMLDATE_CONVERTER);
converter.setType("date"); converter.setType("date");
converter.setPattern(Application.getMessage(context, MSG_DATE)); converter.setPattern(Application.getMessage(context, MSG_DATE));
return converter; return converter;

View File

@@ -23,15 +23,21 @@ public class DateTimePickerGenerator extends DatePickerGenerator
UIComponent component = super.generate(context, id); UIComponent component = super.generate(context, id);
// add the attribute to show the time // add the attribute to show the time
component.getAttributes().put("showTime", Boolean.valueOf(true)); component.getAttributes().put("showTime", Boolean.TRUE);
return component; return component;
} }
protected Converter getConverter(FacesContext context) /**
* Retrieves the default converter for the date time component
*
* @param context FacesContext
* @return XMLDateConverter
*/
protected Converter getDefaultConverter(FacesContext context)
{ {
XMLDateConverter converter = (XMLDateConverter)context.getApplication(). XMLDateConverter converter = (XMLDateConverter)context.getApplication().
createConverter(RepoConstants.ALFRESCO_FACES_XMLDATA_CONVERTER); createConverter(RepoConstants.ALFRESCO_FACES_XMLDATE_CONVERTER);
converter.setType("both"); converter.setType("both");
converter.setPattern(Application.getMessage(context, MSG_DATE_TIME)); converter.setPattern(Application.getMessage(context, MSG_DATE_TIME));
return converter; return converter;

View File

@@ -24,7 +24,6 @@ public class LabelGenerator extends BaseComponentGenerator
UIComponent component = generate(context, "label_" + item.getName()); UIComponent component = generate(context, "label_" + item.getName());
// TODO: Turn the label red if the field is required // TODO: Turn the label red if the field is required
// set the label id to be label_<var>_<name>
// setup the 'for' attribute to associate with it the control // setup the 'for' attribute to associate with it the control
return component; return component;

View File

@@ -1,13 +1,11 @@
package org.alfresco.web.bean.generator; package org.alfresco.web.bean.generator;
import javax.faces.component.UIComponent; import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext; import javax.faces.context.FacesContext;
import javax.faces.el.ValueBinding; import javax.faces.el.ValueBinding;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.ui.common.ComponentConstants;
import org.alfresco.web.ui.common.component.UIImagePicker;
import org.alfresco.web.ui.common.component.UIListItems; import org.alfresco.web.ui.common.component.UIListItems;
import org.alfresco.web.ui.repo.RepoConstants; import org.alfresco.web.ui.repo.RepoConstants;
import org.alfresco.web.ui.repo.component.property.PropertySheetItem; import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
@@ -41,12 +39,16 @@ public class SpaceIconPickerGenerator extends BaseComponentGenerator
public UIComponent generate(FacesContext context, UIPropertySheet propertySheet, public UIComponent generate(FacesContext context, UIPropertySheet propertySheet,
PropertySheetItem item) PropertySheetItem item)
{ {
UIOutput component = null; UIComponent component = null;
// get the property definition
PropertyDefinition propertyDef = getPropertyDefinition(context,
propertySheet.getNode(), item.getName());
if (propertySheet.inEditMode()) if (propertySheet.inEditMode())
{ {
// use the standard component in edit mode // use the standard component in edit mode
component = (UIImagePicker)generate(context, item.getName()); component = generate(context, item.getName());
// create the list items child component // create the list items child component
UIListItems items = (UIListItems)context.getApplication(). UIListItems items = (UIListItems)context.getApplication().
@@ -75,13 +77,31 @@ public class SpaceIconPickerGenerator extends BaseComponentGenerator
// add the list items component to the image picker component // add the list items component to the image picker component
component.getChildren().add(items); component.getChildren().add(items);
// make sure the property is not read only or protected // disable the component if it is read only or protected
disableIfReadOnlyOrProtected(context, propertySheet, item, component); if (item.isReadOnly() || (propertyDef != null && propertyDef.isProtected()))
{
component.getAttributes().put("disabled", Boolean.TRUE);
}
else
{
// if the item is multi valued we need to wrap the standard component
if (propertyDef != null && propertyDef.isMultiValued())
{
component = enableForMultiValue(context, propertySheet, item, component, true);
}
}
} }
else else
{ {
// create an output text component in view mode // create an output text component in view mode
component = createOutputTextComponent(context, item.getName()); component = createOutputTextComponent(context, item.getName());
// if the property is multi-valued and there isn't a custom converter
// specified, add the MultiValue converter as a default
if (propertyDef.isMultiValued() && item.getConverter() == null)
{
item.setConverter(RepoConstants.ALFRESCO_FACES_MULTIVALUE_CONVERTER);
}
} }
// setup the converter if one was specified // setup the converter if one was specified

View File

@@ -5,8 +5,10 @@ import javax.faces.component.UIInput;
import javax.faces.component.UIOutput; import javax.faces.component.UIOutput;
import javax.faces.context.FacesContext; import javax.faces.context.FacesContext;
import org.alfresco.service.cmr.dictionary.PropertyDefinition;
import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.app.servlet.FacesHelper;
import org.alfresco.web.ui.common.ComponentConstants; import org.alfresco.web.ui.common.ComponentConstants;
import org.alfresco.web.ui.repo.RepoConstants;
import org.alfresco.web.ui.repo.component.property.PropertySheetItem; import org.alfresco.web.ui.repo.component.property.PropertySheetItem;
import org.alfresco.web.ui.repo.component.property.UIPropertySheet; import org.alfresco.web.ui.repo.component.property.UIPropertySheet;
@@ -34,18 +36,40 @@ public class TextFieldGenerator extends BaseComponentGenerator
{ {
UIComponent component = null; UIComponent component = null;
// get the property definition
PropertyDefinition propertyDef = getPropertyDefinition(context,
propertySheet.getNode(), item.getName());
if (propertySheet.inEditMode()) if (propertySheet.inEditMode())
{ {
// use the standard component in edit mode // use the standard component in edit mode
component = generate(context, item.getName()); component = generate(context, item.getName());
// make sure the property is not read only or protected // disable the component if it is read only or protected
disableIfReadOnlyOrProtected(context, propertySheet, item, component); if (item.isReadOnly() || (propertyDef != null && propertyDef.isProtected()))
{
component.getAttributes().put("disabled", Boolean.TRUE);
}
else
{
// if the item is multi valued we need to wrap the standard component
if (propertyDef != null && propertyDef.isMultiValued())
{
component = enableForMultiValue(context, propertySheet, item, component, true);
}
}
} }
else else
{ {
// create an output text component in view mode // create an output text component in view mode
component = createOutputTextComponent(context, item.getName()); component = createOutputTextComponent(context, item.getName());
// if the property is multi-valued and there isn't a custom converter
// specified, add the MultiValue converter as a default
if (propertyDef != null && propertyDef.isMultiValued() && item.getConverter() == null)
{
item.setConverter(RepoConstants.ALFRESCO_FACES_MULTIVALUE_CONVERTER);
}
} }
// setup the converter if one was specified // setup the converter if one was specified

View File

@@ -0,0 +1,100 @@
/*
* 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.common.converter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import org.alfresco.web.app.Application;
import org.alfresco.web.ui.repo.RepoConstants;
/**
* Converter class to convert a List of multiple values into a comma
* separated list.
*
* @author gavinc
*/
public class MultiValueConverter implements Converter
{
/**
* <p>The standard converter id for this converter.</p>
*/
public static final String CONVERTER_ID = "org.alfresco.faces.MultiValueConverter";
/**
* @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String)
*/
public Object getAsObject(FacesContext context, UIComponent component, String value)
throws ConverterException
{
List<String> items = new ArrayList<String>();
StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreTokens())
{
items.add(tokenizer.nextToken());
}
return items;
}
/**
* @see javax.faces.convert.Converter#getAsString(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)
*/
public String getAsString(FacesContext context, UIComponent component, Object value)
throws ConverterException
{
String result = null;
if (value instanceof Collection)
{
StringBuilder buffer = new StringBuilder();
for (Object obj : (Collection)value)
{
if (buffer.length() != 0)
{
buffer.append(", ");
}
if (obj instanceof Boolean)
{
Converter boolLabel = context.getApplication().createConverter(
RepoConstants.ALFRESCO_FACES_BOOLEAN_CONVERTER);
buffer.append(boolLabel.getAsString(context, component, obj));
}
else
{
buffer.append(obj.toString());
}
}
result = buffer.toString();
}
else if (value != null)
{
result = value.toString();
}
return result;
}
}

View File

@@ -17,6 +17,7 @@
package org.alfresco.web.ui.common.converter; package org.alfresco.web.ui.common.converter;
import java.util.Date; import java.util.Date;
import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.TimeZone; import java.util.TimeZone;
@@ -36,7 +37,7 @@ public class XMLDateConverter extends DateTimeConverter
/** /**
* <p>The standard converter id for this converter.</p> * <p>The standard converter id for this converter.</p>
*/ */
public static final String CONVERTER_ID = "org.alfresco.faces.XMLDataConverter"; public static final String CONVERTER_ID = "org.alfresco.faces.XMLDateConverter";
/** /**
* @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String) * @see javax.faces.convert.Converter#getAsObject(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.String)
@@ -58,6 +59,21 @@ public class XMLDateConverter extends DateTimeConverter
Date date = ISO8601DateFormat.parse((String)value); Date date = ISO8601DateFormat.parse((String)value);
str = super.getAsString(context, component, date); str = super.getAsString(context, component, date);
} }
else if (value instanceof List)
{
StringBuilder buffer = new StringBuilder();
for (Object date : ((List)value))
{
if (buffer.length() != 0)
{
buffer.append(", ");
}
buffer.append(super.getAsString(context, component, date));
}
str = buffer.toString();
}
else else
{ {
str = super.getAsString(context, component, value); str = super.getAsString(context, component, value);

View File

@@ -21,6 +21,8 @@ package org.alfresco.web.ui.repo;
*/ */
public final class RepoConstants public final class RepoConstants
{ {
// TODO: move these into the respective components as static members - as per JSF spec
public static final String ALFRESCO_FACES_ASSOCIATION = "org.alfresco.faces.Association"; public static final String ALFRESCO_FACES_ASSOCIATION = "org.alfresco.faces.Association";
public static final String ALFRESCO_FACES_CHILD_ASSOCIATION = "org.alfresco.faces.ChildAssociation"; public static final String ALFRESCO_FACES_CHILD_ASSOCIATION = "org.alfresco.faces.ChildAssociation";
public static final String ALFRESCO_FACES_PROPERTY = "org.alfresco.faces.Property"; public static final String ALFRESCO_FACES_PROPERTY = "org.alfresco.faces.Property";
@@ -31,8 +33,13 @@ public final class RepoConstants
public static final String ALFRESCO_FACES_CATEGORY_SELECTOR = "org.alfresco.faces.CategorySelector"; public static final String ALFRESCO_FACES_CATEGORY_SELECTOR = "org.alfresco.faces.CategorySelector";
public static final String ALFRESCO_FACES_IMAGE_PICKER = "org.alfresco.faces.ImagePicker"; public static final String ALFRESCO_FACES_IMAGE_PICKER = "org.alfresco.faces.ImagePicker";
public static final String ALFRESCO_FACES_LIST_ITEMS = "org.alfresco.faces.ListItems"; public static final String ALFRESCO_FACES_LIST_ITEMS = "org.alfresco.faces.ListItems";
public static final String ALFRESCO_FACES_MULTIVALUE_EDITOR = "org.alfresco.faces.MultiValueEditor";
public static final String ALFRESCO_FACES_FIELD_RENDERER = "org.alfresco.faces.Field";
public static final String ALFRESCO_FACES_SELECTOR_RENDERER = "org.alfresco.faces.Selector";
public static final String ALFRESCO_FACES_RADIO_PANEL_RENDERER = "org.alfresco.faces.RadioPanel"; public static final String ALFRESCO_FACES_RADIO_PANEL_RENDERER = "org.alfresco.faces.RadioPanel";
public static final String ALFRESCO_FACES_XMLDATA_CONVERTER = "org.alfresco.faces.XMLDataConverter"; public static final String ALFRESCO_FACES_XMLDATE_CONVERTER = "org.alfresco.faces.XMLDateConverter";
public static final String ALFRESCO_FACES_MULTIVALUE_CONVERTER = "org.alfresco.faces.MultiValueConverter";
public static final String ALFRESCO_FACES_BOOLEAN_CONVERTER = "org.alfresco.faces.BooleanLabelConverter";
public static final String GENERATOR_LABEL = "LabelGenerator"; public static final String GENERATOR_LABEL = "LabelGenerator";
public static final String GENERATOR_TEXT_FIELD = "TextFieldGenerator"; public static final String GENERATOR_TEXT_FIELD = "TextFieldGenerator";

View File

@@ -18,6 +18,7 @@ package org.alfresco.web.ui.repo.component;
import java.io.IOException; import java.io.IOException;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.faces.component.EditableValueHolder; import javax.faces.component.EditableValueHolder;
@@ -262,6 +263,31 @@ public abstract class AbstractItemSelector extends UIInput
{ {
nodeRef = new NodeRef((String)val); nodeRef = new NodeRef((String)val);
} }
else if (val instanceof List)
{
// build a comma separated list of node names
List nodes = (List)val;
StringBuilder buffer = new StringBuilder();
for (Object obj : nodes)
{
if (buffer.length() != 0)
{
buffer.append(", ");
}
if (obj instanceof NodeRef)
{
buffer.append(Repository.getNameForNode(service, (NodeRef)obj));
}
else
{
buffer.append(obj.toString());
}
}
// write out to response
out.write(buffer.toString());
}
} }
// if there is a value show it's name // if there is a value show it's name

View File

@@ -18,8 +18,9 @@
package org.alfresco.web.ui.repo.component; package org.alfresco.web.ui.repo.component;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List; import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent; import javax.faces.component.UIComponent;
import javax.faces.component.UIInput; import javax.faces.component.UIInput;
@@ -30,8 +31,9 @@ import javax.faces.event.ActionEvent;
import javax.faces.event.FacesEvent; import javax.faces.event.FacesEvent;
import org.alfresco.web.app.Application; import org.alfresco.web.app.Application;
import org.apache.commons.logging.Log; import org.alfresco.web.app.servlet.FacesHelper;
import org.apache.commons.logging.LogFactory; import org.alfresco.web.ui.common.renderer.DatePickerRenderer;
import org.alfresco.web.ui.repo.RepoConstants;
/** /**
* This component wraps a standard component to give it multi value capabilities. * This component wraps a standard component to give it multi value capabilities.
@@ -44,12 +46,10 @@ import org.apache.commons.logging.LogFactory;
*/ */
public class UIMultiValueEditor extends UIInput 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_SELECTED_ITEMS = "selected_items";
private static final String MSG_NO_SELECTED_ITEMS = "no_selected_items"; private static final String MSG_NO_SELECTED_ITEMS = "no_selected_items";
private static final String MSG_SELECT_ITEM = "select_an_item"; private static final String MSG_SELECT_ITEM = "select_an_item";
public final static String ACTION_SEPARATOR = ";"; public final static String ACTION_SEPARATOR = ";";
public final static int ACTION_NONE = -1; public final static int ACTION_NONE = -1;
public final static int ACTION_REMOVE = 0; public final static int ACTION_REMOVE = 0;
@@ -71,7 +71,7 @@ public class UIMultiValueEditor extends UIInput
*/ */
public UIMultiValueEditor() public UIMultiValueEditor()
{ {
setRendererType("org.alfresco.faces.List"); setRendererType(RepoConstants.ALFRESCO_FACES_SELECTOR_RENDERER);
} }
/** /**
@@ -79,7 +79,7 @@ public class UIMultiValueEditor extends UIInput
*/ */
public String getFamily() public String getFamily()
{ {
return "org.alfresco.faces.MultiValueEditor"; return RepoConstants.ALFRESCO_FACES_MULTIVALUE_EDITOR;
} }
/** /**
@@ -303,15 +303,48 @@ public class UIMultiValueEditor extends UIInput
setSubmittedValue(items); setSubmittedValue(items);
} }
items.add(getLastItemAdded()); Object addedItem = null;
this.addingNewItem = Boolean.FALSE;
// get hold of the value binding for the lastItemAdded property if (getRendererType().equals(RepoConstants.ALFRESCO_FACES_FIELD_RENDERER))
// 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); UIInput childComponent = (UIInput)this.getChildren().get(0);
// as the 'field' is being submitted in the same request we can go
// directly to the submitted value to find the entered value
addedItem = childComponent.getSubmittedValue();
if (childComponent.getRendererType() != null &&
childComponent.getRendererType().equals(
RepoConstants.ALFRESCO_FACES_DATE_PICKER_RENDERER))
{
// the submitted value for the date is in it's raw form, convert to date
int[] parts = (int[])addedItem;
Calendar date = new GregorianCalendar(parts[0], parts[1], parts[2],
parts[3], parts[4]);
addedItem = date.getTime();
}
// conversely, we can erase the submitted value
childComponent.setSubmittedValue(null);
}
else
{
addedItem = 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);
}
}
if (addedItem != null)
{
items.add(addedItem);
} }
break; break;
@@ -335,9 +368,16 @@ public class UIMultiValueEditor extends UIInput
*/ */
public boolean getRendersChildren() public boolean getRendersChildren()
{ {
// only show the wrapped component when the add button has been clicked if (getRendererType().equals(RepoConstants.ALFRESCO_FACES_FIELD_RENDERER))
{
return !this.addingNewItem.booleanValue(); // if we are using the field renderer always render the childre
return false;
}
else
{
// only show the wrapped component when the add button has been clicked
return !this.addingNewItem.booleanValue();
}
} }
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------

View File

@@ -179,15 +179,7 @@ public class UIProperty extends PropertySheetItem
{ {
logger.warn("Setting property " + propDef.getName().toString() + " to read-only as it can not be edited"); logger.warn("Setting property " + propDef.getName().toString() + " to read-only as it can not be edited");
control.getAttributes().put("disabled", Boolean.TRUE); control.getAttributes().put("disabled", Boolean.TRUE);
} }
// for now we can not handle multi valued properties in the client so if the property
// is defined as such make sure it is rendered as disabled
if (propDef.isMultiValued())
{
logger.warn("Setting property " + propDef.getName().toString() + " to read-only, it can not be edited as it is defined as multi-valued");
control.getAttributes().put("disabled", Boolean.TRUE);
}
} }
// create and set the value binding // create and set the value binding

View File

@@ -1,299 +1,323 @@
/* /*
* Copyright (C) 2005 Alfresco, Inc. * Copyright (C) 2005 Alfresco, Inc.
* *
* Licensed under the Mozilla Public License version 1.1 * Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a * with a permitted attribution clause. You may obtain a
* copy of the License at * copy of the License at
* *
* http://www.alfresco.org/legal/license.txt * http://www.alfresco.org/legal/license.txt
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific * either express or implied. See the License for the specific
* language governing permissions and limitations under the * language governing permissions and limitations under the
* License. * License.
*/ */
package org.alfresco.web.ui.repo.renderer; package org.alfresco.web.ui.repo.renderer;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.Date;
import java.util.Map; import java.util.List;
import java.util.Map;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext; import javax.faces.component.UIComponent;
import javax.faces.context.ResponseWriter; import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.alfresco.service.cmr.repository.NodeRef; import javax.faces.convert.Converter;
import org.alfresco.service.cmr.repository.NodeService; import javax.faces.convert.DateTimeConverter;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository; import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.web.ui.common.Utils; import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.web.ui.common.renderer.BaseRenderer; import org.alfresco.util.ISO8601DateFormat;
import org.alfresco.web.ui.repo.component.UIMultiValueEditor; import org.alfresco.web.app.Application;
import org.alfresco.web.ui.repo.component.UIMultiValueEditor.MultiValueEditorEvent; import org.alfresco.web.app.servlet.FacesHelper;
import org.apache.commons.logging.Log; import org.alfresco.web.bean.repository.Repository;
import org.apache.commons.logging.LogFactory; import org.alfresco.web.ui.common.Utils;
import org.alfresco.web.ui.common.converter.XMLDateConverter;
/** import org.alfresco.web.ui.common.renderer.BaseRenderer;
* Renders the MultiValueEditor component as a list of options that can be import org.alfresco.web.ui.repo.RepoConstants;
* removed using a Remove button import org.alfresco.web.ui.repo.component.UIMultiValueEditor;
* import org.alfresco.web.ui.repo.component.UIMultiValueEditor.MultiValueEditorEvent;
* @author gavinc import org.apache.commons.logging.Log;
*/ import org.apache.commons.logging.LogFactory;
public class MultiValueListEditorRenderer extends BaseRenderer
{ /**
private static Log logger = LogFactory.getLog(MultiValueListEditorRenderer.class); * Base class for renderers of the MultiValueEditor component.
* The current items are displayed as a list of options that can be
/** I18N message strings */ * removed using a Remove button.
private final static String MSG_REMOVE = "remove"; *
private final static String MSG_SELECT_BUTTON = "select_button"; * @author gavinc
private final static String MSG_ADD_TO_LIST_BUTTON = "add_to_list_button"; */
public abstract class BaseMultiValueRenderer extends BaseRenderer
private boolean highlightedRow; {
private static Log logger = LogFactory.getLog(BaseMultiValueRenderer.class);
// ------------------------------------------------------------------------------
// Renderer implemenation /** I18N message strings */
protected final static String MSG_REMOVE = "remove";
/** protected final static String MSG_SELECT_BUTTON = "select_button";
* @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext, javax.faces.component.UIComponent) protected final static String MSG_ADD_TO_LIST_BUTTON = "add_to_list_button";
*/
public void decode(FacesContext context, UIComponent component) protected boolean highlightedRow;
{
Map requestMap = context.getExternalContext().getRequestParameterMap(); // ------------------------------------------------------------------------------
Map valuesMap = context.getExternalContext().getRequestParameterValuesMap(); // Renderer implemenation
String fieldId = getHiddenFieldName(component);
String value = (String)requestMap.get(fieldId); /**
* @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
int action = UIMultiValueEditor.ACTION_NONE; */
int removeIndex = -1; public void decode(FacesContext context, UIComponent component)
if (value != null && value.length() != 0) {
{ Object obj = FacesHelper.getManagedBean(context, "MultiValueEditorBean");
// break up the action into it's parts
int sepIdx = value.indexOf(UIMultiValueEditor.ACTION_SEPARATOR); Map requestMap = context.getExternalContext().getRequestParameterMap();
if (sepIdx != -1) Map valuesMap = context.getExternalContext().getRequestParameterValuesMap();
{ String fieldId = getHiddenFieldName(component);
action = Integer.parseInt(value.substring(0, sepIdx)); String value = (String)requestMap.get(fieldId);
removeIndex = Integer.parseInt(value.substring(sepIdx+1));
} int action = UIMultiValueEditor.ACTION_NONE;
else int removeIndex = -1;
{ if (value != null && value.length() != 0)
action = Integer.parseInt(value); {
} // break up the action into it's parts
} int sepIdx = value.indexOf(UIMultiValueEditor.ACTION_SEPARATOR);
if (sepIdx != -1)
if (action != UIMultiValueEditor.ACTION_NONE) {
{ action = Integer.parseInt(value.substring(0, sepIdx));
MultiValueEditorEvent event = new MultiValueEditorEvent(component, action, removeIndex); removeIndex = Integer.parseInt(value.substring(sepIdx+1));
component.queueEvent(event); }
} else
{
super.decode(context, component); action = Integer.parseInt(value);
} }
}
/**
* @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext, javax.faces.component.UIComponent) if (action != UIMultiValueEditor.ACTION_NONE)
*/ {
public void encodeBegin(FacesContext context, UIComponent component) throws IOException MultiValueEditorEvent event = new MultiValueEditorEvent(component, action, removeIndex);
{ component.queueEvent(event);
if (component.isRendered() == false) }
{
return; super.decode(context, component);
} }
// reset the highlighted row flag /**
this.highlightedRow = false; * @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
*/
if (component instanceof UIMultiValueEditor) public void encodeBegin(FacesContext context, UIComponent component) throws IOException
{ {
ResponseWriter out = context.getResponseWriter(); if (component.isRendered() == false)
Map attrs = component.getAttributes(); {
String clientId = component.getClientId(context); return;
UIMultiValueEditor editor = (UIMultiValueEditor)component; }
// start outer table // reset the highlighted row flag
out.write("<table border='0' cellspacing='4' cellpadding='4' class='selector'"); this.highlightedRow = false;
this.outputAttribute(out, attrs.get("style"), "style");
this.outputAttribute(out, attrs.get("styleClass"), "styleClass"); if (component instanceof UIMultiValueEditor)
out.write(">"); {
ResponseWriter out = context.getResponseWriter();
// show the select an item message Map attrs = component.getAttributes();
out.write("<tr><td>"); String clientId = component.getClientId(context);
out.write("1. "); UIMultiValueEditor editor = (UIMultiValueEditor)component;
out.write(editor.getSelectItemMsg());
out.write("</td></tr>"); // start outer table
out.write("<table border='0' cellspacing='3' cellpadding='3' class='selector'");
if (editor.getAddingNewItem()) this.outputAttribute(out, attrs.get("style"), "style");
{ this.outputAttribute(out, attrs.get("styleClass"), "styleClass");
out.write("<tr><td style='padding-left:8px'>"); out.write(">");
}
else // render the area before the wrapped component
{ renderPreWrappedComponent(context, out, editor);
out.write("<tr><td style='padding-left:8px;'><input type='submit' value='"); }
out.write(Application.getMessage(context, MSG_SELECT_BUTTON)); }
out.write("' onclick=\"");
out.write(generateFormSubmit(context, component, Integer.toString(UIMultiValueEditor.ACTION_SELECT))); /**
out.write("\"/></td></tr>"); * @see javax.faces.render.Renderer#encodeEnd(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
} */
} public void encodeEnd(FacesContext context, UIComponent component) throws IOException
} {
if (component instanceof UIMultiValueEditor)
/** {
* @see javax.faces.render.Renderer#encodeEnd(javax.faces.context.FacesContext, javax.faces.component.UIComponent) ResponseWriter out = context.getResponseWriter();
*/ UIMultiValueEditor editor = (UIMultiValueEditor)component;
public void encodeEnd(FacesContext context, UIComponent component) throws IOException
{ // get hold of the node service
if (component instanceof UIMultiValueEditor) NodeService nodeService = Repository.getServiceRegistry(context).getNodeService();
{
ResponseWriter out = context.getResponseWriter(); // render the area between the component and current items list
UIMultiValueEditor editor = (UIMultiValueEditor)component; renderPostWrappedComponent(context, out, editor);
// get hold of the node service // show the currently selected items
NodeService nodeService = Repository.getServiceRegistry(context).getNodeService(); out.write("<tr><td style='padding-top:8px'>");
out.write(editor.getSelectedItemsMsg());
if (editor.getAddingNewItem()) out.write("</td></tr>");
{
out.write("</td></tr>"); out.write("<tr><td><table cellspacing='0' cellpadding='2' border='0' class='selectedItems'>");
} out.write("<tr><td colspan='2' class='selectedItemsHeader'>");
out.write(Application.getMessage(context, "name"));
// show the add to list button but only if something has been selected out.write("</td></tr>");
out.write("<tr><td>2. <input type='submit'");
if (editor.getAddingNewItem() == false && editor.getLastItemAdded() != null || List currentItems = (List)editor.getValue();;
editor.getLastItemAdded() == null) if (currentItems != null && currentItems.size() > 0)
{ {
out.write(" disabled='true'"); for (int x = 0; x < currentItems.size(); x++)
} {
out.write(" value='"); Object obj = currentItems.get(x);
out.write(Application.getMessage(context, MSG_ADD_TO_LIST_BUTTON)); if (obj != null)
out.write("' onclick=\""); {
out.write(generateFormSubmit(context, component, Integer.toString(UIMultiValueEditor.ACTION_ADD))); if (obj instanceof NodeRef)
out.write("\"/></td></tr>"); {
if (nodeService.exists((NodeRef)obj))
out.write("<tr><td style='padding-top:8px'>"); {
out.write(editor.getSelectedItemsMsg()); renderExistingItem(context, component, out, nodeService, x, obj);
out.write("</td></tr>"); }
else
// show the current items {
out.write("<tr><td><table cellspacing='0' cellpadding='2' border='0' class='selectedItems'>"); // remove invalid NodeRefs from the list
out.write("<tr><td colspan='2' class='selectedItemsHeader'>"); currentItems.remove(x);
out.write(Application.getMessage(context, "name")); }
out.write("</td></tr>"); }
else
List currentItems = (List)editor.getValue(); {
if (currentItems != null && currentItems.size() > 0) renderExistingItem(context, component, out, nodeService, x, obj);
{ }
for (int x = 0; x < currentItems.size(); x++) }
{ }
Object obj = currentItems.get(x); }
if (obj != null) else
{ {
if (obj instanceof NodeRef) out.write("<tr><td class='selectedItemsRow'>");
{ out.write(editor.getNoSelectedItemsMsg());
if (nodeService.exists((NodeRef)obj)) out.write("</td></tr>");
{ }
renderExistingItem(context, component, out, nodeService, x, obj);
} // close tables
else out.write("</table></td></tr></table>");
{ }
// remove invalid NodeRefs from the list }
currentItems.remove(x);
} /**
} * Renders the area of the component before the wrapped component appears.
else *
{ * @param context FacesContext
renderExistingItem(context, component, out, nodeService, x, obj); * @param out The ResponseWriter to write to
} * @param editor The multi value editor component
} */
} protected abstract void renderPreWrappedComponent(FacesContext context,
} ResponseWriter out, UIMultiValueEditor editor) throws IOException;
else
{ /**
out.write("<tr><td class='selectedItemsRow'>"); * Renders the area of the component after the wrapped component but before the list
out.write(editor.getNoSelectedItemsMsg()); * of currently selected values.
out.write("</td></tr>"); *
} * @param context FacesContext
* @param out The ResponseWriter to write to
// close tables * @param editor The multi value editor component
out.write("</table></td></tr></table>"); */
} protected abstract void renderPostWrappedComponent(FacesContext context,
} ResponseWriter out, UIMultiValueEditor editor) throws IOException;
/** /**
* Renders an existing item with a remove button * Renders an existing item with a remove button
* *
* @param context FacesContext * @param context FacesContext
* @param component The UIComponent * @param component The UIComponent
* @param out Writer to write output to * @param out Writer to write output to
* @param nodeService The NodeService * @param nodeService The NodeService
* @param key The key of the item * @param key The key of the item
* @param value The item's value * @param value The item's value
* @throws IOException * @throws IOException
*/ */
protected void renderExistingItem(FacesContext context, UIComponent component, ResponseWriter out, protected void renderExistingItem(FacesContext context, UIComponent component, ResponseWriter out,
NodeService nodeService, int index, Object value) throws IOException NodeService nodeService, int index, Object value) throws IOException
{ {
out.write("<tr><td class='"); out.write("<tr><td class='");
if (this.highlightedRow) if (this.highlightedRow)
{ {
out.write("selectedItemsRowAlt"); out.write("selectedItemsRowAlt");
} }
else else
{ {
out.write("selectedItemsRow"); out.write("selectedItemsRow");
} }
out.write("'>"); out.write("'>");
if (value instanceof NodeRef) if (value instanceof NodeRef)
{ {
out.write(Repository.getNameForNode(nodeService, (NodeRef)value)); out.write(Repository.getNameForNode(nodeService, (NodeRef)value));
} }
else else if (value instanceof Date)
{ {
out.write(value.toString()); XMLDateConverter converter = (XMLDateConverter)context.getApplication().
} createConverter(RepoConstants.ALFRESCO_FACES_XMLDATE_CONVERTER);
UIComponent childComponent = (UIComponent)component.getChildren().get(0);
out.write("&nbsp;&nbsp;"); Boolean showTime = (Boolean)childComponent.getAttributes().get("showTime");
out.write("</td><td class='"); if (showTime != null && showTime.booleanValue())
if (this.highlightedRow) {
{ converter.setPattern(Application.getMessage(context, "date_time_pattern"));
out.write("selectedItemsRowAlt"); }
} else
else {
{ converter.setPattern(Application.getMessage(context, "date_pattern"));
out.write("selectedItemsRow"); }
}
out.write("'><a href='#' title='"); out.write(converter.getAsString(context, childComponent, value));
out.write(Application.getMessage(context, MSG_REMOVE)); }
out.write("' onclick=\""); else if (value instanceof Boolean)
out.write(generateFormSubmit(context, component, UIMultiValueEditor.ACTION_REMOVE + UIMultiValueEditor.ACTION_SEPARATOR + index)); {
out.write("\"><img src='"); Converter converter = context.getApplication().createConverter(
out.write(context.getExternalContext().getRequestContextPath()); RepoConstants.ALFRESCO_FACES_BOOLEAN_CONVERTER);
out.write("/images/icons/delete.gif' border='0' width='13' height='16'/></a>"); out.write(converter.getAsString(context,
(UIComponent)component.getChildren().get(0), value));
this.highlightedRow = !this.highlightedRow; }
} else
{
/** out.write(value.toString());
* We use a hidden field per picker instance on the page. }
*
* @return hidden field name out.write("&nbsp;&nbsp;");
*/ out.write("</td><td class='");
private String getHiddenFieldName(UIComponent component) if (this.highlightedRow)
{ {
return component.getClientId(FacesContext.getCurrentInstance()); out.write("selectedItemsRowAlt");
} }
else
/** {
* Generate FORM submit JavaScript for the specified action out.write("selectedItemsRow");
* }
* @param context FacesContext out.write("'><a href='#' title='");
* @param component The UIComponent out.write(Application.getMessage(context, MSG_REMOVE));
* @param action Action string out.write("' onclick=\"");
* out.write(generateFormSubmit(context, component, UIMultiValueEditor.ACTION_REMOVE + UIMultiValueEditor.ACTION_SEPARATOR + index));
* @return FORM submit JavaScript out.write("\"><img src='");
*/ out.write(context.getExternalContext().getRequestContextPath());
private String generateFormSubmit(FacesContext context, UIComponent component, String action) out.write("/images/icons/delete.gif' border='0' width='13' height='16'/></a>");
{
return Utils.generateFormSubmit(context, component, getHiddenFieldName(component), action); this.highlightedRow = !this.highlightedRow;
} }
}
/**
* We use a hidden field per picker instance on the page.
*
* @return hidden field name
*/
protected String getHiddenFieldName(UIComponent component)
{
return component.getClientId(FacesContext.getCurrentInstance());
}
/**
* Generate FORM submit JavaScript for the specified action
*
* @param context FacesContext
* @param component The UIComponent
* @param action Action string
*
* @return FORM submit JavaScript
*/
protected String generateFormSubmit(FacesContext context, UIComponent component, String action)
{
return Utils.generateFormSubmit(context, component, getHiddenFieldName(component), action);
}
}

View File

@@ -0,0 +1,41 @@
package org.alfresco.web.ui.repo.renderer;
import java.io.IOException;
import java.util.List;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.alfresco.web.app.Application;
import org.alfresco.web.ui.repo.component.UIMultiValueEditor;
/**
* Renders the MultiValueEditor component for use with field components
* i.e. text, checkboxes, lists etc.
*
* This renderer does not show a "select item" message or a select button,
* the wrapped component is shown immediately with an add to list button
* after it.
*
* @author gavinc
*/
public class MultiValueFieldRenderer extends BaseMultiValueRenderer
{
@Override
protected void renderPreWrappedComponent(FacesContext context, ResponseWriter out,
UIMultiValueEditor editor) throws IOException
{
out.write("<tr><td>");
}
@Override
protected void renderPostWrappedComponent(FacesContext context, ResponseWriter out,
UIMultiValueEditor editor) throws IOException
{
out.write("&nbsp;<input type='submit' value='");
out.write(Application.getMessage(context, MSG_ADD_TO_LIST_BUTTON));
out.write("' onclick=\"");
out.write(generateFormSubmit(context, editor, Integer.toString(UIMultiValueEditor.ACTION_ADD)));
out.write("\"/></td></tr>");
}
}

View File

@@ -0,0 +1,68 @@
package org.alfresco.web.ui.repo.renderer;
import java.io.IOException;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.alfresco.web.app.Application;
import org.alfresco.web.ui.repo.component.UIMultiValueEditor;
/**
* Renders the MultiValueEditor component for use with picker components.
*
* This renderer shows a "select items" message and a select button. When
* the select button is pressed the wrapped component will appear and the
* add to list button will be enabled.
*
* @author gavinc
*/
public class MultiValueSelectorRenderer extends BaseMultiValueRenderer
{
@Override
protected void renderPreWrappedComponent(FacesContext context, ResponseWriter out,
UIMultiValueEditor editor) throws IOException
{
// show the select an item message
out.write("<tr><td>");
out.write("1. ");
out.write(editor.getSelectItemMsg());
out.write("</td></tr>");
if (editor.getAddingNewItem())
{
out.write("<tr><td style='padding-left:8px'>");
}
else
{
out.write("<tr><td style='padding-left:8px;'><input type='submit' value='");
out.write(Application.getMessage(context, MSG_SELECT_BUTTON));
out.write("' onclick=\"");
out.write(generateFormSubmit(context, editor, Integer.toString(UIMultiValueEditor.ACTION_SELECT)));
out.write("\"/></td></tr>");
}
}
@Override
protected void renderPostWrappedComponent(FacesContext context, ResponseWriter out,
UIMultiValueEditor editor) throws IOException
{
if (editor.getAddingNewItem())
{
out.write("</td></tr>");
}
// show the add to list button but only if something has been selected
out.write("<tr><td>2. <input type='submit'");
if (editor.getAddingNewItem() == false && editor.getLastItemAdded() != null ||
editor.getLastItemAdded() == null)
{
out.write(" disabled='true'");
}
out.write(" value='");
out.write(Application.getMessage(context, MSG_ADD_TO_LIST_BUTTON));
out.write("' onclick=\"");
out.write(generateFormSubmit(context, editor, Integer.toString(UIMultiValueEditor.ACTION_ADD)));
out.write("\"/></td></tr>");
}
}

View File

@@ -1,139 +1,133 @@
/* /*
* Copyright (C) 2005 Alfresco, Inc. * Copyright (C) 2005 Alfresco, Inc.
* *
* Licensed under the Mozilla Public License version 1.1 * Licensed under the Mozilla Public License version 1.1
* with a permitted attribution clause. You may obtain a * with a permitted attribution clause. You may obtain a
* copy of the License at * copy of the License at
* *
* http://www.alfresco.org/legal/license.txt * http://www.alfresco.org/legal/license.txt
* *
* Unless required by applicable law or agreed to in writing, * Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an * software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific * either express or implied. See the License for the specific
* language governing permissions and limitations under the * language governing permissions and limitations under the
* License. * License.
*/ */
package org.alfresco.web.ui.repo.tag; package org.alfresco.web.ui.repo.tag;
import javax.faces.component.UIComponent; import javax.faces.component.UIComponent;
import org.alfresco.web.ui.common.tag.HtmlComponentTag; import org.alfresco.web.ui.common.tag.HtmlComponentTag;
import org.alfresco.web.ui.repo.RepoConstants;
/**
* Tag to combine the multi value component and list renderer /**
* * Base class for all tags that combine the multi value component
* @author gavinc * and renderers
*/ *
public class MultiValueListEditorTag extends HtmlComponentTag * @author gavinc
{ */
private String value; public abstract class BaseMultiValueTag extends HtmlComponentTag
private String lastItemAdded; {
private String readOnly; private String value;
private String selectItemMsg; private String lastItemAdded;
private String selectedItemsMsg; private String readOnly;
private String noSelectedItemsMsg; private String selectItemMsg;
private String selectedItemsMsg;
/** private String noSelectedItemsMsg;
* @see javax.faces.webapp.UIComponentTag#getComponentType()
*/ /**
public String getComponentType() * @see javax.faces.webapp.UIComponentTag#getComponentType()
{ */
return "org.alfresco.faces.MultiValueEditor"; public String getComponentType()
} {
return RepoConstants.ALFRESCO_FACES_MULTIVALUE_EDITOR;
/** }
* @see javax.faces.webapp.UIComponentTag#getRendererType()
*/ /**
public String getRendererType() * @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent)
{ */
return "org.alfresco.faces.List"; protected void setProperties(UIComponent component)
} {
super.setProperties(component);
/** setStringBindingProperty(component, "value", this.value);
* @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent) setStringBindingProperty(component, "lastItemAdded", this.lastItemAdded);
*/ setStringProperty(component, "selectItemMsg", this.selectItemMsg);
protected void setProperties(UIComponent component) setStringProperty(component, "selectedItemsMsg", this.selectedItemsMsg);
{ setStringProperty(component, "noSelectedItemsMsg", this.noSelectedItemsMsg);
super.setProperties(component); setBooleanProperty(component, "readOnly", this.readOnly);
setStringBindingProperty(component, "value", this.value); }
setStringBindingProperty(component, "lastItemAdded", this.lastItemAdded);
setStringProperty(component, "selectItemMsg", this.selectItemMsg); /**
setStringProperty(component, "selectedItemsMsg", this.selectedItemsMsg); * @see javax.servlet.jsp.tagext.Tag#release()
setStringProperty(component, "noSelectedItemsMsg", this.noSelectedItemsMsg); */
setBooleanProperty(component, "readOnly", this.readOnly); public void release()
} {
this.value = null;
/** this.lastItemAdded = null;
* @see javax.servlet.jsp.tagext.Tag#release() this.readOnly = null;
*/ this.selectedItemsMsg = null;
public void release() this.selectItemMsg = null;
{ this.noSelectedItemsMsg = null;
this.value = null;
this.lastItemAdded = null; super.release();
this.readOnly = null; }
this.selectedItemsMsg = null;
this.selectItemMsg = null; /**
this.noSelectedItemsMsg = null; * @param value The value to set.
*/
super.release(); public void setValue(String value)
} {
this.value = value;
/** }
* @param value The value to set.
*/ /**
public void setValue(String value) * Sets the lastItemAdded value expression binding
{ *
this.value = value; * @param lastItemAdded lastItemAdded binding
} */
public void setLastItemAdded(String lastItemAdded)
/** {
* Sets the lastItemAdded value expression binding this.lastItemAdded = lastItemAdded;
* }
* @param lastItemAdded lastItemAdded binding
*/ /**
public void setLastItemAdded(String lastItemAdded) * Sets the readOnly flag for the component
{ *
this.lastItemAdded = lastItemAdded; * @param readOnly true if the component will be read only
} */
public void setReadOnly(String readOnly)
/** {
* Sets the readOnly flag for the component this.readOnly = readOnly;
* }
* @param readOnly true if the component will be read only
*/ /**
public void setReadOnly(String readOnly) * Sets the message to display for the no selected items
{ *
this.readOnly = readOnly; * @param noSelectedItemsMsg The message
} */
public void setNoSelectedItemsMsg(String noSelectedItemsMsg)
/** {
* Sets the message to display for the no selected items this.noSelectedItemsMsg = noSelectedItemsMsg;
* }
* @param noSelectedItemsMsg The message
*/ /**
public void setNoSelectedItemsMsg(String noSelectedItemsMsg) * Sets the message to display for the selected items
{ *
this.noSelectedItemsMsg = noSelectedItemsMsg; * @param selectedItemsMsg The message
} */
public void setSelectedItemsMsg(String selectedItemsMsg)
/** {
* Sets the message to display for the selected items this.selectedItemsMsg = selectedItemsMsg;
* }
* @param selectedItemsMsg The message
*/ /**
public void setSelectedItemsMsg(String selectedItemsMsg) * Sets the message to display for inviting the user to select an item
{ *
this.selectedItemsMsg = selectedItemsMsg; * @param selectItemMsg The message
} */
public void setSelectItemMsg(String selectItemMsg)
/** {
* Sets the message to display for inviting the user to select an item this.selectItemMsg = selectItemMsg;
* }
* @param selectItemMsg The message }
*/
public void setSelectItemMsg(String selectItemMsg)
{
this.selectItemMsg = selectItemMsg;
}
}

View File

@@ -0,0 +1,21 @@
package org.alfresco.web.ui.repo.tag;
import org.alfresco.web.ui.repo.RepoConstants;
/**
* Tag that combines the multi value editor component and
* the field renderer.
*
* @author gavinc
*
*/
public class MultiValueFieldTag extends BaseMultiValueTag
{
/**
* @see javax.faces.webapp.UIComponentTag#getRendererType()
*/
public String getRendererType()
{
return RepoConstants.ALFRESCO_FACES_FIELD_RENDERER;
}
}

View File

@@ -0,0 +1,20 @@
package org.alfresco.web.ui.repo.tag;
import org.alfresco.web.ui.repo.RepoConstants;
/**
* Tag that combines the multi value editor component and
* the selector renderer.
*
* @author gavinc
*/
public class MultiValueSelectorTag extends BaseMultiValueTag
{
/**
* @see javax.faces.webapp.UIComponentTag#getRendererType()
*/
public String getRendererType()
{
return RepoConstants.ALFRESCO_FACES_SELECTOR_RENDERER;
}
}

View File

@@ -1390,6 +1390,16 @@
</managed-property> </managed-property>
</managed-bean> </managed-bean>
<managed-bean>
<description>
Helper bean that caches the last item added to a multi
value editor component
</description>
<managed-bean-name>MultiValueEditorBean</managed-bean-name>
<managed-bean-class>org.alfresco.web.bean.MultiValueEditorBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<!-- ==================== COMPONENT GENERATOR BEANS ==================== --> <!-- ==================== COMPONENT GENERATOR BEANS ==================== -->
<managed-bean> <managed-bean>

View File

@@ -147,7 +147,7 @@
<!-- ==================== CONVERTERS ==================== --> <!-- ==================== CONVERTERS ==================== -->
<converter> <converter>
<converter-id>org.alfresco.faces.XMLDataConverter</converter-id> <converter-id>org.alfresco.faces.XMLDateConverter</converter-id>
<converter-class>org.alfresco.web.ui.common.converter.XMLDateConverter</converter-class> <converter-class>org.alfresco.web.ui.common.converter.XMLDateConverter</converter-class>
</converter> </converter>

View File

@@ -171,8 +171,14 @@
<renderer> <renderer>
<component-family>org.alfresco.faces.MultiValueEditor</component-family> <component-family>org.alfresco.faces.MultiValueEditor</component-family>
<renderer-type>org.alfresco.faces.List</renderer-type> <renderer-type>org.alfresco.faces.Selector</renderer-type>
<renderer-class>org.alfresco.web.ui.repo.renderer.MultiValueListEditorRenderer</renderer-class> <renderer-class>org.alfresco.web.ui.repo.renderer.MultiValueSelectorRenderer</renderer-class>
</renderer>
<renderer>
<component-family>org.alfresco.faces.MultiValueEditor</component-family>
<renderer-type>org.alfresco.faces.Field</renderer-type>
<renderer-class>org.alfresco.web.ui.repo.renderer.MultiValueFieldRenderer</renderer-class>
</renderer> </renderer>
</render-kit> </render-kit>

View File

@@ -1208,8 +1208,74 @@
</tag> </tag>
<tag> <tag>
<name>multiValueListEditor</name> <name>multiValueSelector</name>
<tag-class>org.alfresco.web.ui.repo.tag.MultiValueListEditorTag</tag-class> <tag-class>org.alfresco.web.ui.repo.tag.MultiValueSelectorTag</tag-class>
<body-content>JSP</body-content>
<attribute>
<name>value</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>lastItemAdded</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>id</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>rendered</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>readOnly</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>selectItemMsg</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>selectedItemsMsg</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>noSelectedItemsMsg</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>style</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>styleClass</name>
<required>false</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>multiValueField</name>
<tag-class>org.alfresco.web.ui.repo.tag.MultiValueFieldTag</tag-class>
<body-content>JSP</body-content> <body-content>JSP</body-content>
<attribute> <attribute>

View File

@@ -99,7 +99,7 @@
<tr> <tr>
<td><h:outputText value="#{msg.categories}" />:</td> <td><h:outputText value="#{msg.categories}" />:</td>
<td width="98%"> <td width="98%">
<r:multiValueListEditor id="multi-category-selector" <r:multiValueSelector id="multi-category-selector"
value="#{DocumentDetailsBean.categories}" value="#{DocumentDetailsBean.categories}"
lastItemAdded="#{DocumentDetailsBean.addedCategory}" lastItemAdded="#{DocumentDetailsBean.addedCategory}"
selectItemMsg="#{msg.select_category}" selectItemMsg="#{msg.select_category}"
@@ -109,7 +109,7 @@
<r:categorySelector id="category-selector" label="#{msg.select_category_prompt}" <r:categorySelector id="category-selector" label="#{msg.select_category_prompt}"
styleClass="selector" styleClass="selector"
value="#{DocumentDetailsBean.addedCategory}"/> value="#{DocumentDetailsBean.addedCategory}"/>
</r:multiValueListEditor> </r:multiValueSelector>
</td> </td>
</tr> </tr>
<tr><td colspan="2" class="paddingRow"></td></tr> <tr><td colspan="2" class="paddingRow"></td></tr>