. Checkout of the "Define Web Content Forms" dialog

- Scrollable list of registered Form Templates
 - Add to List feature to add selected form template to data list
 - Remove Form Template
 - Fist pass of Form Template Details page
. UISelectList component now support "rowIndex" property for single components rendered multiple times in a list
 - This allows command buttons etc. to be rendered multiple times but have access to the correct row data during event handling
. Minor UI tidy ups to UIData based tables with "no items"
. Some code cleanup, use of logger pattern

git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/BRANCHES/WCM-DEV2/root@4270 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
This commit is contained in:
Kevin Roast
2006-11-02 13:51:51 +00:00
parent c13d628547
commit 3730a608ee
18 changed files with 870 additions and 146 deletions

View File

@@ -16,12 +16,10 @@
*/
package org.alfresco.web.bean.wcm;
import java.io.Serializable;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
@@ -33,7 +31,6 @@ import javax.transaction.UserTransaction;
import org.alfresco.model.ContentModel;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.repo.avm.actions.StartAVMWorkflowAction;
import org.alfresco.service.cmr.action.Action;
import org.alfresco.service.cmr.action.ActionService;
import org.alfresco.service.cmr.avm.AVMNodeDescriptor;

View File

@@ -40,7 +40,6 @@ public class CreateFolderDialog extends BaseDialogBean
protected AVMService avmService;
protected AVMBrowseBean avmBrowseBean;
protected NodeService nodeService;
private String name;
private String description;
@@ -73,14 +72,6 @@ public class CreateFolderDialog extends BaseDialogBean
this.avmService = avmService;
}
/**
* @param nodeService The NodeService to set.
*/
public void setNodeService(NodeService nodeService)
{
this.nodeService = nodeService;
}
/**
* @return Returns the description.
*/

View File

