diff --git a/config/alfresco/messages/webclient.properties b/config/alfresco/messages/webclient.properties index 7d0c558df6..26160b9809 100644 --- a/config/alfresco/messages/webclient.properties +++ b/config/alfresco/messages/webclient.properties @@ -1,13 +1,13 @@ # I18N message properties # Date Pattern -date_pattern=MMMM, d yyyy -date_time_pattern=MMMM, d yyyy HH:mm +date_pattern=d MMMM yyyy +date_time_pattern=d MMMM yyyy HH:mm time_pattern=HH:mm # General UI 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. 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. diff --git a/source/java/org/alfresco/web/bean/MultiValueEditorBean.java b/source/java/org/alfresco/web/bean/MultiValueEditorBean.java new file mode 100644 index 0000000000..fc06cfb0ec --- /dev/null +++ b/source/java/org/alfresco/web/bean/MultiValueEditorBean.java @@ -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 lastItemsAdded = new HashMap(10); + + public Map getLastItemsAdded() + { + return lastItemsAdded; + } +} diff --git a/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java b/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java index 9d337a32f5..0ba8974cca 100644 --- a/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/BaseComponentGenerator.java @@ -5,6 +5,7 @@ import javax.faces.component.UIComponent; 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.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.Node; 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.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.UIPropertySheet; 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 - * model as protected + * Creates a wrapper component around the given component to enable the user + * to edit multiple values. * * @param context FacesContext * @param propertySheet The property sheet 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, - PropertySheetItem item, UIComponent component) + protected UIComponent enableForMultiValue(FacesContext context, UIPropertySheet propertySheet, + 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) { - PropertyDefinition propertyDef = getPropertyDefinition(context, - propertySheet.getNode(), item.getName()); - if (item.isReadOnly() || (propertyDef != null && propertyDef.isProtected())) + // if the property is multi-valued create a multi value editor wrapper component + String id = "multi_" + item.getName(); + 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 if (item instanceof UIAssociation || item instanceof UIChildAssociation) - { - AssociationDefinition assocDef = getAssociationDefinition(context, - propertySheet.getNode(), item.getName()); - if (item.isReadOnly() || (assocDef != null && assocDef.isProtected())) + else { - 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; } /** diff --git a/source/java/org/alfresco/web/bean/generator/CategoryPickerGenerator.java b/source/java/org/alfresco/web/bean/generator/CategoryPickerGenerator.java index 2b15d531b5..0651c815e0 100644 --- a/source/java/org/alfresco/web/bean/generator/CategoryPickerGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/CategoryPickerGenerator.java @@ -3,6 +3,7 @@ package org.alfresco.web.bean.generator; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.web.app.servlet.FacesHelper; import org.alfresco.web.ui.repo.RepoConstants; import org.alfresco.web.ui.repo.component.UICategorySelector; @@ -29,10 +30,25 @@ public class CategoryPickerGenerator extends BaseComponentGenerator PropertySheetItem item) { // 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 - disableIfReadOnlyOrProtected(context, propertySheet, item, component); + // get the property definition + 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 setupConverter(context, propertySheet, item, component); diff --git a/source/java/org/alfresco/web/bean/generator/CheckboxGenerator.java b/source/java/org/alfresco/web/bean/generator/CheckboxGenerator.java index c29f59170c..1f861faf5f 100644 --- a/source/java/org/alfresco/web/bean/generator/CheckboxGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/CheckboxGenerator.java @@ -1,11 +1,17 @@ package org.alfresco.web.bean.generator; import javax.faces.component.UIComponent; +import javax.faces.component.UIOutput; import javax.faces.component.UISelectBoolean; 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.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.UIPropertySheet; @@ -31,18 +37,48 @@ public class CheckboxGenerator extends BaseComponentGenerator { UIComponent component = null; + // get the property definition + PropertyDefinition propertyDef = getPropertyDefinition(context, + propertySheet.getNode(), item.getName()); + if (propertySheet.inEditMode()) { // use the standard component in edit mode component = generate(context, item.getName()); - // make sure the property is not read only or protected - disableIfReadOnlyOrProtected(context, propertySheet, item, component); + // 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 { // create an output text component in view mode 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 diff --git a/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java b/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java index 657097d4f5..43d3310f29 100644 --- a/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/DatePickerGenerator.java @@ -6,6 +6,7 @@ import javax.faces.component.UIOutput; 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.ui.common.ComponentConstants; @@ -45,8 +46,23 @@ public class DatePickerGenerator extends BaseComponentGenerator // use the standard date picker component component = generate(context, item.getName()); - // make sure the property is not read only or protected - disableIfReadOnlyOrProtected(context, propertySheet, item, component); + // get the property definition + 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 { @@ -54,31 +70,31 @@ public class DatePickerGenerator extends BaseComponentGenerator 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) { - super.setupConverter(context, propertySheet, item, component); + // setup the converter if one was specified + setupConverter(context, propertySheet, item, component); } else { + // use the default converter for the date component // 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(). - createConverter(RepoConstants.ALFRESCO_FACES_XMLDATA_CONVERTER); + createConverter(RepoConstants.ALFRESCO_FACES_XMLDATE_CONVERTER); converter.setType("date"); converter.setPattern(Application.getMessage(context, MSG_DATE)); return converter; diff --git a/source/java/org/alfresco/web/bean/generator/DateTimePickerGenerator.java b/source/java/org/alfresco/web/bean/generator/DateTimePickerGenerator.java index 5b8349fc55..c25fd16bcb 100644 --- a/source/java/org/alfresco/web/bean/generator/DateTimePickerGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/DateTimePickerGenerator.java @@ -23,15 +23,21 @@ public class DateTimePickerGenerator extends DatePickerGenerator UIComponent component = super.generate(context, id); // add the attribute to show the time - component.getAttributes().put("showTime", Boolean.valueOf(true)); + component.getAttributes().put("showTime", Boolean.TRUE); 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(). - createConverter(RepoConstants.ALFRESCO_FACES_XMLDATA_CONVERTER); + createConverter(RepoConstants.ALFRESCO_FACES_XMLDATE_CONVERTER); converter.setType("both"); converter.setPattern(Application.getMessage(context, MSG_DATE_TIME)); return converter; diff --git a/source/java/org/alfresco/web/bean/generator/LabelGenerator.java b/source/java/org/alfresco/web/bean/generator/LabelGenerator.java index 8f7a8ae0ab..907aeb33ef 100644 --- a/source/java/org/alfresco/web/bean/generator/LabelGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/LabelGenerator.java @@ -24,7 +24,6 @@ public class LabelGenerator extends BaseComponentGenerator UIComponent component = generate(context, "label_" + item.getName()); // TODO: Turn the label red if the field is required - // set the label id to be label__ // setup the 'for' attribute to associate with it the control return component; diff --git a/source/java/org/alfresco/web/bean/generator/SpaceIconPickerGenerator.java b/source/java/org/alfresco/web/bean/generator/SpaceIconPickerGenerator.java index e859d3a03f..980a60e75e 100644 --- a/source/java/org/alfresco/web/bean/generator/SpaceIconPickerGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/SpaceIconPickerGenerator.java @@ -1,13 +1,11 @@ package org.alfresco.web.bean.generator; import javax.faces.component.UIComponent; -import javax.faces.component.UIOutput; import javax.faces.context.FacesContext; import javax.faces.el.ValueBinding; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; 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.repo.RepoConstants; 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, PropertySheetItem item) { - UIOutput component = null; + UIComponent component = null; + // get the property definition + PropertyDefinition propertyDef = getPropertyDefinition(context, + propertySheet.getNode(), item.getName()); + if (propertySheet.inEditMode()) { // use the standard component in edit mode - component = (UIImagePicker)generate(context, item.getName()); + component = generate(context, item.getName()); // create the list items child component UIListItems items = (UIListItems)context.getApplication(). @@ -75,13 +77,31 @@ public class SpaceIconPickerGenerator extends BaseComponentGenerator // add the list items component to the image picker component component.getChildren().add(items); - // make sure the property is not read only or protected - disableIfReadOnlyOrProtected(context, propertySheet, item, component); + // 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 { // create an output text component in view mode 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 diff --git a/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java b/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java index ee53f8dbcf..6f3acff658 100644 --- a/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java +++ b/source/java/org/alfresco/web/bean/generator/TextFieldGenerator.java @@ -5,8 +5,10 @@ import javax.faces.component.UIInput; import javax.faces.component.UIOutput; import javax.faces.context.FacesContext; +import org.alfresco.service.cmr.dictionary.PropertyDefinition; import org.alfresco.web.app.servlet.FacesHelper; 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.UIPropertySheet; @@ -34,18 +36,40 @@ public class TextFieldGenerator extends BaseComponentGenerator { UIComponent component = null; + // get the property definition + PropertyDefinition propertyDef = getPropertyDefinition(context, + propertySheet.getNode(), item.getName()); + if (propertySheet.inEditMode()) { // use the standard component in edit mode component = generate(context, item.getName()); - // make sure the property is not read only or protected - disableIfReadOnlyOrProtected(context, propertySheet, item, component); + // 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 { // create an output text component in view mode 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 diff --git a/source/java/org/alfresco/web/ui/common/converter/MultiValueConverter.java b/source/java/org/alfresco/web/ui/common/converter/MultiValueConverter.java new file mode 100644 index 0000000000..35a297a4a4 --- /dev/null +++ b/source/java/org/alfresco/web/ui/common/converter/MultiValueConverter.java @@ -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 +{ + /** + *

