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:at@nuxeo.com">Anahide Tchertchian</a> 018 */ 019package org.nuxeo.ecm.platform.forms.layout.facelets; 020 021import java.io.Serializable; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Collections; 025import java.util.HashMap; 026import java.util.List; 027import java.util.Map; 028import java.util.regex.Matcher; 029import java.util.regex.Pattern; 030 031import javax.el.ExpressionFactory; 032import javax.el.ValueExpression; 033import javax.faces.component.html.HtmlMessage; 034import javax.faces.component.html.HtmlOutputText; 035import javax.faces.context.FacesContext; 036import javax.faces.convert.Converter; 037import javax.faces.validator.Validator; 038import javax.faces.view.facelets.ComponentConfig; 039import javax.faces.view.facelets.ComponentHandler; 040import javax.faces.view.facelets.ConverterConfig; 041import javax.faces.view.facelets.ConverterHandler; 042import javax.faces.view.facelets.FaceletContext; 043import javax.faces.view.facelets.FaceletHandler; 044import javax.faces.view.facelets.TagAttribute; 045import javax.faces.view.facelets.TagAttributes; 046import javax.faces.view.facelets.TagConfig; 047import javax.faces.view.facelets.TagHandler; 048import javax.faces.view.facelets.ValidatorConfig; 049import javax.faces.view.facelets.ValidatorHandler; 050 051import org.apache.commons.logging.Log; 052import org.apache.commons.logging.LogFactory; 053import org.nuxeo.ecm.platform.forms.layout.actions.NuxeoLayoutManagerBean; 054import org.nuxeo.ecm.platform.forms.layout.api.FieldDefinition; 055import org.nuxeo.ecm.platform.forms.layout.api.Widget; 056import org.nuxeo.ecm.platform.forms.layout.api.WidgetSelectOption; 057import org.nuxeo.ecm.platform.forms.layout.api.WidgetSelectOptions; 058import org.nuxeo.ecm.platform.forms.layout.service.WebLayoutManager; 059import org.nuxeo.ecm.platform.ui.web.binding.alias.AliasTagHandler; 060import org.nuxeo.ecm.platform.ui.web.tag.fn.Functions; 061import org.nuxeo.ecm.platform.ui.web.tag.handler.GenericHtmlComponentHandler; 062import org.nuxeo.ecm.platform.ui.web.tag.handler.SetTagHandler; 063import org.nuxeo.ecm.platform.ui.web.tag.handler.TagConfigFactory; 064import org.nuxeo.ecm.platform.ui.web.util.ComponentTagUtils; 065import org.nuxeo.runtime.api.Framework; 066import org.nuxeo.runtime.services.config.ConfigurationService; 067 068import com.sun.faces.facelets.tag.TagAttributeImpl; 069import com.sun.faces.facelets.tag.TagAttributesImpl; 070import com.sun.faces.facelets.tag.ui.ComponentRef; 071import com.sun.faces.facelets.tag.ui.ComponentRefHandler; 072 073/** 074 * Helpers for layout/widget handlers. 075 * <p> 076 * Helps generating custom tag handlers and custom tag attributes. 077 * 078 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 079 */ 080public final class FaceletHandlerHelper { 081 082 private static final Log log = LogFactory.getLog(FaceletHandlerHelper.class); 083 084 public static final String LAYOUT_ID_PREFIX = "nxl_"; 085 086 public static final String WIDGET_ID_PREFIX = "nxw_"; 087 088 public static final String MESSAGE_ID_SUFFIX = "_message"; 089 090 /** 091 * @since 6.0 092 */ 093 public static final String DEV_CONTAINER_ID_SUFFIX = "_dev_container"; 094 095 /** 096 * @since 6.0 097 */ 098 public static final String DEV_REGION_ID_SUFFIX = "_dev_region"; 099 100 /** 101 * @since 6.0 102 */ 103 public static String DEV_MODE_DISABLED_VARIABLE = "nuxeoLayoutDevModeDisabled"; 104 105 private static final Pattern UNIQUE_ID_STRIP_PATTERN = Pattern.compile("(.*)(_[0-9]+)"); 106 107 /** 108 * @since 5.7 109 */ 110 public static final String DIR_PROPERTY = "dir"; 111 112 /** 113 * @since 5.7 114 */ 115 public static final String DIR_AUTO = "auto"; 116 117 final TagConfig tagConfig; 118 119 public FaceletHandlerHelper(TagConfig tagConfig) { 120 this.tagConfig = tagConfig; 121 } 122 123 /** 124 * Returns a id unique within the facelet context. 125 */ 126 public String generateUniqueId(FaceletContext context) { 127 String id; 128 TagAttribute idAttr = tagConfig.getTag().getAttributes().get("id"); 129 if (idAttr != null) { 130 id = idAttr.getValue(context); 131 } else { 132 id = context.getFacesContext().getViewRoot().createUniqueId(); 133 } 134 return generateUniqueId(context, id); 135 } 136 137 /** 138 * Returns a id unique within the facelet context using given id as base. 139 */ 140 public static String generateUniqueId(FaceletContext context, String base) { 141 return generateUniqueId(context.getFacesContext(), base); 142 } 143 144 /** 145 * Returns a id unique within the faces context using given id as base. 146 * 147 * @since 8.1 148 */ 149 public static String generateUniqueId(FacesContext faces, String base) { 150 NuxeoLayoutIdManagerBean bean = lookupIdBean(faces); 151 return bean.generateUniqueId(base); 152 } 153 154 protected static NuxeoLayoutIdManagerBean lookupIdBean(FacesContext ctx) { 155 String expr = "#{" + NuxeoLayoutIdManagerBean.NAME + "}"; 156 NuxeoLayoutIdManagerBean bean = (NuxeoLayoutIdManagerBean) ctx.getApplication().evaluateExpressionGet(ctx, expr, 157 Object.class); 158 if (bean == null) { 159 throw new RuntimeException("Managed bean not found: " + expr); 160 } 161 return bean; 162 } 163 164 /** 165 * Strips given base of any ending counter that would conflict with potential already generated unique ids 166 * 167 * @since 5.7 168 */ 169 protected static String stripUniqueIdBase(String base) { 170 if (base != null) { 171 Matcher m = UNIQUE_ID_STRIP_PATTERN.matcher(base); 172 if (m.matches()) { 173 base = m.group(1); 174 return stripUniqueIdBase(base); 175 } 176 } 177 return base; 178 } 179 180 /** 181 * @throws IllegalArgumentException if the given string is null or empty. 182 */ 183 protected static String generateValidIdString(String base) { 184 if (base == null) { 185 throw new IllegalArgumentException("Parameter base is null"); 186 } 187 int n = base.length(); 188 if (n < 1) { 189 throw new IllegalArgumentException(base); 190 } 191 return Functions.jsfTagIdEscape(base); 192 } 193 194 public static String generateWidgetId(FaceletContext context, String widgetName) { 195 return generateUniqueId(context, WIDGET_ID_PREFIX + widgetName); 196 } 197 198 public static String generateLayoutId(FaceletContext context, String layoutName) { 199 return generateUniqueId(context, LAYOUT_ID_PREFIX + layoutName); 200 } 201 202 public static String generateMessageId(FaceletContext context, String widgetName) { 203 return generateUniqueId(context, WIDGET_ID_PREFIX + widgetName + MESSAGE_ID_SUFFIX); 204 } 205 206 /** 207 * @since 6.0 208 */ 209 public static String generateDevRegionId(FaceletContext context, String widgetName) { 210 return generateUniqueId(context, WIDGET_ID_PREFIX + widgetName + DEV_REGION_ID_SUFFIX); 211 } 212 213 /** 214 * @since 6.0 215 */ 216 public static String generateDevContainerId(FaceletContext context, String widgetName) { 217 return generateUniqueId(context, WIDGET_ID_PREFIX + widgetName + DEV_CONTAINER_ID_SUFFIX); 218 } 219 220 /** 221 * Creates a unique id and returns corresponding attribute, using given string id as base. 222 */ 223 public TagAttribute createIdAttribute(FaceletContext context, String base) { 224 String value = generateUniqueId(context, base); 225 return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", "id", "id", value); 226 } 227 228 /** 229 * Creates an attribute with given name and value. 230 * <p> 231 * The attribute namespace is assumed to be empty. 232 */ 233 public TagAttribute createAttribute(String name, String value) { 234 return new TagAttributeImpl(tagConfig.getTag().getLocation(), "", name, name, value); 235 } 236 237 /** 238 * Returns true if a reference tag attribute should be created for given property value. 239 * <p> 240 * Reference tag attributes are using a non-literal EL expression so that this property value is not kept (cached) 241 * in the component on ajax refresh. 242 * <p> 243 * Of course property values already representing an expression cannot be mapped as is because they would need to be 244 * resolved twice. 245 * <p> 246 * Converters and validators cannot be referenced either because components expect corresponding value expressions 247 * to resolve to a {@link Converter} or {@link Validator} instance (instead of the converter of validator id). 248 */ 249 public boolean shouldCreateReferenceAttribute(String key, Serializable value) { 250 // FIXME: NXP-7004: make this configurable per widget type and mode or 251 // JSF component 252 if ((value instanceof String) && (ComponentTagUtils.isValueReference((String) value) || "converter".equals(key) 253 || "validator".equals(key) 254 // size is mistaken for the properties map size because 255 // of jboss el resolvers 256 || "size".equals(key) 257 // richfaces calendar does not resolve EL expressions 258 // correctly 259 || "showApplyButton".equals(key) || "defaultTime".equals(key))) { 260 return false; 261 } 262 return true; 263 } 264 265 public static TagAttributes getTagAttributes(TagAttribute... attributes) { 266 if (attributes == null || attributes.length == 0) { 267 return new TagAttributesImpl(new TagAttribute[0]); 268 } 269 return new TagAttributesImpl(attributes); 270 } 271 272 public static TagAttributes getTagAttributes(List<TagAttribute> attributes) { 273 return getTagAttributes(attributes.toArray(new TagAttribute[0])); 274 } 275 276 public static TagAttributes addTagAttribute(TagAttributes orig, TagAttribute newAttr) { 277 if (orig == null) { 278 return new TagAttributesImpl(new TagAttribute[] { newAttr }); 279 } 280 List<TagAttribute> allAttrs = new ArrayList<>(Arrays.asList(orig.getAll())); 281 allAttrs.add(newAttr); 282 return getTagAttributes(allAttrs); 283 } 284 285 /** 286 * Copies tag attributes with given names from the tag config, using given id as base for the id attribute. 287 */ 288 public TagAttributes copyTagAttributes(FaceletContext context, String id, String... names) { 289 List<TagAttribute> list = new ArrayList<>(); 290 list.add(createIdAttribute(context, id)); 291 for (String name : names) { 292 if ("id".equals(name)) { 293 // ignore 294 continue; 295 } 296 TagAttribute attr = tagConfig.getTag().getAttributes().get(name); 297 if (attr != null) { 298 list.add(attr); 299 } 300 } 301 TagAttribute[] attrs = list.toArray(new TagAttribute[list.size()]); 302 return new TagAttributesImpl(attrs); 303 } 304 305 /** 306 * Creates tag attributes using given widget properties and field definitions. 307 * <p> 308 * Assumes the "value" attribute has to be computed from the first field definition, using the "value" expression 309 * (see widget type tag handler exposed values). 310 */ 311 public TagAttributes getTagAttributes(String id, Widget widget) { 312 // add id and value computed from fields 313 TagAttributes widgetAttrs = getTagAttributes(widget); 314 return addTagAttribute(widgetAttrs, createAttribute("id", id)); 315 } 316 317 public TagAttributes getTagAttributes(Widget widget) { 318 return getTagAttributes(widget, null, true); 319 } 320 321 /** 322 * @since 5.5 323 */ 324 public TagAttributes getTagAttributes(Widget widget, List<String> excludedProperties, 325 boolean bindFirstFieldDefinition) { 326 return getTagAttributes(widget, excludedProperties, bindFirstFieldDefinition, false); 327 } 328 329 /** 330 * Return tag attributes for this widget, including value mapping from field definitions and properties 331 * 332 * @since 5.6 333 * @param widget the widget to generate tag attributes for 334 * @param excludedProperties the properties to exclude from tag attributes 335 * @param bindFirstFieldDefinition if true, the first field definition will be bound to the tag attribute named 336 * "value" 337 * @param defaultToValue if true, and there are no field definitions, tag attribute named "value" will be mapped to 338 * the current widget value name (e.g the layout value in most cases, or the parent widget value if 339 * widget is a sub widget) 340 */ 341 public TagAttributes getTagAttributes(Widget widget, List<String> excludedProperties, 342 boolean bindFirstFieldDefinition, boolean defaultToValue) { 343 List<TagAttribute> attrs = new ArrayList<>(); 344 if (bindFirstFieldDefinition) { 345 FieldDefinition field = null; 346 FieldDefinition[] fields = widget.getFieldDefinitions(); 347 if (fields != null && fields.length > 0) { 348 field = fields[0]; 349 } 350 if (field != null || defaultToValue) { 351 // bind value to first field definition or current value name 352 TagAttribute valueAttr = createAttribute("value", 353 ValueExpressionHelper.createExpressionString(widget.getValueName(), field)); 354 attrs.add(valueAttr); 355 } 356 } 357 // fill with widget properties 358 List<TagAttribute> propertyAttrs = getTagAttributes(widget.getProperties(), excludedProperties, true, 359 widget.getType(), widget.getTypeCategory(), widget.getMode()); 360 if (propertyAttrs != null) { 361 attrs.addAll(propertyAttrs); 362 } 363 return getTagAttributes(attrs); 364 } 365 366 /** 367 * @since 5.5, signature changed on 5.6 to include parameters widgetType and widgetMode. 368 */ 369 public List<TagAttribute> getTagAttributes(Map<String, Serializable> properties, List<String> excludedProperties, 370 boolean useReferenceProperties, String widgetType, String widgetTypeCategory, String widgetMode) { 371 WebLayoutManager service = Framework.getService(WebLayoutManager.class); 372 List<TagAttribute> attrs = new ArrayList<>(); 373 if (properties != null) { 374 for (Map.Entry<String, Serializable> prop : properties.entrySet()) { 375 TagAttribute attr; 376 String key = prop.getKey(); 377 if (excludedProperties != null && excludedProperties.contains(key)) { 378 continue; 379 } 380 Serializable valueInstance = prop.getValue(); 381 if (!useReferenceProperties || !service.referencePropertyAsExpression(key, valueInstance, widgetType, 382 widgetTypeCategory, widgetMode, null)) { 383 if (valueInstance == null || valueInstance instanceof String) { 384 // FIXME: this will not be updated correctly using ajax 385 attr = createAttribute(key, (String) valueInstance); 386 } else { 387 attr = createAttribute(key, valueInstance.toString()); 388 } 389 } else { 390 // create a reference so that it's a real expression 391 // and it's not kept (cached) in a component value on 392 // ajax refresh 393 attr = createAttribute(key, 394 "#{" + RenderVariables.widgetVariables.widget.name() + ".properties." + key + "}"); 395 } 396 attrs.add(attr); 397 } 398 } 399 return attrs; 400 } 401 402 /** 403 * @since 6.0 404 */ 405 public TagAttributes getTagAttributes(WidgetSelectOption selectOption, Map<String, Serializable> additionalProps) { 406 Map<String, Serializable> props = getSelectOptionProperties(selectOption); 407 if (additionalProps != null) { 408 props.putAll(additionalProps); 409 } 410 List<TagAttribute> attrs = getTagAttributes(props, null, false, null, null, null); 411 if (attrs == null) { 412 attrs = Collections.emptyList(); 413 } 414 return getTagAttributes(attrs); 415 } 416 417 public TagAttributes getTagAttributes(WidgetSelectOption selectOption) { 418 return getTagAttributes(selectOption, null); 419 } 420 421 public Map<String, Serializable> getSelectOptionProperties(WidgetSelectOption selectOption) { 422 Map<String, Serializable> map = new HashMap<>(); 423 if (selectOption != null) { 424 Serializable value = selectOption.getValue(); 425 if (value != null) { 426 map.put("value", value); 427 } 428 String var = selectOption.getVar(); 429 if (var != null) { 430 map.put("var", var); 431 } 432 String itemLabel = selectOption.getItemLabel(); 433 if (itemLabel != null) { 434 map.put("itemLabel", itemLabel); 435 } 436 String itemValue = selectOption.getItemValue(); 437 if (itemValue != null) { 438 map.put("itemValue", itemValue); 439 } 440 Serializable itemDisabled = selectOption.getItemDisabled(); 441 if (itemDisabled != null) { 442 map.put("itemDisabled", itemDisabled); 443 } 444 Serializable itemRendered = selectOption.getItemRendered(); 445 if (itemRendered != null) { 446 map.put("itemRendered", itemRendered); 447 } 448 if (selectOption instanceof WidgetSelectOptions) { 449 WidgetSelectOptions selectOptions = (WidgetSelectOptions) selectOption; 450 Boolean caseSensitive = selectOptions.getCaseSensitive(); 451 if (caseSensitive != null) { 452 map.put("caseSensitive", caseSensitive); 453 } 454 String ordering = selectOptions.getOrdering(); 455 if (ordering != null) { 456 map.put("ordering", ordering); 457 } 458 } 459 } 460 return map; 461 } 462 463 /** 464 * Returns an html component handler for this configuration. 465 * <p> 466 * Next handler cannot be null, use {@link org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler} if no next 467 * handler is needed. 468 */ 469 public ComponentHandler getHtmlComponentHandler(String tagConfigId, TagAttributes attributes, 470 FaceletHandler nextHandler, String componentType, String rendererType) { 471 ComponentConfig config = TagConfigFactory.createComponentConfig(tagConfig, tagConfigId, attributes, nextHandler, 472 componentType, rendererType); 473 return new GenericHtmlComponentHandler(config); 474 } 475 476 /** 477 * Component handler that displays an error on interface 478 */ 479 public ComponentHandler getErrorComponentHandler(String tagConfigId, String errorMessage) { 480 FaceletHandler leaf = new org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler(); 481 TagAttribute valueAttr = createAttribute("value", 482 "<span style=\"color:red;font-weight:bold;\">ERROR: " + errorMessage + "</span><br />"); 483 TagAttribute escapeAttr = createAttribute("escape", "false"); 484 ComponentHandler output = getHtmlComponentHandler(tagConfigId, 485 FaceletHandlerHelper.getTagAttributes(valueAttr, escapeAttr), leaf, HtmlOutputText.COMPONENT_TYPE, 486 null); 487 return output; 488 } 489 490 /** 491 * Returns a convert handler for this configuration. 492 * <p> 493 * Next handler cannot be null, use {@link org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler} if no next 494 * handler is needed. 495 */ 496 public ConverterHandler getConvertHandler(String tagConfigId, TagAttributes attributes, FaceletHandler nextHandler, 497 String converterId) { 498 ConverterConfig config = TagConfigFactory.createConverterConfig(tagConfig, tagConfigId, attributes, nextHandler, 499 converterId); 500 return new ConverterHandler(config); 501 } 502 503 /** 504 * Returns a validate handler for this configuration. 505 * <p> 506 * Next handler cannot be null, use {@link org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler} if no next 507 * handler is needed. 508 */ 509 public ValidatorHandler getValidateHandler(String tagConfigId, TagAttributes attributes, FaceletHandler nextHandler, 510 String validatorId) { 511 ValidatorConfig config = TagConfigFactory.createValidatorConfig(tagConfig, tagConfigId, attributes, nextHandler, 512 validatorId); 513 return new ValidatorHandler(config); 514 } 515 516 /** 517 * Returns a message component handler with given attributes. 518 * <p> 519 * Uses component type "javax.faces.HtmlMessage" and renderer type "javax.faces.Message". 520 */ 521 public ComponentHandler getMessageComponentHandler(String tagConfigId, String id, String forId, String styleClass) { 522 TagAttribute forAttr = createAttribute("for", forId); 523 TagAttribute idAttr = createAttribute("id", id); 524 if (styleClass == null) { 525 // default style class 526 styleClass = "errorMessage"; 527 } 528 TagAttribute styleAttr = createAttribute("styleClass", styleClass); 529 TagAttributes attributes = getTagAttributes(forAttr, idAttr, styleAttr); 530 ComponentConfig config = TagConfigFactory.createComponentConfig(tagConfig, tagConfigId, attributes, 531 new org.nuxeo.ecm.platform.ui.web.tag.handler.LeafFaceletHandler(), HtmlMessage.COMPONENT_TYPE, null); 532 return new ComponentHandler(config); 533 } 534 535 /** 536 * @since 5.6 537 */ 538 public FaceletHandler getAliasFaceletHandler(String tagConfigId, Map<String, ValueExpression> variables, 539 List<String> blockedPatterns, FaceletHandler nextHandler) { 540 FaceletHandler currentHandler = nextHandler; 541 if (variables != null) { 542 currentHandler = getBareAliasTagHandler(tagConfigId, variables, blockedPatterns, nextHandler); 543 } 544 return currentHandler; 545 } 546 547 /** 548 * @since 8.1 549 */ 550 public TagHandler getAliasTagHandler(String tagConfigId, Map<String, ValueExpression> variables, 551 List<String> blockedPatterns, TagHandler nextHandler) { 552 TagHandler currentHandler = nextHandler; 553 if (variables != null) { 554 currentHandler = getBareAliasTagHandler(tagConfigId, variables, blockedPatterns, nextHandler); 555 } 556 return currentHandler; 557 } 558 559 protected TagHandler getBareAliasTagHandler(String tagConfigId, Map<String, ValueExpression> variables, 560 List<String> blockedPatterns, FaceletHandler nextHandler) { 561 // XXX also set id? cache? anchor? 562 ComponentConfig config = TagConfigFactory.createAliasTagConfig(tagConfig, tagConfigId, getTagAttributes(), 563 nextHandler); 564 AliasTagHandler alias = new AliasTagHandler(config, variables, blockedPatterns); 565 // NXP-18639: always wrap next alias handler in a component ref for tagConfigId to be taken into account and 566 // anchored in the view with this id. 567 ComponentConfig ref = TagConfigFactory.createComponentConfig(tagConfig, tagConfigId, getTagAttributes(), alias, 568 ComponentRef.COMPONENT_TYPE, null); 569 return new ComponentRefHandler(ref); 570 } 571 572 /** 573 * @since 6.0 574 */ 575 public static boolean isDevModeEnabled(FaceletContext ctx) { 576 // avoid stack overflow when using layout tags within the dev 577 // handler 578 if (Framework.isDevModeSet()) { 579 NuxeoLayoutManagerBean bean = lookupBean(ctx.getFacesContext()); 580 if (bean.isDevModeSet()) { 581 ExpressionFactory eFactory = ctx.getExpressionFactory(); 582 ValueExpression disableDevAttr = eFactory.createValueExpression(ctx, 583 "#{" + DEV_MODE_DISABLED_VARIABLE + "}", Boolean.class); 584 if (!Boolean.TRUE.equals(disableDevAttr.getValue(ctx))) { 585 return true; 586 } 587 } 588 } 589 return false; 590 } 591 592 protected static NuxeoLayoutManagerBean lookupBean(FacesContext ctx) { 593 String expr = "#{" + NuxeoLayoutManagerBean.NAME + "}"; 594 NuxeoLayoutManagerBean bean = (NuxeoLayoutManagerBean) ctx.getApplication().evaluateExpressionGet(ctx, expr, 595 Object.class); 596 if (bean == null) { 597 log.error("Managed bean not found: " + expr); 598 return null; 599 } 600 return bean; 601 } 602 603 /** 604 * @since 6.0 605 */ 606 public FaceletHandler getDisableDevModeTagHandler(String tagConfigId, FaceletHandler nextHandler) { 607 TagAttribute[] attrs = new TagAttribute[4]; 608 attrs[0] = createAttribute("var", DEV_MODE_DISABLED_VARIABLE); 609 attrs[1] = createAttribute("value", "true"); 610 attrs[2] = createAttribute("cache", "true"); 611 attrs[3] = createAttribute("blockMerge", "true"); 612 TagAttributes attributes = new TagAttributesImpl(attrs); 613 ComponentConfig config = TagConfigFactory.createAliasTagConfig(tagConfig, tagConfigId, attributes, nextHandler); 614 return new SetTagHandler(config); 615 } 616 617 /** 618 * @since 8.2 619 */ 620 public static boolean isAliasOptimEnabled() { 621 ConfigurationService cs = Framework.getService(ConfigurationService.class); 622 return !cs.isBooleanPropertyTrue("nuxeo.jsf.layout.removeAliasOptims"); 623 } 624 625}