@@ -18,14 +18,16 @@ package org.alfresco.web.bean.wcm;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import javax.faces.component.UISelectItem;
import javax.faces.context.FacesContext;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
@@ -46,7 +48,10 @@ import org.alfresco.web.bean.repository.Node;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.wizard.BaseWizardBean;
import org.alfresco.web.bean.wizard.InviteUsersWizard.UserGroupRole;
import org.alfresco.web.forms.Form;
import org.alfresco.web.forms.FormsService;
import org.alfresco.web.ui.common.component.UIListItem;
import org.alfresco.web.ui.common.component.UISelectList;
import org.alfresco.web.ui.wcm.WebResources;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -56,6 +61,7 @@ import org.apache.commons.logging.LogFactory;
*/
public class CreateWebsiteWizard extends BaseWizardBean
{
private static final String COMPONENT_FORMLIST = "form-list";
private static final String MSG_DESCRIPTION = "description";
private static final String MSG_NAME = "name";
private static final String MSG_USERROLES = "create_website_summary_users";
@@ -74,6 +80,18 @@ public class CreateWebsiteWizard extends BaseWizardBean
protected AVMService avmService;
protected PermissionService permissionService;
/** datamodel for table of selected forms */
private DataModel formsDataModel = null;
/** transient list of form UIListItem objects */
private List<UIListItem> formsList = null;
/** list of forms wrapper objects */
private List<FormWrapper> forms = null;
/** Current form action dialog context */
private FormWrapper actionForm = null;
// ------------------------------------------------------------------------------
// Wizard implementation
@@ -89,6 +107,8 @@ public class CreateWebsiteWizard extends BaseWizardBean
this.dnsName = null;
this.title = null;
this.description = null;
this.formsDataModel = null;
this.forms = new ArrayList<FormWrapper>();
// init the dependant bean we are using for the invite users pages
InviteWebsiteUsersWizard wiz = getInviteUsersWizard();
@@ -216,6 +236,23 @@ public class CreateWebsiteWizard extends BaseWizardBean
this.name = name;
}
public DataModel getFormsDataModel()
{
if (this.formsDataModel == null)
{
this.formsDataModel = new ListDataModel();
}
this.formsDataModel.setWrappedData(this.forms);
return this.formsDataModel;
}
public void setFormsDataModel(DataModel formsDataModel)
{
this.formsDataModel = formsDataModel;
}
/**
* @return
*/
@@ -299,46 +336,76 @@ public class CreateWebsiteWizard extends BaseWizardBean
new String[] {this.name, this.description, buf.toString()});
}
/**
* @return List of UI items to represent the available Forms for all websites
*/
public List<UIListItem> getFormsList()
{
List<UIListItem> forms = new ArrayList<UIListItem>();
UIListItem item = new UIListItem();
item.setValue("0001");
item.setLabel("Company Press Release");
item.setDescription("Standard monthly press release form");
item.setTooltip("Standard monthly press release form");
item.setImage(WebResources.IMAGE_SANDBOX_32);
forms.add(item);
item = new UIListItem();
item.setValue("0002");
item.setLabel("Company Site Note");
item.setDescription("Main site footer node");
item.setTooltip("Basic footer node addition form");
item.setImage(WebResources.IMAGE_SANDBOX_32);
forms.add(item);
item = new UIListItem();
item.setValue("0003");
item.setLabel("Index Generator");
item.setDescription("Complete site index");
item.setTooltip("Complete site index generation form");
item.setImage(WebResources.IMAGE_SANDBOX_32);
forms.add(item);
return forms;
Collection<Form> forms = FormsService.getInstance().getForms();
List<UIListItem> items = new ArrayList<UIListItem>(forms.size());
for (Form form : forms)
{
UIListItem item = new UIListItem();
item.setValue(form.getName());
item.setLabel(form.getName());
item.setDescription((String)this.nodeService.getProperty(
form.getNodeRef(), ContentModel.PROP_DESCRIPTION));
item.setImage(WebResources.IMAGE_WEBFORM_32);
items.add(item);
}
this.formsList = items;
return items;
}
public String[] getFormsSelectedValue()
/**
* Action handler called when the Add to List button is pressed for a form template
*/
public void addForm(ActionEvent event)
{
return testValue;
UISelectList selectList = (UISelectList)event.getComponent().findComponent(COMPONENT_FORMLIST);
int index = selectList.getRowIndex();
if (index != -1)
{
this.forms.add(new FormWrapper((String)this.formsList.get(index).getValue()));
}
}
public void setFormsSelectedValue(String[] value)
/**
* Remove a template form from the selected list
*/
public void removeForm(ActionEvent event)
{
testValue = value;
FormWrapper wrapper = (FormWrapper)this.formsDataModel.getRowData();
if (wrapper != null)
{
this.forms.remove(wrapper);
}
}
private String[] testValue = new String[] {"0001"};
/**
* Action method to navigate to setup a form dialog for the current row context
*/
public void setupFormAction(ActionEvent event)
{
setActionForm( (FormWrapper)this.formsDataModel.getRowData() );
}
/**
* @return the current action form for dialog context
*/
public FormWrapper getActionForm()
{
return this.actionForm;
}
/**
* @param actionForm For dialog context
*/
public void setActionForm(FormWrapper actionForm)
{
this.actionForm = actionForm;
}
/**
* @return the InviteWebsiteUsersWizard delegate bean
*/
@@ -596,4 +663,51 @@ public class CreateWebsiteWizard extends BaseWizardBean
logger.debug(" " + name + ": " + props.get(name));
}
}
/**
* Wrapper class for a configurable template Form instance in the UI
*/
public static class FormWrapper
{
private String name;
private String title;
private String description;
FormWrapper(String name)
{
this.name = name;
this.title = name;
}
public String getName()
{
return this.name;
}
public String getTitle()
{
return this.title;
}
public void setTitle(String title)
{
this.title = title;
}
public String getDescription()
{
return this.description;
}
public void setDescription(String description)
{
this.description = description;
}
public String getDetails()
{
return "Using workflow 'default-workflow', <none> pre-script, <none> post-script, no templates selected.";
}
}
}

View File