The standard converter id for this converter.

+ */ + 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 items = new ArrayList(); + 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; + } +} diff --git a/source/java/org/alfresco/web/ui/common/converter/XMLDateConverter.java b/source/java/org/alfresco/web/ui/common/converter/XMLDateConverter.java index a98c5527c7..232d1ae758 100644 --- a/source/java/org/alfresco/web/ui/common/converter/XMLDateConverter.java +++ b/source/java/org/alfresco/web/ui/common/converter/XMLDateConverter.java @@ -17,6 +17,7 @@ package org.alfresco.web.ui.common.converter; import java.util.Date; +import java.util.List; import java.util.Locale; import java.util.TimeZone; @@ -36,7 +37,7 @@ public class XMLDateConverter extends DateTimeConverter /** *

The standard converter id for this converter.

*/ - 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) @@ -58,6 +59,21 @@ public class XMLDateConverter extends DateTimeConverter Date date = ISO8601DateFormat.parse((String)value); 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 { str = super.getAsString(context, component, value); diff --git a/source/java/org/alfresco/web/ui/repo/RepoConstants.java b/source/java/org/alfresco/web/ui/repo/RepoConstants.java index 8707fd05ea..e2f996b4ed 100644 --- a/source/java/org/alfresco/web/ui/repo/RepoConstants.java +++ b/source/java/org/alfresco/web/ui/repo/RepoConstants.java @@ -21,6 +21,8 @@ package org.alfresco.web.ui.repo; */ 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_CHILD_ASSOCIATION = "org.alfresco.faces.ChildAssociation"; 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_IMAGE_PICKER = "org.alfresco.faces.ImagePicker"; 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_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_TEXT_FIELD = "TextFieldGenerator"; diff --git a/source/java/org/alfresco/web/ui/repo/component/AbstractItemSelector.java b/source/java/org/alfresco/web/ui/repo/component/AbstractItemSelector.java index 699e499390..d24f2887c9 100644 --- a/source/java/org/alfresco/web/ui/repo/component/AbstractItemSelector.java +++ b/source/java/org/alfresco/web/ui/repo/component/AbstractItemSelector.java @@ -18,6 +18,7 @@ package org.alfresco.web.ui.repo.component; import java.io.IOException; import java.util.Collection; +import java.util.List; import java.util.Map; import javax.faces.component.EditableValueHolder; @@ -262,6 +263,31 @@ public abstract class AbstractItemSelector extends UIInput { 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 diff --git a/source/java/org/alfresco/web/ui/repo/component/UIMultiValueEditor.java b/source/java/org/alfresco/web/ui/repo/component/UIMultiValueEditor.java index d5f7944341..0b27ff8c55 100644 --- a/source/java/org/alfresco/web/ui/repo/component/UIMultiValueEditor.java +++ b/source/java/org/alfresco/web/ui/repo/component/UIMultiValueEditor.java @@ -18,8 +18,9 @@ package org.alfresco.web.ui.repo.component; import java.util.ArrayList; +import java.util.Calendar; +import java.util.GregorianCalendar; import java.util.List; -import java.util.Map; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; @@ -30,8 +31,9 @@ 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; +import org.alfresco.web.app.servlet.FacesHelper; +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. @@ -44,12 +46,10 @@ import org.apache.commons.logging.LogFactory; */ 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; @@ -71,7 +71,7 @@ public class UIMultiValueEditor extends UIInput */ 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() { - return "org.alfresco.faces.MultiValueEditor"; + return RepoConstants.ALFRESCO_FACES_MULTIVALUE_EDITOR; } /** @@ -303,15 +303,48 @@ public class UIMultiValueEditor extends UIInput setSubmittedValue(items); } - items.add(getLastItemAdded()); - this.addingNewItem = Boolean.FALSE; + Object addedItem = null; - // 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) + if (getRendererType().equals(RepoConstants.ALFRESCO_FACES_FIELD_RENDERER)) { - 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; @@ -335,9 +368,16 @@ public class UIMultiValueEditor extends UIInput */ public boolean getRendersChildren() { - // only show the wrapped component when the add button has been clicked - - return !this.addingNewItem.booleanValue(); + if (getRendererType().equals(RepoConstants.ALFRESCO_FACES_FIELD_RENDERER)) + { + // 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(); + } } // ------------------------------------------------------------------------------ diff --git a/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java b/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java index 1108b3bc25..4277cf31b9 100644 --- a/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java +++ b/source/java/org/alfresco/web/ui/repo/component/property/UIProperty.java @@ -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"); 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 diff --git a/source/java/org/alfresco/web/ui/repo/renderer/MultiValueListEditorRenderer.java b/source/java/org/alfresco/web/ui/repo/renderer/BaseMultiValueRenderer.java similarity index 70% rename from source/java/org/alfresco/web/ui/repo/renderer/MultiValueListEditorRenderer.java rename to source/java/org/alfresco/web/ui/repo/renderer/BaseMultiValueRenderer.java index ae3265ff14..41e8164c8f 100644 --- a/source/java/org/alfresco/web/ui/repo/renderer/MultiValueListEditorRenderer.java +++ b/source/java/org/alfresco/web/ui/repo/renderer/BaseMultiValueRenderer.java @@ -1,299 +1,323 @@ -/* - * 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.renderer; - -import java.io.IOException; -import java.util.List; -import java.util.Map; - -import javax.faces.component.UIComponent; -import javax.faces.context.FacesContext; -import javax.faces.context.ResponseWriter; - -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.renderer.BaseRenderer; -import org.alfresco.web.ui.repo.component.UIMultiValueEditor; -import org.alfresco.web.ui.repo.component.UIMultiValueEditor.MultiValueEditorEvent; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -/** - * Renders the MultiValueEditor component as a list of options that can be - * removed using a Remove button - * - * @author gavinc - */ -public class MultiValueListEditorRenderer extends BaseRenderer -{ - private static Log logger = LogFactory.getLog(MultiValueListEditorRenderer.class); - - /** I18N message strings */ - private final static String MSG_REMOVE = "remove"; - private final static String MSG_SELECT_BUTTON = "select_button"; - private final static String MSG_ADD_TO_LIST_BUTTON = "add_to_list_button"; - - private boolean highlightedRow; - - // ------------------------------------------------------------------------------ - // Renderer implemenation - - /** - * @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext, javax.faces.component.UIComponent) - */ - public void decode(FacesContext context, UIComponent component) - { - Map requestMap = context.getExternalContext().getRequestParameterMap(); - Map valuesMap = context.getExternalContext().getRequestParameterValuesMap(); - String fieldId = getHiddenFieldName(component); - String value = (String)requestMap.get(fieldId); - - int action = UIMultiValueEditor.ACTION_NONE; - int removeIndex = -1; - if (value != null && value.length() != 0) - { - // break up the action into it's parts - int sepIdx = value.indexOf(UIMultiValueEditor.ACTION_SEPARATOR); - if (sepIdx != -1) - { - action = Integer.parseInt(value.substring(0, sepIdx)); - removeIndex = Integer.parseInt(value.substring(sepIdx+1)); - } - else - { - action = Integer.parseInt(value); - } - } - - if (action != UIMultiValueEditor.ACTION_NONE) - { - MultiValueEditorEvent event = new MultiValueEditorEvent(component, action, removeIndex); - component.queueEvent(event); - } - - super.decode(context, component); - } - - /** - * @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext, javax.faces.component.UIComponent) - */ - public void encodeBegin(FacesContext context, UIComponent component) throws IOException - { - if (component.isRendered() == false) - { - return; - } - - // reset the highlighted row flag - this.highlightedRow = false; - - if (component instanceof UIMultiValueEditor) - { - ResponseWriter out = context.getResponseWriter(); - Map attrs = component.getAttributes(); - String clientId = component.getClientId(context); - UIMultiValueEditor editor = (UIMultiValueEditor)component; - - // start outer table - out.write(""); - - // show the select an item message - out.write(""); - - if (editor.getAddingNewItem()) - { - out.write(""); - } - } - } - - /** - * @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) - { - ResponseWriter out = context.getResponseWriter(); - UIMultiValueEditor editor = (UIMultiValueEditor)component; - - // get hold of the node service - NodeService nodeService = Repository.getServiceRegistry(context).getNodeService(); - - if (editor.getAddingNewItem()) - { - out.write(""); - } - - // show the add to list button but only if something has been selected - out.write(""); - - out.write(""); - - // show the current items - out.write("
"); - out.write("1. "); - out.write(editor.getSelectItemMsg()); - out.write("
"); - } - else - { - out.write("
2.
"); - out.write(editor.getSelectedItemsMsg()); - out.write("
"); - out.write(""); - - List currentItems = (List)editor.getValue(); - if (currentItems != null && currentItems.size() > 0) - { - for (int x = 0; x < currentItems.size(); x++) - { - Object obj = currentItems.get(x); - if (obj != null) - { - if (obj instanceof NodeRef) - { - if (nodeService.exists((NodeRef)obj)) - { - renderExistingItem(context, component, out, nodeService, x, obj); - } - else - { - // remove invalid NodeRefs from the list - currentItems.remove(x); - } - } - else - { - renderExistingItem(context, component, out, nodeService, x, obj); - } - } - } - } - else - { - out.write(""); - } - - // close tables - out.write("
"); - out.write(Application.getMessage(context, "name")); - out.write("
"); - out.write(editor.getNoSelectedItemsMsg()); - out.write("
"); - } - } - - /** - * Renders an existing item with a remove button - * - * @param context FacesContext - * @param component The UIComponent - * @param out Writer to write output to - * @param nodeService The NodeService - * @param key The key of the item - * @param value The item's value - * @throws IOException - */ - protected void renderExistingItem(FacesContext context, UIComponent component, ResponseWriter out, - NodeService nodeService, int index, Object value) throws IOException - { - out.write(""); - - if (value instanceof NodeRef) - { - out.write(Repository.getNameForNode(nodeService, (NodeRef)value)); - } - else - { - out.write(value.toString()); - } - - out.write("  "); - out.write(""); - - this.highlightedRow = !this.highlightedRow; - } - - /** - * We use a hidden field per picker instance on the page. - * - * @return hidden field name - */ - private 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 - */ - private String generateFormSubmit(FacesContext context, UIComponent component, String action) - { - return Utils.generateFormSubmit(context, component, getHiddenFieldName(component), action); - } -} +/* + * 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.renderer; + +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.Map; + +import javax.faces.component.UIComponent; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseWriter; +import javax.faces.convert.Converter; +import javax.faces.convert.DateTimeConverter; + +import org.alfresco.service.cmr.repository.NodeRef; +import org.alfresco.service.cmr.repository.NodeService; +import org.alfresco.util.ISO8601DateFormat; +import org.alfresco.web.app.Application; +import org.alfresco.web.app.servlet.FacesHelper; +import org.alfresco.web.bean.repository.Repository; +import org.alfresco.web.ui.common.Utils; +import org.alfresco.web.ui.common.converter.XMLDateConverter; +import org.alfresco.web.ui.common.renderer.BaseRenderer; +import org.alfresco.web.ui.repo.RepoConstants; +import org.alfresco.web.ui.repo.component.UIMultiValueEditor; +import org.alfresco.web.ui.repo.component.UIMultiValueEditor.MultiValueEditorEvent; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Base class for renderers of the MultiValueEditor component. + * The current items are displayed as a list of options that can be + * removed using a Remove button. + * + * @author gavinc + */ +public abstract class BaseMultiValueRenderer extends BaseRenderer +{ + private static Log logger = LogFactory.getLog(BaseMultiValueRenderer.class); + + /** I18N message strings */ + protected final static String MSG_REMOVE = "remove"; + protected final static String MSG_SELECT_BUTTON = "select_button"; + protected final static String MSG_ADD_TO_LIST_BUTTON = "add_to_list_button"; + + protected boolean highlightedRow; + + // ------------------------------------------------------------------------------ + // Renderer implemenation + + /** + * @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext, javax.faces.component.UIComponent) + */ + public void decode(FacesContext context, UIComponent component) + { + Object obj = FacesHelper.getManagedBean(context, "MultiValueEditorBean"); + + Map requestMap = context.getExternalContext().getRequestParameterMap(); + Map valuesMap = context.getExternalContext().getRequestParameterValuesMap(); + String fieldId = getHiddenFieldName(component); + String value = (String)requestMap.get(fieldId); + + int action = UIMultiValueEditor.ACTION_NONE; + int removeIndex = -1; + if (value != null && value.length() != 0) + { + // break up the action into it's parts + int sepIdx = value.indexOf(UIMultiValueEditor.ACTION_SEPARATOR); + if (sepIdx != -1) + { + action = Integer.parseInt(value.substring(0, sepIdx)); + removeIndex = Integer.parseInt(value.substring(sepIdx+1)); + } + else + { + action = Integer.parseInt(value); + } + } + + if (action != UIMultiValueEditor.ACTION_NONE) + { + MultiValueEditorEvent event = new MultiValueEditorEvent(component, action, removeIndex); + component.queueEvent(event); + } + + super.decode(context, component); + } + + /** + * @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext, javax.faces.component.UIComponent) + */ + public void encodeBegin(FacesContext context, UIComponent component) throws IOException + { + if (component.isRendered() == false) + { + return; + } + + // reset the highlighted row flag + this.highlightedRow = false; + + if (component instanceof UIMultiValueEditor) + { + ResponseWriter out = context.getResponseWriter(); + Map attrs = component.getAttributes(); + String clientId = component.getClientId(context); + UIMultiValueEditor editor = (UIMultiValueEditor)component; + + // start outer table + out.write(""); + + // render the area before the wrapped component + renderPreWrappedComponent(context, out, editor); + } + } + + /** + * @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) + { + ResponseWriter out = context.getResponseWriter(); + UIMultiValueEditor editor = (UIMultiValueEditor)component; + + // get hold of the node service + NodeService nodeService = Repository.getServiceRegistry(context).getNodeService(); + + // render the area between the component and current items list + renderPostWrappedComponent(context, out, editor); + + // show the currently selected items + out.write(""); + + out.write("
"); + out.write(editor.getSelectedItemsMsg()); + out.write("
"); + out.write(""); + + List currentItems = (List)editor.getValue();; + if (currentItems != null && currentItems.size() > 0) + { + for (int x = 0; x < currentItems.size(); x++) + { + Object obj = currentItems.get(x); + if (obj != null) + { + if (obj instanceof NodeRef) + { + if (nodeService.exists((NodeRef)obj)) + { + renderExistingItem(context, component, out, nodeService, x, obj); + } + else + { + // remove invalid NodeRefs from the list + currentItems.remove(x); + } + } + else + { + renderExistingItem(context, component, out, nodeService, x, obj); + } + } + } + } + else + { + out.write(""); + } + + // close tables + out.write("
"); + out.write(Application.getMessage(context, "name")); + out.write("
"); + out.write(editor.getNoSelectedItemsMsg()); + out.write("
"); + } + } + + /** + * Renders the area of the component before the wrapped component appears. + * + * @param context FacesContext + * @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; + + /** + * Renders the area of the component after the wrapped component but before the list + * of currently selected values. + * + * @param context FacesContext + * @param out The ResponseWriter to write to + * @param editor The multi value editor component + */ + protected abstract void renderPostWrappedComponent(FacesContext context, + ResponseWriter out, UIMultiValueEditor editor) throws IOException; + + /** + * Renders an existing item with a remove button + * + * @param context FacesContext + * @param component The UIComponent + * @param out Writer to write output to + * @param nodeService The NodeService + * @param key The key of the item + * @param value The item's value + * @throws IOException + */ + protected void renderExistingItem(FacesContext context, UIComponent component, ResponseWriter out, + NodeService nodeService, int index, Object value) throws IOException + { + out.write(""); + + if (value instanceof NodeRef) + { + out.write(Repository.getNameForNode(nodeService, (NodeRef)value)); + } + else if (value instanceof Date) + { + XMLDateConverter converter = (XMLDateConverter)context.getApplication(). + createConverter(RepoConstants.ALFRESCO_FACES_XMLDATE_CONVERTER); + UIComponent childComponent = (UIComponent)component.getChildren().get(0); + Boolean showTime = (Boolean)childComponent.getAttributes().get("showTime"); + if (showTime != null && showTime.booleanValue()) + { + converter.setPattern(Application.getMessage(context, "date_time_pattern")); + } + else + { + converter.setPattern(Application.getMessage(context, "date_pattern")); + } + + out.write(converter.getAsString(context, childComponent, value)); + } + else if (value instanceof Boolean) + { + Converter converter = context.getApplication().createConverter( + RepoConstants.ALFRESCO_FACES_BOOLEAN_CONVERTER); + out.write(converter.getAsString(context, + (UIComponent)component.getChildren().get(0), value)); + } + else + { + out.write(value.toString()); + } + + out.write("  "); + out.write(""); + + 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); + } +} diff --git a/source/java/org/alfresco/web/ui/repo/renderer/MultiValueFieldRenderer.java b/source/java/org/alfresco/web/ui/repo/renderer/MultiValueFieldRenderer.java new file mode 100644 index 0000000000..8f1cefc356 --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/renderer/MultiValueFieldRenderer.java @@ -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(""); + } + + @Override + protected void renderPostWrappedComponent(FacesContext context, ResponseWriter out, + UIMultiValueEditor editor) throws IOException + { + out.write(" "); + } +} diff --git a/source/java/org/alfresco/web/ui/repo/renderer/MultiValueSelectorRenderer.java b/source/java/org/alfresco/web/ui/repo/renderer/MultiValueSelectorRenderer.java new file mode 100644 index 0000000000..426cf0aee9 --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/renderer/MultiValueSelectorRenderer.java @@ -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(""); + out.write("1. "); + out.write(editor.getSelectItemMsg()); + out.write(""); + + if (editor.getAddingNewItem()) + { + out.write(""); + } + else + { + out.write(""); + } + } + + @Override + protected void renderPostWrappedComponent(FacesContext context, ResponseWriter out, + UIMultiValueEditor editor) throws IOException + { + if (editor.getAddingNewItem()) + { + out.write(""); + } + + // show the add to list button but only if something has been selected + out.write("2. "); + } +} diff --git a/source/java/org/alfresco/web/ui/repo/tag/MultiValueListEditorTag.java b/source/java/org/alfresco/web/ui/repo/tag/BaseMultiValueTag.java similarity index 87% rename from source/java/org/alfresco/web/ui/repo/tag/MultiValueListEditorTag.java rename to source/java/org/alfresco/web/ui/repo/tag/BaseMultiValueTag.java index 228fee545e..38bc2440c4 100644 --- a/source/java/org/alfresco/web/ui/repo/tag/MultiValueListEditorTag.java +++ b/source/java/org/alfresco/web/ui/repo/tag/BaseMultiValueTag.java @@ -1,139 +1,133 @@ -/* - * 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.tag; - -import javax.faces.component.UIComponent; - -import org.alfresco.web.ui.common.tag.HtmlComponentTag; - -/** - * Tag to combine the multi value component and list renderer - * - * @author gavinc - */ -public class MultiValueListEditorTag extends HtmlComponentTag -{ - private String value; - private String lastItemAdded; - private String readOnly; - private String selectItemMsg; - private String selectedItemsMsg; - private String noSelectedItemsMsg; - - /** - * @see javax.faces.webapp.UIComponentTag#getComponentType() - */ - public String getComponentType() - { - return "org.alfresco.faces.MultiValueEditor"; - } - - /** - * @see javax.faces.webapp.UIComponentTag#getRendererType() - */ - public String getRendererType() - { - return "org.alfresco.faces.List"; - } - - /** - * @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent) - */ - protected void setProperties(UIComponent component) - { - super.setProperties(component); - setStringBindingProperty(component, "value", this.value); - setStringBindingProperty(component, "lastItemAdded", this.lastItemAdded); - setStringProperty(component, "selectItemMsg", this.selectItemMsg); - setStringProperty(component, "selectedItemsMsg", this.selectedItemsMsg); - setStringProperty(component, "noSelectedItemsMsg", this.noSelectedItemsMsg); - setBooleanProperty(component, "readOnly", this.readOnly); - } - - /** - * @see javax.servlet.jsp.tagext.Tag#release() - */ - public void release() - { - this.value = null; - this.lastItemAdded = null; - this.readOnly = null; - this.selectedItemsMsg = null; - this.selectItemMsg = null; - this.noSelectedItemsMsg = null; - - super.release(); - } - - /** - * @param value The value to set. - */ - public void setValue(String value) - { - this.value = value; - } - - /** - * Sets the lastItemAdded value expression binding - * - * @param lastItemAdded lastItemAdded binding - */ - public void setLastItemAdded(String lastItemAdded) - { - this.lastItemAdded = lastItemAdded; - } - - /** - * Sets the readOnly flag for the component - * - * @param readOnly true if the component will be read only - */ - public void setReadOnly(String readOnly) - { - this.readOnly = readOnly; - } - - /** - * Sets the message to display for the no selected items - * - * @param noSelectedItemsMsg The message - */ - public void setNoSelectedItemsMsg(String noSelectedItemsMsg) - { - this.noSelectedItemsMsg = noSelectedItemsMsg; - } - - /** - * Sets the message to display for the selected items - * - * @param selectedItemsMsg The message - */ - public void setSelectedItemsMsg(String selectedItemsMsg) - { - this.selectedItemsMsg = selectedItemsMsg; - } - - /** - * Sets the message to display for inviting the user to select an item - * - * @param selectItemMsg The message - */ - public void setSelectItemMsg(String selectItemMsg) - { - this.selectItemMsg = selectItemMsg; - } -} +/* + * 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.tag; + +import javax.faces.component.UIComponent; + +import org.alfresco.web.ui.common.tag.HtmlComponentTag; +import org.alfresco.web.ui.repo.RepoConstants; + +/** + * Base class for all tags that combine the multi value component + * and renderers + * + * @author gavinc + */ +public abstract class BaseMultiValueTag extends HtmlComponentTag +{ + private String value; + private String lastItemAdded; + private String readOnly; + private String selectItemMsg; + private String selectedItemsMsg; + private String noSelectedItemsMsg; + + /** + * @see javax.faces.webapp.UIComponentTag#getComponentType() + */ + public String getComponentType() + { + return RepoConstants.ALFRESCO_FACES_MULTIVALUE_EDITOR; + } + + /** + * @see javax.faces.webapp.UIComponentTag#setProperties(javax.faces.component.UIComponent) + */ + protected void setProperties(UIComponent component) + { + super.setProperties(component); + setStringBindingProperty(component, "value", this.value); + setStringBindingProperty(component, "lastItemAdded", this.lastItemAdded); + setStringProperty(component, "selectItemMsg", this.selectItemMsg); + setStringProperty(component, "selectedItemsMsg", this.selectedItemsMsg); + setStringProperty(component, "noSelectedItemsMsg", this.noSelectedItemsMsg); + setBooleanProperty(component, "readOnly", this.readOnly); + } + + /** + * @see javax.servlet.jsp.tagext.Tag#release() + */ + public void release() + { + this.value = null; + this.lastItemAdded = null; + this.readOnly = null; + this.selectedItemsMsg = null; + this.selectItemMsg = null; + this.noSelectedItemsMsg = null; + + super.release(); + } + + /** + * @param value The value to set. + */ + public void setValue(String value) + { + this.value = value; + } + + /** + * Sets the lastItemAdded value expression binding + * + * @param lastItemAdded lastItemAdded binding + */ + public void setLastItemAdded(String lastItemAdded) + { + this.lastItemAdded = lastItemAdded; + } + + /** + * Sets the readOnly flag for the component + * + * @param readOnly true if the component will be read only + */ + public void setReadOnly(String readOnly) + { + this.readOnly = readOnly; + } + + /** + * Sets the message to display for the no selected items + * + * @param noSelectedItemsMsg The message + */ + public void setNoSelectedItemsMsg(String noSelectedItemsMsg) + { + this.noSelectedItemsMsg = noSelectedItemsMsg; + } + + /** + * Sets the message to display for the selected items + * + * @param selectedItemsMsg The message + */ + public void setSelectedItemsMsg(String selectedItemsMsg) + { + this.selectedItemsMsg = selectedItemsMsg; + } + + /** + * Sets the message to display for inviting the user to select an item + * + * @param selectItemMsg The message + */ + public void setSelectItemMsg(String selectItemMsg) + { + this.selectItemMsg = selectItemMsg; + } +} diff --git a/source/java/org/alfresco/web/ui/repo/tag/MultiValueFieldTag.java b/source/java/org/alfresco/web/ui/repo/tag/MultiValueFieldTag.java new file mode 100644 index 0000000000..0a75922469 --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/tag/MultiValueFieldTag.java @@ -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; + } +} diff --git a/source/java/org/alfresco/web/ui/repo/tag/MultiValueSelectorTag.java b/source/java/org/alfresco/web/ui/repo/tag/MultiValueSelectorTag.java new file mode 100644 index 0000000000..878f268221 --- /dev/null +++ b/source/java/org/alfresco/web/ui/repo/tag/MultiValueSelectorTag.java @@ -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; + } +} diff --git a/source/web/WEB-INF/faces-config-beans.xml b/source/web/WEB-INF/faces-config-beans.xml index 5d09663d95..d56bfd6ca1 100644 --- a/source/web/WEB-INF/faces-config-beans.xml +++ b/source/web/WEB-INF/faces-config-beans.xml @@ -1390,6 +1390,16 @@ + + + Helper bean that caches the last item added to a multi + value editor component + + MultiValueEditorBean + org.alfresco.web.bean.MultiValueEditorBean + session + + diff --git a/source/web/WEB-INF/faces-config-common.xml b/source/web/WEB-INF/faces-config-common.xml index 6fd4aedf61..8659112c61 100644 --- a/source/web/WEB-INF/faces-config-common.xml +++ b/source/web/WEB-INF/faces-config-common.xml @@ -147,7 +147,7 @@ - org.alfresco.faces.XMLDataConverter + org.alfresco.faces.XMLDateConverter org.alfresco.web.ui.common.converter.XMLDateConverter diff --git a/source/web/WEB-INF/faces-config-repo.xml b/source/web/WEB-INF/faces-config-repo.xml index 91a24b2eb3..688595d6f0 100644 --- a/source/web/WEB-INF/faces-config-repo.xml +++ b/source/web/WEB-INF/faces-config-repo.xml @@ -171,8 +171,14 @@ org.alfresco.faces.MultiValueEditor - org.alfresco.faces.List - org.alfresco.web.ui.repo.renderer.MultiValueListEditorRenderer + org.alfresco.faces.Selector + org.alfresco.web.ui.repo.renderer.MultiValueSelectorRenderer + + + + org.alfresco.faces.MultiValueEditor + org.alfresco.faces.Field + org.alfresco.web.ui.repo.renderer.MultiValueFieldRenderer diff --git a/source/web/WEB-INF/repo.tld b/source/web/WEB-INF/repo.tld index df1fc9afbc..9b73393cdf 100644 --- a/source/web/WEB-INF/repo.tld +++ b/source/web/WEB-INF/repo.tld @@ -1208,8 +1208,74 @@ - multiValueListEditor - org.alfresco.web.ui.repo.tag.MultiValueListEditorTag + multiValueSelector + org.alfresco.web.ui.repo.tag.MultiValueSelectorTag + JSP + + + value + true + true + + + + lastItemAdded + true + true + + + + id + false + true + + + + rendered + false + true + + + + readOnly + false + true + + + + selectItemMsg + false + true + + + + selectedItemsMsg + false + true + + + + noSelectedItemsMsg + false + true + + + + style + false + true + + + + styleClass + false + true + + + + + multiValueField + org.alfresco.web.ui.repo.tag.MultiValueFieldTag JSP diff --git a/source/web/jsp/dialog/edit-category.jsp b/source/web/jsp/dialog/edit-category.jsp index d0b0bb71cd..82a374169e 100644 --- a/source/web/jsp/dialog/edit-category.jsp +++ b/source/web/jsp/dialog/edit-category.jsp @@ -99,7 +99,7 @@ : - - +