001/* 002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * <a href="mailto:[email protected]">Anahide Tchertchian</a> 018 */ 019package org.nuxeo.ecm.platform.forms.layout.service; 020 021import java.io.Serializable; 022import java.lang.reflect.Constructor; 023import java.util.ArrayList; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.HashSet; 027import java.util.LinkedHashMap; 028import java.util.List; 029import java.util.Map; 030import java.util.Set; 031 032import javax.el.ExpressionFactory; 033import javax.el.ValueExpression; 034import javax.el.VariableMapper; 035import javax.faces.view.facelets.FaceletContext; 036import javax.faces.view.facelets.TagConfig; 037 038import org.apache.commons.lang3.StringUtils; 039import org.apache.commons.logging.Log; 040import org.apache.commons.logging.LogFactory; 041import org.nuxeo.ecm.platform.forms.layout.api.BuiltinModes; 042import org.nuxeo.ecm.platform.forms.layout.api.BuiltinWidgetModes; 043import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition; 044import org.nuxeo.ecm.platform.forms.layout.api.Layout; 045import org.nuxeo.ecm.platform.forms.layout.api.LayoutDefinition; 046import org.nuxeo.ecm.platform.forms.layout.api.LayoutRow; 047import org.nuxeo.ecm.platform.forms.layout.api.LayoutRowDefinition; 048import org.nuxeo.ecm.platform.forms.layout.api.LayoutTypeConfiguration; 049import org.nuxeo.ecm.platform.forms.layout.api.LayoutTypeDefinition; 050import org.nuxeo.ecm.platform.forms.layout.api.Widget; 051import org.nuxeo.ecm.platform.forms.layout.api.WidgetDefinition; 052import org.nuxeo.ecm.platform.forms.layout.api.WidgetReference; 053import org.nuxeo.ecm.platform.forms.layout.api.WidgetType; 054import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeConfiguration; 055import org.nuxeo.ecm.platform.forms.layout.api.WidgetTypeDefinition; 056import org.nuxeo.ecm.platform.forms.layout.api.converters.LayoutConversionContext; 057import org.nuxeo.ecm.platform.forms.layout.api.converters.LayoutDefinitionConverter; 058import org.nuxeo.ecm.platform.forms.layout.api.converters.WidgetDefinitionConverter; 059import org.nuxeo.ecm.platform.forms.layout.api.exceptions.LayoutException; 060import org.nuxeo.ecm.platform.forms.layout.api.exceptions.WidgetException; 061import org.nuxeo.ecm.platform.forms.layout.api.impl.LayoutImpl; 062import org.nuxeo.ecm.platform.forms.layout.api.impl.LayoutRowComparator; 063import org.nuxeo.ecm.platform.forms.layout.api.impl.LayoutRowImpl; 064import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetDefinitionImpl; 065import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetImpl; 066import org.nuxeo.ecm.platform.forms.layout.api.impl.WidgetReferenceImpl; 067import org.nuxeo.ecm.platform.forms.layout.core.service.AbstractLayoutManager; 068import org.nuxeo.ecm.platform.forms.layout.core.service.LayoutStoreImpl; 069import org.nuxeo.ecm.platform.forms.layout.descriptors.LayoutDescriptor; 070import org.nuxeo.ecm.platform.forms.layout.descriptors.LayoutTypeDescriptor; 071import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetDescriptor; 072import org.nuxeo.ecm.platform.forms.layout.descriptors.WidgetTypeDescriptor; 073import org.nuxeo.ecm.platform.forms.layout.facelets.RenderVariables; 074import org.nuxeo.ecm.platform.forms.layout.facelets.WidgetTypeHandler; 075import org.nuxeo.ecm.platform.forms.layout.facelets.plugins.TemplateWidgetTypeHandler; 076import org.nuxeo.ecm.platform.forms.layout.functions.LayoutFunctions; 077import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; 078import org.nuxeo.runtime.api.Framework; 079import org.nuxeo.runtime.model.ComponentInstance; 080import org.nuxeo.runtime.model.ComponentName; 081 082import com.sun.faces.facelets.el.VariableMapperWrapper; 083 084/** 085 * Layout service implementation. 086 * 087 * @author <a href="mailto:[email protected]">Anahide Tchertchian</a> 088 */ 089public class WebLayoutManagerImpl extends AbstractLayoutManager implements WebLayoutManager { 090 091 public static final ComponentName NAME = new ComponentName(WebLayoutManagerImpl.class.getName()); 092 093 private static final Log log = LogFactory.getLog(WebLayoutManagerImpl.class); 094 095 private static final long serialVersionUID = 1L; 096 097 public static final String WIDGET_TYPES_EP_NAME = LayoutStoreImpl.WIDGET_TYPES_EP_NAME; 098 099 /** 100 * @since 6.0 101 */ 102 public static final String LAYOUT_TYPES_EP_NAME = LayoutStoreImpl.LAYOUT_TYPES_EP_NAME; 103 104 public static final String WIDGETS_EP_NAME = LayoutStoreImpl.WIDGETS_EP_NAME; 105 106 public static final String LAYOUTS_EP_NAME = LayoutStoreImpl.LAYOUTS_EP_NAME; 107 108 public static final String PROPS_REF_EP_NAME = "disabledPropertyRefs"; 109 110 protected DisabledPropertyRefRegistry disabledPropertyRefsReg; 111 112 // Runtime component API 113 114 public WebLayoutManagerImpl() { 115 super(); 116 disabledPropertyRefsReg = new DisabledPropertyRefRegistry(); 117 } 118 119 @Override 120 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 121 if (extensionPoint.equals(WIDGET_TYPES_EP_NAME)) { 122 registerWidgetType(((WidgetTypeDescriptor) contribution).getWidgetTypeDefinition()); 123 } else if (extensionPoint.equals(LAYOUT_TYPES_EP_NAME)) { 124 registerLayoutType(((LayoutTypeDescriptor) contribution).getLayoutTypeDefinition()); 125 } else if (extensionPoint.equals(LAYOUTS_EP_NAME)) { 126 registerLayout(((LayoutDescriptor) contribution).getLayoutDefinition()); 127 } else if (extensionPoint.equals(WIDGETS_EP_NAME)) { 128 registerWidget(((WidgetDescriptor) contribution).getWidgetDefinition()); 129 } else if (extensionPoint.equals(PROPS_REF_EP_NAME)) { 130 registerDisabledPropertyRef(((DisabledPropertyRefDescriptor) contribution)); 131 } else { 132 log.error(String.format("Unknown extension point '%s', can't register !", extensionPoint)); 133 } 134 } 135 136 @Override 137 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 138 if (extensionPoint.equals(WIDGET_TYPES_EP_NAME)) { 139 unregisterWidgetType(((WidgetTypeDescriptor) contribution).getWidgetTypeDefinition()); 140 } else if (extensionPoint.equals(LAYOUT_TYPES_EP_NAME)) { 141 unregisterLayoutType(((LayoutTypeDescriptor) contribution).getLayoutTypeDefinition()); 142 } else if (extensionPoint.equals(LAYOUTS_EP_NAME)) { 143 unregisterLayout(((LayoutDescriptor) contribution).getLayoutDefinition()); 144 } else if (extensionPoint.equals(WIDGETS_EP_NAME)) { 145 unregisterWidget(((WidgetDescriptor) contribution).getWidgetDefinition()); 146 } else if (extensionPoint.equals(PROPS_REF_EP_NAME)) { 147 unregisterDisabledPropertyRef(((DisabledPropertyRefDescriptor) contribution)); 148 } else { 149 log.error(String.format("Unknown extension point '%s', can't unregister !", extensionPoint)); 150 } 151 } 152 153 // specific API (depends on JSF impl) 154 155 @Override 156 public String getDefaultStoreCategory() { 157 return JSF_CATEGORY; 158 } 159 160 @Override 161 public WidgetTypeHandler getWidgetTypeHandler(TagConfig config, String typeCategory, String typeName) 162 throws WidgetException { 163 if (StringUtils.isBlank(typeCategory)) { 164 typeCategory = getDefaultStoreCategory(); 165 } 166 WidgetType type = getLayoutStore().getWidgetType(typeCategory, typeName); 167 if (type == null) { 168 return null; 169 } 170 WidgetTypeHandler handler; 171 Class<?> klass = type.getWidgetTypeClass(); 172 if (klass == null) { 173 // implicit handler is the "template" one 174 handler = new TemplateWidgetTypeHandler(config); 175 } else { 176 try { 177 Constructor<?> ctor = klass.getDeclaredConstructor(TagConfig.class); 178 ctor.setAccessible(true); 179 handler = (WidgetTypeHandler) ctor.newInstance(config); 180 } catch (ReflectiveOperationException e) { 181 log.error("Caught error when instanciating widget type handler", e); 182 return null; 183 } 184 } 185 handler.setProperties(type.getProperties()); 186 return handler; 187 } 188 189 @Override 190 public WidgetTypeHandler getWidgetTypeHandler(TagConfig config, Widget widget) throws WidgetException { 191 if (widget == null) { 192 return null; 193 } 194 WidgetTypeHandler handler = getWidgetTypeHandler(config, widget.getTypeCategory(), widget.getType()); 195 if (handler != null) { 196 handler.setWidget(widget); 197 } 198 return handler; 199 } 200 201 /** 202 * Evaluates an EL expression in given context. 203 * <p> 204 * If the expression resolves to an EL expression, evaluate it again this is useful when retrieving the expression 205 * from a configuration file. 206 * <p> 207 * If given context is null, do no try to evaluate it and return the expression itself. 208 * 209 * @param context the facelet context. 210 * @param expression the string expression. 211 */ 212 protected static Object evaluateExpression(FaceletContext context, String expression) { 213 if (expression == null) { 214 return null; 215 } 216 if (context == null) { 217 return expression; 218 } 219 Object value = ComponentTagUtils.resolveElExpression(context, expression); 220 if (value != null && value instanceof String) { 221 // evaluate a second time in case it's another EL expression 222 value = ComponentTagUtils.resolveElExpression(context, (String) value); 223 } 224 return value; 225 } 226 227 /** 228 * Evaluates an expression to a boolean value. 229 */ 230 protected static Boolean getBooleanValue(FaceletContext context, String expression) { 231 Object value = evaluateExpression(context, expression); 232 if (value instanceof Boolean) { 233 return (Boolean) value; 234 } else if (value == null || value instanceof String) { 235 return Boolean.valueOf((String) value); 236 } else { 237 log.error("Could not get boolean value for '" + value + "' in expression '" + expression + "'"); 238 return Boolean.FALSE; 239 } 240 } 241 242 /** 243 * Evaluates an expression to a string value. 244 */ 245 protected static String getStringValue(FaceletContext context, String expression) { 246 Object value = evaluateExpression(context, expression); 247 if (value == null || value instanceof String) { 248 return (String) value; 249 } else { 250 log.error("Could not get string value for '" + value + "' in expression '" + expression + "'"); 251 return null; 252 } 253 } 254 255 protected static String getModeFromLayoutMode(FaceletContext context, WidgetDefinition wDef, String layoutMode) { 256 String wMode = getStringValue(context, wDef.getMode(layoutMode)); 257 if (wMode == null) { 258 wMode = BuiltinModes.getWidgetModeFromLayoutMode(layoutMode); 259 } 260 return wMode; 261 } 262 263 @Override 264 public Widget getWidget(FaceletContext ctx, String widgetName, String widgetCategory, String layoutMode, 265 String valueName, String layoutName) { 266 WidgetReference widgetRef = new WidgetReferenceImpl(widgetCategory, widgetName); 267 WidgetDefinition wDef = lookupWidget(widgetRef); 268 return getWidget(ctx, null, null, layoutName, null, wDef, widgetCategory, layoutMode, valueName, 0); 269 } 270 271 @Override 272 public Widget getWidget(FaceletContext ctx, WidgetDefinition wDef, String layoutMode, String valueName, 273 String layoutName) { 274 return getWidget(ctx, null, null, layoutName, null, wDef, getDefaultStoreCategory(), layoutMode, valueName, 0); 275 } 276 277 @Override 278 public Widget getWidget(FaceletContext ctx, LayoutConversionContext lctx, String conversionCat, 279 WidgetDefinition widgetDef, String layoutMode, String valueName, String layoutName) { 280 return getWidget(ctx, null, null, layoutName, null, widgetDef, getDefaultStoreCategory(), layoutMode, 281 valueName, 0); 282 } 283 284 /** 285 * Computes a widget from a definition for a mode in a given context. 286 * <p> 287 * If the widget is configured not to be rendered in the given mode, returns null. 288 * <p> 289 * Sub widgets are also computed recursively. 290 */ 291 @SuppressWarnings("deprecation") 292 protected Widget getWidget(FaceletContext context, LayoutConversionContext lctx, String conversionCat, 293 String layoutName, LayoutDefinition layoutDef, WidgetDefinition widgetDefinition, String widgetCategory, 294 String layoutMode, String valueName, int level) { 295 if (widgetDefinition == null) { 296 return null; 297 } 298 WidgetDefinition wDef = widgetDefinition.clone(); 299 if (lctx != null && !StringUtils.isBlank(conversionCat)) { 300 List<WidgetDefinitionConverter> lcs = getLayoutStore().getWidgetConverters(conversionCat); 301 for (WidgetDefinitionConverter wc : lcs) { 302 wDef = wc.getWidgetDefinition(wDef, lctx); 303 } 304 } 305 VariableMapper orig = null; 306 // avoid variable mapper changes if context is null for tests 307 if (context != null) { 308 // expose widget mode so that it can be used in a mode el 309 // expression 310 orig = context.getVariableMapper(); 311 VariableMapper vm = new VariableMapperWrapper(orig); 312 context.setVariableMapper(vm); 313 ExpressionFactory eFactory = context.getExpressionFactory(); 314 ValueExpression modeVe = eFactory.createValueExpression(layoutMode, String.class); 315 vm.setVariable(RenderVariables.globalVariables.mode.name(), modeVe); 316 } 317 String wMode = getModeFromLayoutMode(context, wDef, layoutMode); 318 if (context != null) { 319 context.setVariableMapper(orig); 320 } 321 322 if (BuiltinWidgetModes.HIDDEN.equals(wMode)) { 323 return null; 324 } 325 List<Widget> subWidgets = new ArrayList<>(); 326 WidgetDefinition[] swDefs = wDef.getSubWidgetDefinitions(); 327 if (swDefs != null) { 328 for (WidgetDefinition swDef : swDefs) { 329 Widget subWidget = getWidget(context, lctx, conversionCat, layoutName, layoutDef, swDef, 330 widgetCategory, wMode, valueName, level + 1); 331 if (subWidget != null) { 332 subWidgets.add(subWidget); 333 } 334 } 335 } 336 337 WidgetReference[] swRefs = wDef.getSubWidgetReferences(); 338 if (swRefs != null) { 339 for (WidgetReference swRef : swRefs) { 340 String cat = swRef.getCategory(); 341 if (StringUtils.isBlank(cat)) { 342 cat = widgetCategory; 343 } 344 WidgetDefinition swDef = lookupWidget(layoutDef, new WidgetReferenceImpl(cat, swRef.getName())); 345 if (swDef == null) { 346 log.error("Widget '" + swRef.getName() + "' not found in layout " + layoutName); 347 } else { 348 Widget subWidget = getWidget(context, lctx, conversionCat, layoutName, layoutDef, swDef, cat, 349 wMode, valueName, level + 1); 350 if (subWidget != null) { 351 subWidgets.add(subWidget); 352 } 353 } 354 } 355 } 356 357 boolean required = getBooleanValue(context, wDef.getRequired(layoutMode, wMode)).booleanValue(); 358 359 String wType = wDef.getType(); 360 String wTypeCat = wDef.getTypeCategory(); 361 // fill default property and control values from the widget definition 362 Map<String, Serializable> props = new HashMap<>(); 363 Map<String, Serializable> controls = new HashMap<>(); 364 String actualWTypeCat = getStoreCategory(wTypeCat); 365 WidgetTypeDefinition def = getLayoutStore().getWidgetTypeDefinition(actualWTypeCat, wType); 366 367 WidgetTypeConfiguration conf = def != null ? def.getConfiguration() : null; 368 if (conf != null) { 369 Map<String, Serializable> defaultProps = conf.getDefaultPropertyValues(wMode); 370 if (defaultProps != null && !defaultProps.isEmpty()) { 371 props.putAll(defaultProps); 372 } 373 Map<String, Serializable> defaultControls = conf.getDefaultControlValues(wMode); 374 if (defaultControls != null && !defaultControls.isEmpty()) { 375 controls.putAll(defaultControls); 376 } 377 } 378 379 props.putAll(wDef.getProperties(layoutMode, wMode)); 380 controls.putAll(wDef.getControls(layoutMode, wMode)); 381 382 WidgetImpl widget = new WidgetImpl(layoutName, wDef.getName(), wMode, wType, valueName, 383 wDef.getFieldDefinitions(), wDef.getLabel(layoutMode), wDef.getHelpLabel(layoutMode), 384 wDef.isTranslated(), wDef.isHandlingLabels(), props, required, subWidgets.toArray(new Widget[0]), 385 level, wDef.getSelectOptions(), LayoutFunctions.computeWidgetDefinitionId(wDef), 386 wDef.getRenderingInfos(layoutMode)); 387 widget.setControls(controls); 388 widget.setTypeCategory(actualWTypeCat); 389 if (Framework.isDevModeSet()) { 390 widget.setDefinition(wDef); 391 } 392 return widget; 393 } 394 395 @Override 396 public Layout getLayout(FaceletContext ctx, String layoutName, String mode, String valueName) 397 throws LayoutException { 398 return getLayout(ctx, layoutName, mode, valueName, null, false); 399 } 400 401 @Override 402 public Layout getLayout(FaceletContext ctx, String layoutName, String mode, String valueName, 403 List<String> selectedRows, boolean selectAllRowsByDefault) { 404 return getLayout(ctx, layoutName, null, mode, valueName, selectedRows, selectAllRowsByDefault); 405 } 406 407 @Override 408 public Layout getLayout(FaceletContext ctx, String layoutName, String layoutCategory, String mode, 409 String valueName, List<String> selectedRows, boolean selectAllRowsByDefault) { 410 if (StringUtils.isBlank(layoutCategory)) { 411 layoutCategory = getDefaultStoreCategory(); 412 } 413 LayoutDefinition layoutDef = getLayoutStore().getLayoutDefinition(layoutCategory, layoutName); 414 if (layoutDef == null) { 415 if (log.isDebugEnabled()) { 416 log.debug("Layout '" + layoutName + "' not found for category '" + layoutCategory + "'"); 417 } 418 return null; 419 } 420 return getLayout(ctx, layoutDef, mode, valueName, selectedRows, selectAllRowsByDefault); 421 } 422 423 @Override 424 public Layout getLayout(FaceletContext ctx, LayoutDefinition layoutDef, String mode, String valueName, 425 List<String> selectedRows, boolean selectAllRowsByDefault) { 426 return getLayout(ctx, null, null, layoutDef, mode, valueName, selectedRows, selectAllRowsByDefault); 427 } 428 429 @Override 430 public Layout getLayout(FaceletContext ctx, LayoutConversionContext lctx, String conversionCat, 431 LayoutDefinition layoutDefinition, String mode, String valueName, List<String> selectedRows, 432 boolean selectAllRowsByDefault) { 433 if (layoutDefinition == null) { 434 log.debug("Layout definition is null"); 435 return null; 436 } 437 if (ctx == null) { 438 log.warn("Layout creation computed in a null facelet context: expressions " 439 + "found in the layout definition will not be evaluated"); 440 } 441 LayoutDefinition lDef = layoutDefinition.clone(); 442 if (lctx != null && !StringUtils.isBlank(conversionCat)) { 443 List<LayoutDefinitionConverter> lcs = getLayoutStore().getLayoutConverters(conversionCat); 444 for (LayoutDefinitionConverter lc : lcs) { 445 lDef = lc.getLayoutDefinition(lDef, lctx); 446 } 447 } 448 String layoutName = lDef.getName(); 449 450 String layoutTypeCategory = lDef.getTypeCategory(); 451 String actualLayoutTypeCategory = getStoreCategory(layoutTypeCategory); 452 LayoutTypeDefinition layoutTypeDef = null; 453 String layoutType = lDef.getType(); 454 if (!StringUtils.isBlank(layoutType)) { 455 // retrieve type for templates and props mapping 456 layoutTypeDef = getLayoutStore().getLayoutTypeDefinition(actualLayoutTypeCategory, layoutType); 457 if (layoutTypeDef == null) { 458 log.warn("Layout type '" + layoutType + "' not found for category '" + layoutTypeCategory + "'"); 459 } 460 } 461 462 String template = lDef.getTemplate(mode); 463 Map<String, Serializable> props = new HashMap<>(); 464 if (layoutTypeDef != null) { 465 if (StringUtils.isEmpty(template)) { 466 template = layoutTypeDef.getTemplate(mode); 467 } 468 LayoutTypeConfiguration conf = layoutTypeDef.getConfiguration(); 469 if (conf != null) { 470 Map<String, Serializable> typeProps = conf.getDefaultPropertyValues(mode); 471 if (typeProps != null) { 472 props.putAll(typeProps); 473 } 474 } 475 } 476 Map<String, Serializable> lprops = lDef.getProperties(mode); 477 if (lprops != null) { 478 props.putAll(lprops); 479 } 480 481 LayoutImpl layout; 482 boolean scaffold = Boolean.parseBoolean(String.valueOf(props.get("scaffold"))); 483 if (scaffold) { 484 // ignore rows, retrieve all widgets from the definition, and put them in a map held by layout 485 Map<String, Widget> widgetsMap = new LinkedHashMap<>(); 486 Map<String, WidgetDefinition> widgetDefs = lDef.getWidgetDefinitions(); 487 if (widgetDefs != null) { 488 for (WidgetDefinition widgetDef : widgetDefs.values()) { 489 String widgetName = widgetDef.getName(); 490 if (StringUtils.isBlank(widgetName)) { 491 // no widget at this place 492 continue; 493 } 494 // TODO: handle category for global widgets 495 String cat = null; 496 if (StringUtils.isBlank(cat)) { 497 cat = getDefaultStoreCategory(); 498 } 499 WidgetDefinition wDef = lookupWidget(lDef, new WidgetReferenceImpl(cat, widgetName)); 500 if (wDef == null) { 501 log.error("Widget '" + widgetName + "' not found in layout " + layoutName); 502 continue; 503 } 504 Widget widget = getWidget(ctx, lctx, conversionCat, layoutName, lDef, wDef, cat, mode, valueName, 505 0); 506 if (widget != null) { 507 widgetsMap.put(widgetName, widget); 508 } 509 } 510 } 511 layout = new LayoutImpl(lDef.getName(), mode, template, widgetsMap, props, 512 LayoutFunctions.computeLayoutDefinitionId(lDef)); 513 } else { 514 LayoutRowDefinition[] rowsDef = lDef.getRows(); 515 List<LayoutRow> rows = new ArrayList<>(); 516 Set<String> foundRowNames = new HashSet<>(); 517 int rowIndex = -1; 518 for (LayoutRowDefinition rowDef : rowsDef) { 519 rowIndex++; 520 String rowName = rowDef.getName(); 521 if (rowName == null) { 522 rowName = rowDef.getDefaultName(rowIndex); 523 if (selectedRows != null && log.isDebugEnabled()) { 524 log.debug("Generating default name '" + rowName + "' in layout '" + layoutName 525 + "' for row or column at index " + rowIndex); 526 } 527 } 528 boolean emptyRow = true; 529 if (selectedRows != null && !selectedRows.contains(rowName) && !rowDef.isAlwaysSelected()) { 530 continue; 531 } 532 if (selectedRows == null && !selectAllRowsByDefault && !rowDef.isSelectedByDefault() 533 && !rowDef.isAlwaysSelected()) { 534 continue; 535 } 536 List<Widget> widgets = new ArrayList<>(); 537 for (WidgetReference widgetRef : rowDef.getWidgetReferences()) { 538 String widgetName = widgetRef.getName(); 539 if (StringUtils.isBlank(widgetName)) { 540 // no widget at this place 541 widgets.add(null); 542 continue; 543 } 544 String cat = widgetRef.getCategory(); 545 if (StringUtils.isBlank(cat)) { 546 cat = getDefaultStoreCategory(); 547 } 548 WidgetDefinition wDef = lookupWidget(lDef, new WidgetReferenceImpl(cat, widgetName)); 549 if (wDef == null) { 550 log.error("Widget '" + widgetName + "' not found in layout " + layoutName); 551 widgets.add(null); 552 continue; 553 } 554 Widget widget = getWidget(ctx, lctx, conversionCat, layoutName, lDef, wDef, cat, mode, valueName, 555 0); 556 if (widget != null) { 557 emptyRow = false; 558 } 559 widgets.add(widget); 560 } 561 if (!emptyRow) { 562 rows.add(new LayoutRowImpl(rowName, rowDef.isSelectedByDefault(), rowDef.isAlwaysSelected(), 563 widgets, rowDef.getProperties(mode), LayoutFunctions.computeLayoutRowDefinitionId(rowDef))); 564 } 565 foundRowNames.add(rowName); 566 } 567 if (selectedRows != null) { 568 Collections.sort(rows, new LayoutRowComparator(selectedRows)); 569 for (String selectedRow : selectedRows) { 570 if (!foundRowNames.contains(selectedRow)) { 571 log.warn("Selected row or column named '" + selectedRow + "' " + "was not found in layout '" 572 + layoutName + "'"); 573 } 574 } 575 } 576 577 layout = new LayoutImpl(lDef.getName(), mode, template, rows, lDef.getColumns(), props, 578 LayoutFunctions.computeLayoutDefinitionId(lDef)); 579 } 580 581 layout.setValueName(valueName); 582 layout.setType(layoutType); 583 layout.setTypeCategory(actualLayoutTypeCategory); 584 if (Framework.isDevModeSet()) { 585 layout.setDefinition(lDef); 586 // resolve template in "dev" mode, avoiding default lookup on "any" 587 // mode 588 Map<String, String> templates = lDef.getTemplates(); 589 String devTemplate = templates != null ? templates.get(BuiltinModes.DEV) : null; 590 if (layoutTypeDef != null && StringUtils.isEmpty(devTemplate)) { 591 Map<String, String> typeTemplates = layoutTypeDef.getTemplates(); 592 devTemplate = typeTemplates != null ? typeTemplates.get(BuiltinModes.DEV) : null; 593 } 594 layout.setDevTemplate(devTemplate); 595 } 596 return layout; 597 } 598 599 @Override 600 public Widget createWidget(FaceletContext ctx, String type, String mode, String valueName, 601 List<FieldDefinition> fieldDefinitions, String label, String helpLabel, Boolean translated, 602 Map<String, Serializable> properties, Widget[] subWidgets) { 603 return createWidget( 604 ctx, 605 createWidgetDefinition(ctx, type, null, mode, valueName, fieldDefinitions, null, label, helpLabel, 606 translated, properties, subWidgets), mode, valueName, subWidgets); 607 } 608 609 @Override 610 public Widget createWidget(FaceletContext ctx, WidgetDefinition wDef, String mode, String valueName, 611 Widget[] subWidgets) { 612 613 String wType = wDef.getType(); 614 String wTypeCat = wDef.getTypeCategory(); 615 // fill default property and control values from the widget definition 616 Map<String, Serializable> props = new HashMap<>(); 617 Map<String, Serializable> controls = new HashMap<>(); 618 String actualWTypeCat = getStoreCategory(wTypeCat); 619 WidgetTypeDefinition def = getLayoutStore().getWidgetTypeDefinition(actualWTypeCat, wType); 620 621 boolean required = false; 622 WidgetTypeConfiguration conf = def != null ? def.getConfiguration() : null; 623 if (conf != null) { 624 Map<String, Serializable> defaultProps = conf.getDefaultPropertyValues(mode); 625 if (defaultProps != null && !defaultProps.isEmpty()) { 626 props.putAll(defaultProps); 627 } 628 Map<String, Serializable> defaultControls = conf.getDefaultControlValues(mode); 629 if (defaultControls != null && !defaultControls.isEmpty()) { 630 controls.putAll(defaultControls); 631 } 632 } 633 Map<String, Serializable> modeProps = wDef.getProperties(mode, mode); 634 if (modeProps != null) { 635 props.putAll(modeProps); 636 Serializable requiredProp = props.get(WidgetDefinition.REQUIRED_PROPERTY_NAME); 637 if (requiredProp != null) { 638 if (requiredProp instanceof Boolean) { 639 required = ((Boolean) requiredProp).booleanValue(); 640 } else if (requiredProp instanceof String) { 641 required = getBooleanValue(ctx, (String) requiredProp).booleanValue(); 642 } else { 643 log.error("Invalid property 'required' on widget: '" + requiredProp + "'."); 644 } 645 } 646 } 647 Map<String, Serializable> modeControls = wDef.getControls(mode, mode); 648 if (modeControls != null) { 649 controls.putAll(modeControls); 650 } 651 WidgetImpl widget = new WidgetImpl("layout", wDef.getName(), mode, wType, valueName, 652 wDef.getFieldDefinitions(), wDef.getLabel(mode), wDef.getHelpLabel(mode), wDef.isTranslated(), props, 653 required, subWidgets, 0, null, LayoutFunctions.computeWidgetDefinitionId(wDef)); 654 widget.setControls(controls); 655 widget.setTypeCategory(actualWTypeCat); 656 widget.setDynamic(wDef.isDynamic()); 657 widget.setGlobal(wDef.isGlobal()); 658 if (Framework.isDevModeSet()) { 659 widget.setDefinition(wDef); 660 } 661 return widget; 662 } 663 664 protected WidgetDefinition createWidgetDefinition(FaceletContext ctx, String type, String category, String mode, 665 String valueName, List<FieldDefinition> fieldDefinitions, String widgetName, String label, 666 String helpLabel, Boolean translated, Map<String, Serializable> properties, Widget[] subWidgets) { 667 String wName = widgetName; 668 if (StringUtils.isBlank(widgetName)) { 669 wName = type; 670 } 671 WidgetDefinitionImpl wDef = new WidgetDefinitionImpl(wName, type, label, helpLabel, 672 Boolean.TRUE.equals(translated), null, fieldDefinitions, properties, null); 673 wDef.setDynamic(true); 674 return wDef; 675 } 676 677 /** 678 * @since 5.6 679 */ 680 protected void registerDisabledPropertyRef(DisabledPropertyRefDescriptor desc) { 681 disabledPropertyRefsReg.addContribution(desc); 682 log.info(String.format("Registered disabled property reference descriptor: %s", desc.toString())); 683 } 684 685 /** 686 * @since 5.6 687 */ 688 protected void unregisterDisabledPropertyRef(DisabledPropertyRefDescriptor desc) { 689 disabledPropertyRefsReg.removeContribution(desc); 690 log.info(String.format("Removed disabled property reference descriptor: %s", desc.toString())); 691 } 692 693 @Override 694 public boolean referencePropertyAsExpression(String name, Serializable value, String widgetType, 695 String widgetTypeCategory, String widgetMode, String template) { 696 if ((value instanceof String) && (ComponentTagUtils.isValueReference((String) value))) { 697 return false; 698 } 699 String cat = widgetTypeCategory; 700 if (widgetTypeCategory == null) { 701 cat = WebLayoutManager.JSF_CATEGORY; 702 } 703 for (DisabledPropertyRefDescriptor desc : disabledPropertyRefsReg.getDisabledPropertyRefs()) { 704 if (Boolean.TRUE.equals(desc.getEnabled()) && desc.matches(name, widgetType, cat, widgetMode, template)) { 705 return false; 706 } 707 } 708 return true; 709 } 710 711}