@@ -0,0 +1,183 @@
/*
* 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.bean.wcm;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.web.bean.dialog.BaseDialogBean;
import org.alfresco.web.ui.common.component.UIListItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author Kevin Roast
*/
public class FormDetailsDialog extends BaseDialogBean
{
private static final Log logger = LogFactory.getLog(FormDetailsDialog.class);
protected AVMService avmService;
protected CreateWebsiteWizard websiteWizard;
private String title;
private String description;
private String preScript;
private String postScript;
private String[] workflowSelectedValue;
/**
* @see org.alfresco.web.bean.dialog.BaseDialogBean#init(java.util.Map)
*/
@Override
public void init(Map<String, String> parameters)
{
super.init(parameters);
this.title = null;
this.description = null;
}
/**
* @param avmService The avmService to set.
*/
public void setAvmService(AVMService avmService)
{
this.avmService = avmService;
}
/**
* @param wizard The Create Website Wizard to set.
*/
public void setCreateWebsiteWizard(CreateWebsiteWizard wizard)
{
this.websiteWizard = wizard;
}
/**
* @return Returns the description.
*/
public String getDescription()
{
if (this.description == null)
{
this.description = this.websiteWizard.getActionForm().getDescription();
}
return this.description;
}
/**
* @param description The description to set.
*/
public void setDescription(String description)
{
this.description = description;
}
/**
* @return Returns the title.
*/
public String getTitle()
{
if (this.title == null)
{
this.title = this.websiteWizard.getActionForm().getTitle();
}
return this.title;
}
/**
* @param title The title to set.
*/
public void setTitle(String title)
{
this.title = title;
}
/**
* @return Returns the post-save Script.
*/
public String getPostScript()
{
return this.postScript;
}
/**
* @param postScript The post-save Script to set.
*/
public void setPostScript(String postScript)
{
this.postScript = postScript;
}
/**
* @return Returns the pre-save Script.
*/
public String getPreScript()
{
return this.preScript;
}
/**
* @param preScript The pre-save Script to set.
*/
public void setPreScript(String preScript)
{
this.preScript = preScript;
}
/**
* @return Returns the workflow Selected Value.
*/
public String[] getWorkflowSelectedValue()
{
return this.workflowSelectedValue;
}
/**
* @param workflowSelectedValue The workflow Selected Value to set.
*/
public void setWorkflowSelectedValue(String[] workflowSelectedValue)
{
this.workflowSelectedValue = workflowSelectedValue;
}
public List<UIListItem> getWorkflowList()
{
List<UIListItem> items = new ArrayList<UIListItem>();
return items;
}
// ------------------------------------------------------------------------------
// Dialog implementation
/**
* @see org.alfresco.web.bean.dialog.BaseDialogBean#finishImpl(javax.faces.context.FacesContext, java.lang.String)
*/
@Override
protected String finishImpl(FacesContext context, String outcome) throws Exception
{
// TODO: push values from title/description etc. back into action FormWrapper!
return outcome;
}
}

View File

