001/* 002 * (C) Copyright 2011 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Anahide Tchertchian 016 */ 017package org.nuxeo.ecm.platform.forms.layout.facelets.plugins; 018 019import java.io.Serializable; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024 025import javax.faces.component.html.HtmlSelectManyCheckbox; 026import javax.faces.component.html.HtmlSelectManyListbox; 027import javax.faces.component.html.HtmlSelectManyMenu; 028import javax.faces.view.facelets.ComponentHandler; 029import javax.faces.view.facelets.CompositeFaceletHandler; 030import javax.faces.view.facelets.FaceletContext; 031import javax.faces.view.facelets.FaceletHandler; 032import javax.faces.view.facelets.TagAttributes; 033import javax.faces.view.facelets.TagConfig; 034 035import org.apache.commons.lang.ArrayUtils; 036import org.nuxeo.ecm.platform.forms.layout.api.BuiltinWidgetModes; 037import org.nuxeo.ecm.platform.forms.layout.api.Widget; 038import org.nuxeo.ecm.platform.forms.layout.api.WidgetSelectOption; 039import org.nuxeo.ecm.platform.forms.layout.api.WidgetSelectOptions; 040import org.nuxeo.ecm.platform.forms.layout.api.exceptions.WidgetException; 041import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetSelectOptionImpl; 042import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetSelectOptionsImpl; 043import org.nuxeo.ecm.platform.forms.layout.facelets.FaceletHandlerHelper; 044import org.nuxeo.ecm.platform.ui.web.component.UISelectItem; 045import org.nuxeo.ecm.platform.ui.web.component.UISelectItems; 046import org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler; 047import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; 048 049/** 050 * Helper class for options generation depending on the widget definition 051 * 052 * @since 5.4.2 053 */ 054public abstract class AbstractSelectWidgetTypeHandler extends AbstractWidgetTypeHandler { 055 056 private static final long serialVersionUID = 1L; 057 058 protected enum SelectPropertyMappings { 059 selectOptions, var, itemLabel, resolveItemLabelTwice, itemLabelPrefix, itemLabelPrefixSeparator, 060 // 061 itemLabelSuffix, itemLabelSuffixSeparator, itemValue, 062 // 063 itemRendered, itemDisabled, itemEscaped, ordering, caseSensitive, 064 // 065 displayIdAndLabel, displayIdAndLabelSeparator, notDisplayDefaultOption, 066 // 067 localize, dbl10n; 068 } 069 070 // ease up override of behavior without impacting default options 071 // management 072 protected Map<String, Serializable> getOptionProperties(FaceletContext ctx, Widget widget, 073 WidgetSelectOption selectOption) { 074 Map<String, Serializable> props = new HashMap<>(); 075 for (SelectPropertyMappings mapping : SelectPropertyMappings.values()) { 076 if (widget.getProperties().containsKey(mapping.name())) { 077 props.put(mapping.name(), widget.getProperty(mapping.name())); 078 } 079 } 080 return props; 081 } 082 083 protected String getOptionComponentType(WidgetSelectOption selectOption) { 084 if (selectOption instanceof WidgetSelectOptions) { 085 return UISelectItems.COMPONENT_TYPE; 086 } else { 087 return UISelectItem.COMPONENT_TYPE; 088 } 089 } 090 091 protected FaceletHandler getOptionFaceletHandler(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget, 092 WidgetSelectOption selectOption, FaceletHandler nextHandler) { 093 String componentType = getOptionComponentType(selectOption); 094 TagAttributes attrs = helper.getTagAttributes(selectOption, getOptionProperties(ctx, widget, selectOption)); 095 return helper.getHtmlComponentHandler(widget.getTagConfigId(), attrs, nextHandler, componentType, null); 096 } 097 098 // not impacted by custom behaviour by default 099 protected FaceletHandler getBareOptionFaceletHandler(FaceletContext ctx, FaceletHandlerHelper helper, 100 Widget widget, WidgetSelectOption selectOption, FaceletHandler nextHandler) { 101 String componentType = getOptionComponentType(selectOption); 102 TagAttributes attrs = helper.getTagAttributes(selectOption); 103 return helper.getHtmlComponentHandler(widget.getTagConfigId(), attrs, nextHandler, componentType, null); 104 } 105 106 /** 107 * Adds a default disabled "select a value" option if widget is not required. 108 * 109 * @since 6.0 110 */ 111 protected FaceletHandler getFirstHandler(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget, 112 FaceletHandler nextHandler) { 113 Object doNotDisplay = widget.getProperty(SelectPropertyMappings.notDisplayDefaultOption.name()); 114 if (doNotDisplay != null) { 115 if (Boolean.TRUE.equals(doNotDisplay)) { 116 return null; 117 } 118 if (doNotDisplay instanceof String) { 119 Object res = ComponentTagUtils.resolveElExpression(ctx, (String) doNotDisplay); 120 if ((res instanceof Boolean && Boolean.TRUE.equals(res)) 121 || (res instanceof String && Boolean.parseBoolean((String) res))) { 122 return null; 123 } 124 } 125 } 126 String bundleName = ctx.getFacesContext().getApplication().getMessageBundle(); 127 String localizedExpression = String.format("#{%s['%s']}", bundleName, "label.vocabulary.selectValue"); 128 WidgetSelectOption selectOption = new WidgetSelectOptionImpl("", "", localizedExpression, "", Boolean.FALSE, 129 Boolean.TRUE); 130 return getBareOptionFaceletHandler(ctx, helper, widget, selectOption, nextHandler); 131 } 132 133 /** 134 * Returns true if widget properties should generate a default tag handler for select options. 135 * <p> 136 * This default implementation requires the selectOptions widget property to be filled. 137 * 138 * @since 6.0 139 */ 140 protected boolean shouldAddWidgetPropsHandler(Widget widget) { 141 if (widget.getProperties().containsKey(SelectPropertyMappings.selectOptions.name())) { 142 return true; 143 } 144 return false; 145 } 146 147 /** 148 * Computes select options from widget properties. 149 * 150 * @since 6.0 151 */ 152 protected FaceletHandler getWidgetPropsHandler(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget, 153 FaceletHandler nextHandler) { 154 if (shouldAddWidgetPropsHandler(widget)) { 155 WidgetSelectOptionsImpl selectOption = new WidgetSelectOptionsImpl( 156 widget.getProperty(SelectPropertyMappings.selectOptions.name()), 157 (String) widget.getProperty(SelectPropertyMappings.var.name()), 158 (String) widget.getProperty(SelectPropertyMappings.itemLabel.name()), 159 (String) widget.getProperty(SelectPropertyMappings.itemValue.name()), 160 widget.getProperty(SelectPropertyMappings.itemDisabled.name()), 161 widget.getProperty(SelectPropertyMappings.itemRendered.name())); 162 return getOptionFaceletHandler(ctx, helper, widget, selectOption, nextHandler); 163 } 164 return null; 165 } 166 167 protected FaceletHandler getOptionsFaceletHandler(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget, 168 WidgetSelectOption[] selectOptions) { 169 FaceletHandler leaf = new LeafFaceletHandler(); 170 List<FaceletHandler> selectItems = new ArrayList<FaceletHandler>(); 171 FaceletHandler firstItem = getFirstHandler(ctx, helper, widget, leaf); 172 if (firstItem != null) { 173 selectItems.add(firstItem); 174 } 175 FaceletHandler widgetPropsHandler = getWidgetPropsHandler(ctx, helper, widget, leaf); 176 if (widgetPropsHandler != null) { 177 selectItems.add(widgetPropsHandler); 178 } 179 if (selectOptions != null && selectOptions.length > 0) { 180 for (WidgetSelectOption selectOption : selectOptions) { 181 if (selectOption == null) { 182 continue; 183 } 184 FaceletHandler h = getBareOptionFaceletHandler(ctx, helper, widget, selectOption, leaf); 185 if (h != null) { 186 selectItems.add(h); 187 } 188 } 189 } 190 return new CompositeFaceletHandler(selectItems.toArray(new FaceletHandler[0])); 191 } 192 193 protected FaceletHandler getOptionsFaceletHandler(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget) { 194 return getOptionsFaceletHandler(ctx, helper, widget, widget.getSelectOptions()); 195 } 196 197 /** 198 * Returns properties useful for select items, not to be reported on the select component. 199 */ 200 protected List<String> getExcludedProperties() { 201 List<String> excludedProps = new ArrayList<>(); 202 // BBB 203 excludedProps.add("cssStyle"); 204 excludedProps.add("cssStyleClass"); 205 for (SelectPropertyMappings mapping : SelectPropertyMappings.values()) { 206 excludedProps.add(mapping.name()); 207 } 208 return excludedProps; 209 } 210 211 protected FaceletHandler getFaceletHandler(FaceletContext ctx, TagConfig tagConfig, Widget widget, 212 FaceletHandler[] subHandlers, String componentType) throws WidgetException { 213 return getFaceletHandler(ctx, tagConfig, widget, subHandlers, componentType, null); 214 } 215 216 protected FaceletHandler getComponentFaceletHandler(FaceletContext ctx, FaceletHandlerHelper helper, Widget widget, 217 FaceletHandler componentHandler) { 218 return componentHandler; 219 } 220 221 protected FaceletHandler getFaceletHandler(FaceletContext ctx, TagConfig tagConfig, Widget widget, 222 FaceletHandler[] subHandlers, String componentType, String rendererType) throws WidgetException { 223 FaceletHandlerHelper helper = new FaceletHandlerHelper(ctx, tagConfig); 224 String mode = widget.getMode(); 225 String widgetId = widget.getId(); 226 String widgetName = widget.getName(); 227 String widgetTagConfigId = widget.getTagConfigId(); 228 List<String> excludedProps = getExcludedProperties(); 229 TagAttributes attributes = helper.getTagAttributes(widget, excludedProps, true); 230 // BBB for CSS style classes on directory select components 231 if (widget.getProperty("cssStyle") != null) { 232 attributes = FaceletHandlerHelper.addTagAttribute(attributes, 233 helper.createAttribute("style", (String) widget.getProperty("cssStyle"))); 234 } 235 if (widget.getProperty("cssStyleClass") != null) { 236 attributes = FaceletHandlerHelper.addTagAttribute(attributes, 237 helper.createAttribute("styleClass", (String) widget.getProperty("cssStyleClass"))); 238 } 239 if (!BuiltinWidgetModes.isLikePlainMode(mode)) { 240 attributes = FaceletHandlerHelper.addTagAttribute(attributes, helper.createAttribute("id", widgetId)); 241 } 242 if (BuiltinWidgetModes.EDIT.equals(mode)) { 243 FaceletHandler optionsHandler = getOptionsFaceletHandler(ctx, helper, widget); 244 FaceletHandler[] nextHandlers = new FaceletHandler[] {}; 245 nextHandlers = (FaceletHandler[]) ArrayUtils.add(nextHandlers, optionsHandler); 246 if (subHandlers != null) { 247 nextHandlers = (FaceletHandler[]) ArrayUtils.addAll(nextHandlers, subHandlers); 248 } 249 FaceletHandler leaf = getNextHandler(ctx, tagConfig, widget, nextHandlers, helper, true, true); 250 // maybe add convert handler for easier integration of select2 251 // widgets handling multiple values 252 if (HtmlSelectManyListbox.COMPONENT_TYPE.equals(componentType) 253 || HtmlSelectManyCheckbox.COMPONENT_TYPE.equals(componentType) 254 || HtmlSelectManyMenu.COMPONENT_TYPE.equals(componentType)) { 255 // add hint for value conversion to collection 256 attributes = FaceletHandlerHelper.addTagAttribute(attributes, 257 helper.createAttribute("collectionType", ArrayList.class.getName())); 258 } 259 260 ComponentHandler input = helper.getHtmlComponentHandler(widgetTagConfigId, attributes, leaf, componentType, 261 rendererType); 262 String msgId = helper.generateMessageId(widgetName); 263 ComponentHandler message = helper.getMessageComponentHandler(widgetTagConfigId, msgId, widgetId, null); 264 FaceletHandler[] handlers = { getComponentFaceletHandler(ctx, helper, widget, input), message }; 265 return new CompositeFaceletHandler(handlers); 266 } else { 267 // TODO 268 return null; 269 } 270 } 271}