mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-07-31 17:39:05 +00:00
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:
@@ -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.
|
||||||
|
23
source/java/org/alfresco/web/bean/MultiValueEditorBean.java
Normal file
23
source/java/org/alfresco/web/bean/MultiValueEditorBean.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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;
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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);
|
||||||
|
@@ -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";
|
||||||
|
@@ -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
|
||||||
|
@@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
|
@@ -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
|
||||||
|
@@ -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(" ");
|
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(" ");
|
||||||
*/
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -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(" <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>");
|
||||||
|
}
|
||||||
|
}
|
@@ -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>");
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
|
||||||
|
@@ -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>
|
||||||
|
|
||||||
|
@@ -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>
|
||||||
|
@@ -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>
|
||||||
|
Reference in New Issue
Block a user