@@ -16,34 +16,56 @@
*/
package org.alfresco.web.forms;
import java.io.*;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.util.*;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.faces.context.FacesContext;
import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.alfresco.error.AlfrescoRuntimeException;
import org.alfresco.model.ContentModel;
import org.alfresco.model.WCMModel;
import org.alfresco.repo.avm.*;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.repo.avm.AVMNodeConverter;
import org.alfresco.service.cmr.avm.AVMNotFoundException;
import org.alfresco.service.cmr.avm.AVMService;
import org.alfresco.service.cmr.dictionary.DictionaryService;
import org.alfresco.service.cmr.model.*;
import org.alfresco.service.cmr.model.FileFolderService;
import org.alfresco.service.cmr.repository.AssociationRef;
import org.alfresco.service.cmr.repository.ContentReader;
import org.alfresco.service.cmr.repository.ContentService;
import org.alfresco.service.cmr.repository.NodeRef;
import org.alfresco.service.cmr.repository.NodeService;
import org.alfresco.service.cmr.search.*;
import org.alfresco.service.cmr.search.ResultSet;
import org.alfresco.service.cmr.search.ResultSetRow;
import org.alfresco.service.cmr.search.SearchParameters;
import org.alfresco.service.cmr.search.SearchService;
import org.alfresco.service.namespace.NamespaceService;
import org.alfresco.service.namespace.QName;
import org.alfresco.web.app.Application;
import org.alfresco.web.bean.repository.Repository;
import org.alfresco.web.bean.wcm.AVMConstants;
import org.alfresco.web.forms.xforms.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
@@ -134,28 +156,22 @@ public final class FormsService
*/
public Collection<Form> getForms()
{
try
{
final SearchParameters sp = new SearchParameters();
sp.addStore(Repository.getStoreRef());
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery("ASPECT:\"" + WCMModel.ASPECT_FORM + "\"");
final SearchParameters sp = new SearchParameters();
sp.addStore(Repository.getStoreRef());
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery("ASPECT:\"" + WCMModel.ASPECT_FORM + "\"");
if (LOGGER.isDebugEnabled())
LOGGER.debug("running query [" + sp.getQuery() + "]");
final ResultSet rs = this.searchService.query(sp);
final ResultSet rs = this.searchService.query(sp);
if (LOGGER.isDebugEnabled())
LOGGER.debug("received " + rs.length() + " results");
final Collection<Form> result = new LinkedList<Form>();
for (ResultSetRow row : rs)
{
final NodeRef nodeRef = row.getNodeRef();
result.add(this.newForm(nodeRef));
}
return result;
}
catch (RuntimeException re)
final Collection<Form> result = new LinkedList<Form>();
for (ResultSetRow row : rs)
{
LOGGER.error(re);
throw re;
final NodeRef nodeRef = row.getNodeRef();
result.add(this.newForm(nodeRef));
}
return result;
}
/**
@@ -165,35 +181,28 @@ public final class FormsService
*/
public Form getForm(final String name)
{
try
{
final SearchParameters sp = new SearchParameters();
sp.addStore(Repository.getStoreRef());
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery("ASPECT:\"" + WCMModel.ASPECT_FORM +
"\" AND @" + Repository.escapeQName(ContentModel.PROP_TITLE) +
":\"" + name + "\"");
final SearchParameters sp = new SearchParameters();
sp.addStore(Repository.getStoreRef());
sp.setLanguage(SearchService.LANGUAGE_LUCENE);
sp.setQuery("ASPECT:\"" + WCMModel.ASPECT_FORM +
"\" AND @" + Repository.escapeQName(ContentModel.PROP_TITLE) +
":\"" + name + "\"");
if (LOGGER.isDebugEnabled())
LOGGER.debug("running query [" + sp.getQuery() + "]");
final ResultSet rs = this.searchService.query(sp);
NodeRef result = null;
for (ResultSetRow row : rs)
{
final NodeRef nr = row.getNodeRef();
if (this.nodeService.getProperty(nr, ContentModel.PROP_TITLE).equals(name))
{
result = nr;
break;
}
}
if (result == null && LOGGER.isDebugEnabled())
LOGGER.debug("unable to find tempalte type " + name);
return result != null ? this.newForm(result) : null;
}
catch (RuntimeException re)
final ResultSet rs = this.searchService.query(sp);
NodeRef result = null;
for (ResultSetRow row : rs)
{
LOGGER.error(re);
throw re;
final NodeRef nr = row.getNodeRef();
if (this.nodeService.getProperty(nr, ContentModel.PROP_TITLE).equals(name))
{
result = nr;
break;
}
}
if (result == null && LOGGER.isDebugEnabled())
LOGGER.debug("unable to find tempalte type " + name);
return result != null ? this.newForm(result) : null;
}
/**
@@ -216,13 +225,16 @@ public final class FormsService
*/
private Form newForm(final NodeRef schemaNodeRef)
{
LOGGER.debug("creating form for " + schemaNodeRef);
if (LOGGER.isDebugEnabled())
LOGGER.debug("creating form for " + schemaNodeRef);
final String title = (String)
this.nodeService.getProperty(schemaNodeRef, ContentModel.PROP_TITLE);
LOGGER.debug("title is " + title);
if (LOGGER.isDebugEnabled())
LOGGER.debug("title is " + title);
final String schemaRootTagName = (String)
this.nodeService.getProperty(schemaNodeRef, WCMModel.PROP_SCHEMA_ROOT_ELEMENT_NAME);
LOGGER.debug("root tag name is " + schemaRootTagName);
if (LOGGER.isDebugEnabled())
LOGGER.debug("root tag name is " + schemaRootTagName);
final Form tt = new FormImpl(title, schemaNodeRef, schemaRootTagName);
for (AssociationRef assoc : this.nodeService.getTargetAssocs(schemaNodeRef,
WCMModel.ASSOC_RENDERING_ENGINES))
@@ -237,14 +249,17 @@ public final class FormsService
final Constructor c = formDataRendererType.getConstructor(NodeRef.class, NodeService.class, ContentService.class);
final RenderingEngine tom = (RenderingEngine)
c.newInstance(tomNodeRef, this.nodeService, this.contentService);
LOGGER.debug("loaded form data renderer type " + tom.getClass().getName() +
" for extension " + tom.getFileExtensionForRendition() +
", " + tomNodeRef);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("loaded form data renderer type " + tom.getClass().getName() +
" for extension " + tom.getFileExtensionForRendition() +
", " + tomNodeRef);
}
tt.addRenderingEngine(tom);
}
catch (Exception e)
{
LOGGER.error(e);
throw new AlfrescoRuntimeException(e.getMessage(), e);
}
}
return tt;
@@ -303,8 +318,9 @@ public final class FormsService
props = new HashMap<QName, Serializable>(1, 1.0f);
props.put(ContentModel.PROP_TITLE, renditionFileName);
nodeService.addAspect(renditionNodeRef, ContentModel.ASPECT_TITLED, props);
LOGGER.debug("generated " + renditionFileName + " using " + re);
if (LOGGER.isDebugEnabled())
LOGGER.debug("generated " + renditionFileName + " using " + re);
}
}
@@ -362,7 +378,8 @@ public final class FormsService
re.render(formInstanceData, parameters, out);
out.close();
LOGGER.debug("generated " + renditionFileName + " using " + re);
if (LOGGER.isDebugEnabled())
LOGGER.debug("generated " + renditionFileName + " using " + re);
}
}
@@ -398,14 +415,14 @@ public final class FormsService
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("writing out a document for " +
(n instanceof Document
? ((Document)n).getDocumentElement()
: n).getNodeName() +
" to " + (output instanceof StringWriter
? "string"
: output));
final StringWriter sw = new StringWriter();
t.transform(new DOMSource(n), new StreamResult(sw));
(n instanceof Document
? ((Document)n).getDocumentElement()
: n).getNodeName() +
" to " + (output instanceof StringWriter
? "string"
: output));
final StringWriter sw = new StringWriter();
t.transform(new DOMSource(n), new StreamResult(sw));
LOGGER.debug(sw.toString());
}
t.transform(new DOMSource(n), new StreamResult(output));

