/* * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser General Public License * (LGPL) version 2.1 which accompanies this distribution, and is available at * http://www.gnu.org/licenses/lgpl.html * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Contributors: * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> * * $Id: WidgetTypeTagHandler.java 26053 2007-10-16 01:45:43Z atchertchian $ */ package org.nuxeo.ecm.platform.forms.layout.facelets; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.el.ELException; import javax.el.ValueExpression; import javax.el.VariableMapper; import javax.faces.FacesException; import javax.faces.component.UIComponent; import javax.faces.view.facelets.ComponentConfig; import javax.faces.view.facelets.FaceletContext; import javax.faces.view.facelets.FaceletHandler; import javax.faces.view.facelets.TagAttribute; import javax.faces.view.facelets.TagConfig; import javax.faces.view.facelets.TagHandler; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition; import org.nuxeo.ecm.platform.forms.layout.api.Widget; import org.nuxeo.ecm.platform.forms.layout.api.impl.FieldDefinitionImpl; import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetDefinitionImpl; import org.nuxeo.ecm.platform.forms.layout.facelets.plugins.TemplateWidgetTypeHandler; import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager; import org.nuxeo.ecm.platform.ui.web.tag.handler.SetTagHandler; import org.nuxeo.ecm.platform.ui.web.tag.handler.TagConfigFactory; import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; import org.nuxeo.runtime.api.Framework; import com.sun.faces.facelets.el.VariableMapperWrapper; /** * Widget type tag handler. * <p> * Applies a {@link WidgetTypeHandler} resolved from a widget created for given * type name and mode, and uses other tag attributes to fill the widget * properties. * <p> * Does not handle sub widgets. * * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> */ public class WidgetTypeTagHandler extends TagHandler { private static final Log log = LogFactory.getLog(WidgetTypeTagHandler.class); protected final TagConfig config; protected final TagAttribute name; protected final TagAttribute category; protected final TagAttribute mode; protected final TagAttribute value; protected final TagAttribute field; protected final TagAttribute fields; protected final TagAttribute label; protected final TagAttribute helpLabel; protected final TagAttribute translated; protected final TagAttribute properties; /** * @since 5.7 */ protected final TagAttribute widgetName; /** * Convenient attribute to remove the "template" property from widget * properties (and avoid stack overflow errors when using another widget * type in a widget template, for compatibility code for instance). * * @since 5.6 */ protected final TagAttribute ignoreTemplateProperty; /** * @since 5.6 */ protected final TagAttribute subWidgets; protected final TagAttribute resolveOnly; protected final TagAttribute[] vars; protected final String[] reservedVarsArray = { "id", "name", "category", "mode", "value", "type", "field", "fields", "widgetName", "label", "helpLabel", "translated", "properties", "ignoreTemplateProperty", "subWidgets", "resolveOnly" }; public WidgetTypeTagHandler(TagConfig config) { super(config); this.config = config; name = getRequiredAttribute("name"); category = getAttribute("category"); mode = getRequiredAttribute("mode"); value = getAttribute("value"); field = getAttribute("field"); fields = getAttribute("fields"); widgetName = getAttribute("widgetName"); label = getAttribute("label"); helpLabel = getAttribute("helpLabel"); translated = getAttribute("translated"); properties = getAttribute("properties"); ignoreTemplateProperty = getAttribute("ignoreTemplateProperty"); subWidgets = getAttribute("subWidgets"); resolveOnly = getAttribute("resolveOnly"); vars = tag.getAttributes().getAll(); } @Override @SuppressWarnings({ "unchecked", "rawtypes" }) public final void apply(FaceletContext ctx, UIComponent parent) throws IOException, FacesException, ELException { WebLayoutManager layoutService; try { layoutService = Framework.getService(WebLayoutManager.class); } catch (Exception e) { throw new FacesException(e); } if (layoutService == null) { throw new FacesException("Layout service not found"); } // compute field definitions List<FieldDefinition> fieldsValue = new ArrayList<FieldDefinition>(); if (field != null) { Object fieldValue = field.getValue(ctx); if (fieldValue instanceof FieldDefinition) { fieldsValue.add((FieldDefinition) fieldValue); } else if (fieldValue instanceof String) { fieldsValue.add(new FieldDefinitionImpl(null, (String) fieldValue)); } else { log.error("Invalid field item => discard: " + fieldValue); } } if (fields != null) { List resolvedfields = (List) fields.getObject(ctx, List.class); for (Object item : resolvedfields) { if (item instanceof FieldDefinition) { fieldsValue.add((FieldDefinition) item); } else if (item instanceof String) { fieldsValue.add(new FieldDefinitionImpl(null, (String) item)); } else { log.error("Invalid field item => discard: " + item); } } } // build handler List<String> reservedVars = Arrays.asList(reservedVarsArray); Map<String, Serializable> widgetProps = new HashMap<String, Serializable>(); if (properties != null) { Map<String, Serializable> propertiesValue = (Map<String, Serializable>) properties.getObject( ctx, Map.class); if (propertiesValue != null) { widgetProps.putAll(propertiesValue); } } // do not propagate value the value attribute to the widget // properties if field definitions should be taken into account // instead String widgetPropertyMarker = RenderVariables.widgetVariables.widgetProperty.name() + "_"; boolean includeValueInProps = fieldsValue.isEmpty(); for (TagAttribute var : vars) { String localName = var.getLocalName(); if ((!reservedVars.contains(localName)) || ("value".equals(localName) && includeValueInProps)) { String varName = localName; if (localName != null && localName.startsWith(widgetPropertyMarker)) { varName = localName.substring(widgetPropertyMarker.length()); } widgetProps.put(varName, var.getValue()); } } boolean ignoreTemplatePropValue = false; if (ignoreTemplateProperty != null) { ignoreTemplatePropValue = ignoreTemplateProperty.getBoolean(ctx); } if (ignoreTemplatePropValue) { widgetProps.remove(TemplateWidgetTypeHandler.TEMPLATE_PROPERTY_NAME); } String typeValue = name.getValue(ctx); String categoryValue = null; if (category != null) { categoryValue = category.getValue(ctx); } String modeValue = mode.getValue(ctx); String valueName = null; if (value != null) { valueName = value.getValue(); if (ComponentTagUtils.isStrictValueReference(valueName)) { valueName = ComponentTagUtils.getBareValueName(valueName); } } String widgetNameValue = null; if (widgetName != null) { widgetNameValue = widgetName.getValue(ctx); } String labelValue = null; if (label != null) { labelValue = label.getValue(ctx); } String helpLabelValue = null; if (helpLabel != null) { helpLabelValue = helpLabel.getValue(ctx); } Boolean translatedValue = Boolean.FALSE; if (translated != null) { translatedValue = Boolean.valueOf(translated.getBoolean(ctx)); } Widget[] subWidgetsValue = null; if (subWidgets != null) { subWidgetsValue = (Widget[]) subWidgets.getObject(ctx, Widget[].class); } // avoid double markers if (widgetNameValue != null && widgetNameValue.startsWith(FaceletHandlerHelper.WIDGET_ID_PREFIX)) { widgetNameValue = widgetNameValue.substring(FaceletHandlerHelper.WIDGET_ID_PREFIX.length()); } if (StringUtils.isBlank(widgetNameValue)) { widgetNameValue = typeValue; } WidgetDefinitionImpl wDef = new WidgetDefinitionImpl(widgetNameValue, typeValue, labelValue, helpLabelValue, translatedValue.booleanValue(), null, fieldsValue, widgetProps, null); wDef.setTypeCategory(categoryValue); wDef.setDynamic(true); Widget widget = layoutService.createWidget(ctx, wDef, modeValue, valueName, subWidgetsValue); // expose widget variable VariableMapper orig = ctx.getVariableMapper(); VariableMapper vm = new VariableMapperWrapper(orig); ctx.setVariableMapper(vm); ValueExpression widgetVe = ctx.getExpressionFactory().createValueExpression( widget, Widget.class); vm.setVariable(RenderVariables.widgetVariables.widget.name(), widgetVe); vm.setVariable( String.format("%s_%s", RenderVariables.widgetVariables.widget.name(), Integer.valueOf(widget.getLevel())), widgetVe); // TODO NXP-13280: expose widget controls too when they can be // retrieved from tag attributes try { // set unique id on widget and sub widgets before exposing them to // the context FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, config); WidgetTagHandler.generateWidgetIdsRecursive(helper, widget); boolean resolveOnlyBool = false; if (resolveOnly != null) { resolveOnlyBool = resolveOnly.getBoolean(ctx); } if (resolveOnlyBool) { // NXP-12882: wrap handler in an nxu:set tag to avoid duplicate // id issue when widget definition changes, as component ids // can be cached and not generated-again on ajax re-render, // this is a quick fix that can be optimized, as the widget // variable is already exposed in the current variable mapper. // Update after NXP-15050: this does not seem to be necessary // anymore, could not reproduce the corresponding bug, to // remove after complementary tests. String setTagConfigId = widget.getTagConfigId(); ComponentConfig aliasConfig = TagConfigFactory.createAliasTagConfig( this.config, setTagConfigId, RenderVariables.widgetVariables.widget.name(), "#{widget}", "true", "true", nextHandler); FaceletHandler handler = new SetTagHandler(aliasConfig); handler.apply(ctx, parent); } else { WidgetTagHandler.applyWidgetHandler(ctx, parent, config, widget, value, true, nextHandler); } } finally { ctx.setVariableMapper(orig); } } }