View File

@@ -121,7 +121,8 @@ public class XSLTRenderingEngine
final FormDataFunctions ef = XSLTRenderingEngine.getFormDataFunctions();
path = XSLTRenderingEngine.toAVMPath(ec, path);
final Map<String, Document> resultMap = ef.parseXMLDocuments(formName, path);
LOGGER.debug("received " + resultMap.size() + " documents in " + path);
if (LOGGER.isDebugEnabled())
LOGGER.debug("received " + resultMap.size() + " documents in " + path);
// create a root document for rooting all the results. we do this
// so that each document root element has a common parent node
@@ -154,7 +155,8 @@ public class XSLTRenderingEngine
public void detach()
{
LOGGER.debug("detaching NodeIterator");
if (LOGGER.isDebugEnabled())
LOGGER.debug("detaching NodeIterator");
resultMap.clear();
documents.clear();
this.detached = true;
@@ -189,7 +191,8 @@ public class XSLTRenderingEngine
public Node nextNode()
throws DOMException
{
LOGGER.debug("NodeIterator.nextNode(" + index + ")");
if (LOGGER.isDebugEnabled())
LOGGER.debug("NodeIterator.nextNode(" + index + ")");
if (this.detached)
throw new DOMException(DOMException.INVALID_STATE_ERR, null);
if (index == documents.size())
@@ -200,7 +203,8 @@ public class XSLTRenderingEngine
public Node previousNode()
throws DOMException
{
LOGGER.debug("NodeIterator.previousNode(" + index + ")");
if (LOGGER.isDebugEnabled())
LOGGER.debug("NodeIterator.previousNode(" + index + ")");
if (this.detached)
throw new DOMException(DOMException.INVALID_STATE_ERR, null);
if (index == -1)
@@ -337,14 +341,15 @@ public class XSLTRenderingEngine
}
try
{
LOGGER.debug("loading " + uri);
if (LOGGER.isDebugEnabled())
LOGGER.debug("loading " + uri);
final Document d = ts.parseXML(uri.toURL().openStream());
LOGGER.debug("loaded " + ts.writeXMLToString(d));
if (LOGGER.isDebugEnabled())
LOGGER.debug("loaded " + ts.writeXMLToString(d));
return new DOMSource(d);
}
catch (Exception e)
{
LOGGER.warn(e);
throw new TransformerException("unable to load " + uri, e);
}
}

View File

@@ -29,6 +29,10 @@ import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.el.ValueBinding;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.FacesEvent;
import javax.faces.event.FacesListener;
import javax.faces.event.PhaseId;
import org.alfresco.web.ui.common.Utils;
@@ -47,10 +51,12 @@ import org.alfresco.web.ui.common.Utils;
*
* @author Kevin Roast
*/
public class UISelectList extends UIInput
public class UISelectList extends UIInput implements NamingContainer
{
private Boolean multiSelect;
private Boolean activeSelect;
private int rowIndex = -1;
private int itemCount;
// ------------------------------------------------------------------------------
@@ -82,6 +88,7 @@ public class UISelectList extends UIInput
super.restoreState(context, values[0]);
this.multiSelect = (Boolean)values[1];
this.activeSelect = (Boolean)values[2];
this.itemCount = (Integer)values[3];
}
/**
@@ -89,14 +96,107 @@ public class UISelectList extends UIInput
*/
public Object saveState(FacesContext context)
{
Object values[] = new Object[3];
Object values[] = new Object[4];
// standard component attributes are saved by the super class
values[0] = super.saveState(context);
values[1] = this.multiSelect;
values[2] = this.activeSelect;
values[3] = this.itemCount;
return (values);
}
/**
* @return the client Id for this naming container component - based on the current row context.
* This allows a single component rendered multiple times in a list to dynamically base
* their ID on the current row - so that the 'correct' component is decoded and event is
* queued with the current row value.
*/
@Override
public String getClientId(FacesContext context)
{
String clientId = super.getClientId(context);
int rowIndex = getRowIndex();
if (rowIndex == -1)
{
return clientId;
}
return clientId + "_" + rowIndex;
}
/**
* Override the processing of child component decodes - we set the current row context so any
* events queued by child components wrapped in FacesEventWrapper have current row value.
*/
@Override
public void processDecodes(FacesContext context)
{
if (!isRendered())
{
return;
}
setRowIndex(-1);
for (Iterator itr=getChildren().iterator(); itr.hasNext(); /**/)
{
UIComponent child = (UIComponent)itr.next();
if (child instanceof UIListItem == false && child instanceof UIListItems == false)
{
for (int i=0; i<this.itemCount; i++)
{
setRowIndex(i);
child.processDecodes(context);
}
}
}
setRowIndex(-1);
try
{
decode(context);
}
catch (RuntimeException e)
{
context.renderResponse();
throw e;
}
}
/**
* Override event queueing from child components - wrap and add current row value
*/
@Override
public void queueEvent(FacesEvent event)
{
super.queueEvent(new FacesEventWrapper(event, getRowIndex(), this));
}
/**
* Override event broadcasting to look for event wrappers to set the current row context
* correctly for components that have been rendered multiple times in the list.
*/
@Override
public void broadcast(FacesEvent event) throws AbortProcessingException
{
if (event instanceof FacesEventWrapper)
{
FacesEvent originalEvent = ((FacesEventWrapper)event).getWrappedFacesEvent();
int eventRowIndex = ((FacesEventWrapper)event).getRowIndex();
int currentRowIndex = getRowIndex();
setRowIndex(eventRowIndex);
try
{
originalEvent.getComponent().broadcast(originalEvent);
}
finally
{
setRowIndex(currentRowIndex);
}
}
else
{
super.broadcast(event);
}
}
/**
* @see javax.faces.component.UIComponentBase#decode(javax.faces.context.FacesContext)
*/
@@ -159,6 +259,8 @@ public class UISelectList extends UIInput
out.write('>');
// get the child components and look for compatible ListItem objects
this.itemCount = 0;
setRowIndex(-1);
for (Iterator i = getChildren().iterator(); i.hasNext(); /**/)
{
UIComponent child = (UIComponent)i.next();
@@ -177,8 +279,10 @@ public class UISelectList extends UIInput
{
requestMap.put(var, item);
}
setRowIndex(this.itemCount);
renderItem(context, out, item);
}
this.itemCount++;
}
}
}
@@ -192,10 +296,13 @@ public class UISelectList extends UIInput
{
requestMap.put(var, item);
}
setRowIndex(this.itemCount);
renderItem(context, out, item);
}
this.itemCount++;
}
}
setRowIndex(-1);
if (var != null)
{
requestMap.remove(var);
@@ -318,6 +425,37 @@ public class UISelectList extends UIInput
// ------------------------------------------------------------------------------
// Strongly typed property accessors
/**
* @return current row index
*/
public int getRowIndex()
{
return this.rowIndex;
}
/**
* Set the transient current row index. Setting this value causes all child components to
* have their ID values reset - so that cached clientID values are regenerated when next requested.
*
* @param rowIndex
*/
public void setRowIndex(int rowIndex)
{
this.rowIndex = rowIndex;
for (Iterator itr=getChildren().iterator(); itr.hasNext(); /**/)
{
UIComponent child = (UIComponent)itr.next();
if (child instanceof UIListItem == false && child instanceof UIListItems == false)
{
// forces a reset of the clientId for the component
// This is then regenerated - relative to this naming container which itself uses the
// current row index as part of the Id. This is what facilities the correct component
// rendering submit script and then identified during the decode() phase.
child.setId(child.getId());
}
}
}
/**
* Get the multi-select rendering flag
*
@@ -399,4 +537,61 @@ public class UISelectList extends UIInput
UIForm form = Utils.getParentForm(context, component);
return form.getClientId(context) + NamingContainer.SEPARATOR_CHAR + "selectlist";
}
/**
* Wrapper for a FacesEvent to hold current row value when the event was fired
*/
private static class FacesEventWrapper extends FacesEvent
{
private FacesEvent wrappedFacesEvent;
private int rowIndex;
public FacesEventWrapper(FacesEvent facesEvent, int rowIndex, UISelectList redirectComponent)
{
super(redirectComponent);
wrappedFacesEvent = facesEvent;
this.rowIndex = rowIndex;
}
public PhaseId getPhaseId()
{
return wrappedFacesEvent.getPhaseId();
}
public void setPhaseId(PhaseId phaseId)
{
wrappedFacesEvent.setPhaseId(phaseId);
}
public void queue()
{
wrappedFacesEvent.queue();
}
public String toString()
{
return wrappedFacesEvent.toString();
}
public boolean isAppropriateListener(FacesListener faceslistener)
{
return wrappedFacesEvent.isAppropriateListener(faceslistener);
}
public void processListener(FacesListener faceslistener)
{
wrappedFacesEvent.processListener(faceslistener);
}
public FacesEvent getWrappedFacesEvent()
{
return wrappedFacesEvent;
}
public int getRowIndex()
{
return rowIndex;
}
}
}

View File

@@ -24,4 +24,5 @@ public class WebResources extends org.alfresco.web.ui.repo.WebResources
// Image paths
public static final String IMAGE_SANDBOX_32 = "/images/icons/sandbox_large.gif";
public static final String IMAGE_USERSANDBOX_32 = "/images/icons/user_sandbox_large.gif";
public static final String IMAGE_WEBFORM_32 = "/images/icons/webform_large.gif";
}