001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ade.contenteditor; 029 030import org.opencms.acacia.shared.CmsAttributeConfiguration; 031import org.opencms.acacia.shared.CmsTabInfo; 032import org.opencms.acacia.shared.CmsType; 033import org.opencms.ade.contenteditor.CmsWidgetUtil.WidgetInfo; 034import org.opencms.ade.contenteditor.shared.CmsComplexWidgetData; 035import org.opencms.ade.contenteditor.shared.CmsExternalWidgetConfiguration; 036import org.opencms.file.CmsFile; 037import org.opencms.file.CmsObject; 038import org.opencms.file.CmsRequestContext; 039import org.opencms.i18n.CmsMessages; 040import org.opencms.i18n.CmsMultiMessages; 041import org.opencms.jsp.util.CmsKeyDummyMacroResolver; 042import org.opencms.main.CmsLog; 043import org.opencms.main.OpenCms; 044import org.opencms.util.CmsMacroResolver; 045import org.opencms.util.CmsStringUtil; 046import org.opencms.util.I_CmsMacroResolver; 047import org.opencms.widgets.A_CmsWidget; 048import org.opencms.widgets.I_CmsADEWidget; 049import org.opencms.widgets.I_CmsComplexWidget; 050import org.opencms.widgets.I_CmsWidget; 051import org.opencms.xml.CmsXmlContentDefinition; 052import org.opencms.xml.CmsXmlException; 053import org.opencms.xml.content.CmsDefaultXmlContentHandler; 054import org.opencms.xml.content.CmsXmlContentTab; 055import org.opencms.xml.content.I_CmsXmlContentHandler; 056import org.opencms.xml.content.I_CmsXmlContentHandler.DisplayType; 057import org.opencms.xml.types.A_CmsXmlContentValue; 058import org.opencms.xml.types.CmsXmlAccessRestrictionValue; 059import org.opencms.xml.types.CmsXmlDynamicCategoryValue; 060import org.opencms.xml.types.CmsXmlNestedContentDefinition; 061import org.opencms.xml.types.I_CmsXmlSchemaType; 062 063import java.util.ArrayList; 064import java.util.Collection; 065import java.util.Collections; 066import java.util.HashMap; 067import java.util.List; 068import java.util.Locale; 069import java.util.Map; 070 071import org.apache.commons.logging.Log; 072 073/** 074 * Visitor to read all types and attribute configurations within a content definition.<p> 075 */ 076public class CmsContentTypeVisitor { 077 078 /** 079 * Helper class to evaluate the widget display type.<p> 080 */ 081 protected static class DisplayTypeEvaluator { 082 083 /** The attribute name. */ 084 private String m_attributeName; 085 086 /** The attribute type configuration. */ 087 private CmsAttributeConfiguration m_config; 088 089 /** The configured display type. */ 090 private DisplayType m_configuredType; 091 092 /** The default display type. */ 093 private DisplayType m_default; 094 095 /** The applied rule. */ 096 private EvaluationRule m_rule; 097 098 /** 099 * Constructor.<p> 100 * 101 * @param config the attribute type configuration 102 * @param configuredType the configured display type 103 * @param defaultType the default display type 104 * @param rule the applied rule 105 */ 106 protected DisplayTypeEvaluator( 107 CmsAttributeConfiguration config, 108 DisplayType configuredType, 109 DisplayType defaultType, 110 EvaluationRule rule) { 111 112 m_config = config; 113 114 m_configuredType = configuredType; 115 m_default = defaultType; 116 m_rule = rule; 117 } 118 119 /** 120 * Returns the attribute name.<p> 121 * 122 * @return the attribute name 123 */ 124 protected String getAttributeName() { 125 126 return m_attributeName; 127 } 128 129 /** 130 * Returns the attribute configuration with the evaluated display type.<p> 131 * 132 * @param predecessor the proposed predecessor display type 133 * @param successor the proposed successor display type 134 * 135 * @return the attribute configuration 136 */ 137 protected CmsAttributeConfiguration getEvaluatedConfiguration(DisplayType predecessor, DisplayType successor) { 138 139 DisplayType resultingType = m_configuredType; 140 141 if (resultingType.equals(DisplayType.none)) { 142 if (m_rule.equals(EvaluationRule.rootLevel)) { 143 resultingType = DisplayType.wide; 144 } else { 145 resultingType = getProposedType(); 146 if ((predecessor != null) && predecessor.equals(DisplayType.none)) { 147 predecessor = null; 148 } 149 if ((successor != null) && successor.equals(DisplayType.none)) { 150 successor = null; 151 } 152 if ((predecessor != null) && predecessor.equals(DisplayType.column)) { 153 predecessor = DisplayType.singleline; 154 } 155 if ((successor != null) && successor.equals(DisplayType.column)) { 156 successor = DisplayType.singleline; 157 } 158 boolean strong = m_rule.equals(EvaluationRule.none) 159 || (m_rule.equals(EvaluationRule.optional) && m_default.equals(DisplayType.singleline)) 160 || (m_rule.equals(EvaluationRule.labelLength) && m_default.equals(DisplayType.wide)); 161 if (((predecessor == null) || (successor == null)) && strong) { 162 resultingType = m_default; 163 } else if ((predecessor != null) || (successor != null)) { 164 165 // check if the proposed type matches neither the type of the predecessor nor the type of the successor 166 if (!(((predecessor != null) && resultingType.equals(predecessor)) 167 || ((successor != null) && resultingType.equals(successor)))) { 168 DisplayType match = (predecessor != null) 169 && (predecessor.equals(DisplayType.wide) || predecessor.equals(DisplayType.singleline)) 170 ? predecessor 171 : ((successor != null) 172 && (successor.equals(DisplayType.wide) || successor.equals(DisplayType.singleline)) 173 ? successor 174 : null); 175 resultingType = match != null ? match : resultingType; 176 } 177 } 178 } 179 } 180 m_config.setDisplayType(resultingType.name()); 181 return m_config; 182 } 183 184 /** 185 * Returns the proposed display type.<p> 186 * 187 * @return the proposed display type 188 */ 189 protected DisplayType getProposedType() { 190 191 DisplayType resultingType = m_configuredType; 192 if (resultingType.equals(DisplayType.none)) { 193 switch (m_rule) { 194 case rootLevel: 195 case labelLength: 196 resultingType = DisplayType.wide; 197 break; 198 case optional: 199 resultingType = DisplayType.singleline; 200 break; 201 default: 202 resultingType = m_default; 203 204 } 205 } 206 return resultingType; 207 } 208 209 /** 210 * Sets the attribute name.<p> 211 * 212 * @param attributeName the attribute name 213 */ 214 protected void setAttributeName(String attributeName) { 215 216 m_attributeName = attributeName; 217 } 218 } 219 220 /** Widget display type evaluation rules. */ 221 protected enum EvaluationRule { 222 /** Label length rule. */ 223 labelLength, 224 /** No rule applied. */ 225 none, 226 /** Optional field rule. */ 227 optional, 228 /** Root level rule. */ 229 rootLevel 230 } 231 232 /** Logger instance for this class. */ 233 private static final Log LOG = CmsLog.getLog(CmsContentTypeVisitor.class); 234 235 /** The localization macro start sequence. */ 236 private static final String MESSAGE_MACRO_START = "" 237 + I_CmsMacroResolver.MACRO_DELIMITER 238 + I_CmsMacroResolver.MACRO_START 239 + CmsMacroResolver.KEY_LOCALIZED_PREFIX; 240 241 /** The old style localization macro start sequence. */ 242 private static final String MESSAGE_MACRO_START_OLD = "" 243 + I_CmsMacroResolver.MACRO_DELIMITER_OLD 244 + I_CmsMacroResolver.MACRO_START_OLD 245 + CmsMacroResolver.KEY_LOCALIZED_PREFIX; 246 247 /** The attribute configurations. */ 248 private Map<String, CmsAttributeConfiguration> m_attributeConfigurations; 249 250 /** The CMS context used for this visitor. */ 251 private CmsObject m_cms; 252 253 /** Map from attribute names to complex widget configurations. */ 254 private Map<String, CmsComplexWidgetData> m_complexWidgets = new HashMap<String, CmsComplexWidgetData>(); 255 256 /** The content handler. */ 257 private I_CmsXmlContentHandler m_contentHandler; 258 259 /** The dynamically loaded attribute names. */ 260 private List<String> m_dynamicallyLoaded; 261 262 /** The optional dynamic categoy fields. */ 263 private CmsDynamicCategoryFieldList m_dynamicCategoryFields = new CmsDynamicCategoryFieldList(); 264 265 /** The content resource. */ 266 private CmsFile m_file; 267 268 /** Indicates the visited content has fields that are configured to be invisible to the current user. */ 269 private boolean m_hasInvisible; 270 271 /** The content locale. */ 272 private Locale m_locale; 273 274 /** The locale synchronized attribute names. */ 275 private List<String> m_localeSynchronizations; 276 277 /** The messages. */ 278 private CmsMultiMessages m_messages; 279 280 /** The registered types. */ 281 private Map<String, CmsType> m_registeredTypes; 282 283 /** The root content definition. */ 284 private CmsXmlContentDefinition m_rootContentDefinition; 285 286 /** The tab informations. */ 287 private List<CmsTabInfo> m_tabInfos; 288 289 /** The widget configurations. */ 290 private Map<String, CmsExternalWidgetConfiguration> m_widgetConfigurations; 291 292 /** The widgets encountered by this visitor. */ 293 private List<I_CmsWidget> m_widgets = new ArrayList<I_CmsWidget>(); 294 295 /** 296 * Constructor.<p> 297 * 298 * @param cms the CMS context 299 * @param file the content file 300 * @param locale the content locale 301 */ 302 public CmsContentTypeVisitor(CmsObject cms, CmsFile file, Locale locale) { 303 304 m_file = file; 305 m_cms = cms; 306 m_locale = locale; 307 } 308 309 /** 310 * Returns the tab informations for the given content definition.<p> 311 * @param cms the CMS context 312 * @param definition the content definition 313 * @param messages the localization messages 314 * 315 * @return the tab informations 316 */ 317 public static List<CmsTabInfo> collectTabInfos( 318 CmsObject cms, 319 CmsXmlContentDefinition definition, 320 CmsMessages messages) { 321 322 List<CmsTabInfo> result = new ArrayList<CmsTabInfo>(); 323 CmsMacroResolver resolver = new CmsMacroResolver(); 324 resolver.setCmsObject(cms); 325 resolver.setMessages(messages); 326 CmsKeyDummyMacroResolver keyResolver = new CmsKeyDummyMacroResolver(resolver); 327 328 if (definition.getContentHandler().getTabs() != null) { 329 for (CmsXmlContentTab xmlTab : definition.getContentHandler().getTabs()) { 330 String tabKey = null; 331 String tabName; 332 333 // in case the tab name attribute contains a localization macro 334 if (xmlTab.getTabName().contains(MESSAGE_MACRO_START) 335 || xmlTab.getTabName().contains(MESSAGE_MACRO_START_OLD)) { 336 tabName = resolver.resolveMacros(xmlTab.getTabName()); 337 tabKey = CmsKeyDummyMacroResolver.getKey(keyResolver.resolveMacros(xmlTab.getTabName())); 338 } else { 339 tabName = messages.keyDefault( 340 A_CmsWidget.LABEL_PREFIX + definition.getInnerName() + "." + xmlTab.getTabName(), 341 xmlTab.getTabName()); 342 343 tabKey = A_CmsWidget.LABEL_PREFIX + definition.getInnerName() + "." + xmlTab.getTabName(); 344 } 345 346 String descriptionKey = null; 347 if (xmlTab.getDescription() != null) { 348 descriptionKey = CmsKeyDummyMacroResolver.getKey( 349 keyResolver.resolveMacros(xmlTab.getDescription())); 350 } 351 352 result.add( 353 new CmsTabInfo( 354 tabName, 355 tabKey, 356 xmlTab.getTabName(), 357 xmlTab.getIdName(), 358 xmlTab.getStartName(), 359 xmlTab.isCollapsed(), 360 resolver.resolveMacros(xmlTab.getDescription()), 361 descriptionKey, 362 xmlTab.getDescription())); 363 } 364 } 365 return result; 366 } 367 368 /** 369 * Gets the CMS context.<p> 370 * 371 * @return the CMS context 372 */ 373 public CmsObject getCmsObject() { 374 375 return m_cms; 376 } 377 378 /** 379 * Gets the list of widgets which have been processed by this visitor.<p> 380 * 381 * @return the list of widget 382 */ 383 public List<I_CmsWidget> getCollectedWidgets() { 384 385 return Collections.unmodifiableList(m_widgets); 386 } 387 388 /** 389 * Gets the map of complex widget configurations.<p> 390 * 391 * @return a map from attribute names to complex widget configurations 392 */ 393 public Map<String, CmsComplexWidgetData> getComplexWidgetData() { 394 395 return m_complexWidgets; 396 } 397 398 /** 399 * Returns the label for this value.<p> 400 * 401 * @param value the value 402 * @param defaultValue the default value 403 * 404 * @return the label 405 */ 406 public String getLabel(I_CmsXmlSchemaType value, String defaultValue) { 407 408 I_CmsXmlContentHandler handler = value.getContentDefinition().getContentHandler(); 409 if (handler instanceof CmsDefaultXmlContentHandler) { 410 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 411 String label = defaultHandler.getFieldLabels().get(value.getName()); 412 if (label != null) { 413 CmsMacroResolver resolver = new CmsMacroResolver(); 414 resolver.setCmsObject(m_cms); 415 resolver.setKeepEmptyMacros(true); 416 resolver.setMessages(m_messages); 417 return resolver.resolveMacros(label); 418 } 419 } 420 StringBuffer result = new StringBuffer(64); 421 result.append(A_CmsWidget.LABEL_PREFIX); 422 result.append(getTypeKey(value)); 423 return m_messages.keyDefault(result.toString(), defaultValue); 424 } 425 426 /** 427 * Gets the optional dynamic category fields collected so far. 428 * 429 * @return the optional dynamic category fields 430 */ 431 public CmsDynamicCategoryFieldList getOptionalDynamicCategoryFields() { 432 433 return m_dynamicCategoryFields; 434 } 435 436 /** 437 * Returns the tabInfos.<p> 438 * 439 * @return the tabInfos 440 */ 441 public List<CmsTabInfo> getTabInfos() { 442 443 return m_tabInfos; 444 } 445 446 /** 447 * Returns if the visited content has invisible fields.<p> 448 * 449 * @return <code>true</code> if the visited content has invisible fields 450 */ 451 public boolean hasInvisibleFields() { 452 453 return m_hasInvisible; 454 } 455 456 /** 457 * Returns <code>true</code> if the value of the attribute is dynamically loaded. 458 * @param attributeName the attribute to check 459 * @return <code>true</code> if the value of the attribute is dynamically loaded. 460 */ 461 public boolean isDynamicallyLoaded(String attributeName) { 462 463 return m_dynamicallyLoaded.contains(attributeName); 464 } 465 466 /** 467 * Checks if the content type widgets are compatible with the new content editor.<p> 468 * 469 * @param xmlContentDefinition the content definition 470 * 471 * @return <code>true</code> if the content type widgets are compatible with the new content editor 472 * 473 * @throws CmsXmlException if something goes wrong reading the type widget 474 */ 475 public boolean isEditorCompatible(CmsXmlContentDefinition xmlContentDefinition) throws CmsXmlException { 476 477 boolean result = true; 478 for (I_CmsXmlSchemaType subType : xmlContentDefinition.getTypeSequence()) { 479 if (subType.isSimpleType()) { 480 result = isEditorCompatible((A_CmsXmlContentValue)subType); 481 } else { 482 CmsXmlContentDefinition subTypeDefinition = ((CmsXmlNestedContentDefinition)subType).getNestedContentDefinition(); 483 result = isEditorCompatible(subTypeDefinition); 484 } 485 if (!result) { 486 break; 487 } 488 } 489 return result; 490 } 491 492 /** 493 * Visits all types within the XML content definition.<p> 494 * 495 * @param xmlContentDefinition the content definition 496 * @param messageLocale the locale 497 */ 498 /** 499 * Visits all types within the XML content definition.<p> 500 * 501 * @param xmlContentDefinition the content definition 502 * @param messageLocale the locale 503 */ 504 public void visitTypes(CmsXmlContentDefinition xmlContentDefinition, Locale messageLocale) { 505 506 m_rootContentDefinition = xmlContentDefinition; 507 m_contentHandler = xmlContentDefinition.getContentHandler(); 508 CmsMessages messages = null; 509 m_messages = new CmsMultiMessages(messageLocale); 510 m_messages.setFallbackHandler(xmlContentDefinition.getContentHandler().getMessageKeyHandler()); 511 512 try { 513 messages = OpenCms.getWorkplaceManager().getMessages(messageLocale); 514 if (messages != null) { 515 m_messages.addMessages(messages); 516 } 517 messages = m_contentHandler.getMessages(messageLocale); 518 if (messages != null) { 519 m_messages.addMessages(messages); 520 } 521 } catch (Exception e) { 522 // may happen during start up 523 LOG.debug(e.getMessage(), e); 524 } 525 // generate a new multi messages object and add the messages from the workplace 526 527 m_attributeConfigurations = new HashMap<String, CmsAttributeConfiguration>(); 528 m_widgetConfigurations = new HashMap<String, CmsExternalWidgetConfiguration>(); 529 m_registeredTypes = new HashMap<String, CmsType>(); 530 m_localeSynchronizations = new ArrayList<String>(); 531 m_dynamicallyLoaded = new ArrayList<String>(); 532 m_tabInfos = collectTabInfos(m_cms, xmlContentDefinition, m_messages); 533 readTypes(xmlContentDefinition, ""); 534 } 535 536 /** 537 * Returns the attribute configurations.<p> 538 * 539 * @return the attribute configurations 540 */ 541 protected Map<String, CmsAttributeConfiguration> getAttributeConfigurations() { 542 543 return m_attributeConfigurations; 544 } 545 546 /** 547 * Returns the locale synchronized attribute names.<p> 548 * 549 * @return the locale synchronized attribute names 550 */ 551 protected List<String> getLocaleSynchronizations() { 552 553 return m_localeSynchronizations; 554 } 555 556 /** 557 * Returns the types of the visited content definition.<p> 558 * 559 * @return the types 560 */ 561 protected Map<String, CmsType> getTypes() { 562 563 return m_registeredTypes; 564 } 565 566 /** 567 * Returns the external widget configurations.<p> 568 * 569 * @return the external widget configurations 570 */ 571 protected Collection<CmsExternalWidgetConfiguration> getWidgetConfigurations() { 572 573 return m_widgetConfigurations.values(); 574 } 575 576 /** 577 * Returns the help information for this value.<p> 578 * 579 * @param value the value 580 * 581 * @return the help information 582 */ 583 private String getHelp(I_CmsXmlSchemaType value) { 584 585 StringBuffer result = new StringBuffer(64); 586 I_CmsXmlContentHandler handler = value.getContentDefinition().getContentHandler(); 587 if (handler instanceof CmsDefaultXmlContentHandler) { 588 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 589 String help = defaultHandler.getFieldHelp().get(value.getName()); 590 if (help != null) { 591 CmsMacroResolver resolver = new CmsMacroResolver(); 592 resolver.setCmsObject(m_cms); 593 resolver.setKeepEmptyMacros(true); 594 resolver.setMessages(m_messages); 595 return resolver.resolveMacros(help); 596 } 597 } 598 result.append(A_CmsWidget.LABEL_PREFIX); 599 result.append(getTypeKey(value)); 600 result.append(A_CmsWidget.HELP_POSTFIX); 601 return m_messages.keyDefault(result.toString(), null); 602 } 603 604 /** 605 * Returns the schema type message key.<p> 606 * 607 * @param value the schema type 608 * 609 * @return the schema type message key 610 */ 611 private String getTypeKey(I_CmsXmlSchemaType value) { 612 613 StringBuffer result = new StringBuffer(64); 614 result.append(value.getContentDefinition().getInnerName()); 615 result.append('.'); 616 result.append(value.getName()); 617 return result.toString(); 618 } 619 620 /** 621 * Checks if the content value widget is compatible with the new content editor.<p> 622 * 623 * @param schemaType the content value type 624 * 625 * @return <code>true</code> if the content value widget is compatible with the new content editor 626 * 627 * @throws CmsXmlException if something goes wrong reading the type widget 628 */ 629 private boolean isEditorCompatible(A_CmsXmlContentValue schemaType) throws CmsXmlException { 630 631 boolean result = false; 632 I_CmsXmlContentHandler contentHandler = schemaType.getContentDefinition().getContentHandler(); 633 // We don't care about the old editor for the 'inheritable' widget configuration, 634 // so we're using the old getWidget method here 635 I_CmsWidget widget = contentHandler.getWidget(schemaType); 636 result = (widget == null) || (widget instanceof I_CmsADEWidget); 637 return result; 638 } 639 640 /** 641 * Returns if an element with the given path will be displayed at root level of a content editor tab.<p> 642 * 643 * @param path the element path 644 * 645 * @return <code>true</code> if an element with the given path will be displayed at root level of a content editor tab 646 */ 647 private boolean isTabRootLevel(String path) { 648 649 path = path.substring(1); 650 if (!path.contains("/")) { 651 return true; 652 } 653 if (m_tabInfos != null) { 654 for (CmsTabInfo info : m_tabInfos) { 655 if (info.isCollapsed() 656 && path.startsWith(info.getStartName()) 657 && !path.substring(info.getStartName().length() + 1).contains("/")) { 658 return true; 659 } 660 } 661 } 662 return false; 663 } 664 665 /** 666 * Reads the attribute configuration for the given schema type. May return <code>null</code> if no special configuration was set.<p> 667 * 668 * @param schemaType the schema type 669 * @param path the attribute path 670 * 671 * @return the attribute configuration 672 */ 673 private DisplayTypeEvaluator readConfiguration(A_CmsXmlContentValue schemaType, String path) { 674 675 String widgetName = null; 676 String widgetConfig = null; 677 CmsObject cms = getCmsObject(); 678 String label = getLabel(schemaType, schemaType.getName()); 679 // set the default display type 680 DisplayType configuredType = DisplayType.none; 681 DisplayType defaultType = DisplayType.none; 682 EvaluationRule rule = EvaluationRule.none; 683 try { 684 if ((cms.getRequestContext().getAttribute(CmsRequestContext.ATTRIBUTE_ADE_CONTEXT_PATH) == null) 685 && (m_file != null)) { 686 cms.getRequestContext().setAttribute( 687 CmsRequestContext.ATTRIBUTE_ADE_CONTEXT_PATH, 688 m_file.getRootPath()); 689 } 690 WidgetInfo widgetInfo = CmsWidgetUtil.collectWidgetInfo( 691 cms, 692 m_rootContentDefinition, 693 path, 694 m_messages, 695 null); 696 I_CmsWidget widget = widgetInfo.getWidget(); 697 I_CmsComplexWidget complexWidget = widgetInfo.getComplexWidget(); 698 configuredType = widgetInfo.getDisplayType(); 699 if (configuredType.equals(DisplayType.none) && schemaType.isSimpleType()) { 700 // check the type is on the root level of the document, those will be displayed 'wide' 701 // the path will always have a leading '/' 702 // also in case the label has more than 15 characters, we display 'wide' 703 if (isTabRootLevel(path)) { 704 rule = EvaluationRule.rootLevel; 705 } else if (label.length() > 15) { 706 rule = EvaluationRule.labelLength; 707 } else if ((schemaType.getMinOccurs() == 0)) { 708 rule = EvaluationRule.optional; 709 } 710 } 711 if (widget != null) { 712 widgetName = widget.getClass().getName(); 713 if ((configuredType == DisplayType.column) 714 && !(schemaType.isSimpleType() 715 && (schemaType.getMaxOccurs() == 1) 716 && widget.isCompactViewEnabled())) { 717 // column view is not allowed for this widget 718 configuredType = DisplayType.singleline; 719 } 720 long timer = 0; 721 if (widget instanceof I_CmsADEWidget) { 722 if (CmsContentService.LOG.isDebugEnabled()) { 723 timer = System.currentTimeMillis(); 724 } 725 I_CmsADEWidget adeWidget = (I_CmsADEWidget)widget; 726 defaultType = adeWidget.getDefaultDisplayType(); 727 widgetName = adeWidget.getWidgetName(); 728 729 widgetConfig = adeWidget.getConfiguration(cms, schemaType, m_messages, m_file, m_locale); 730 if (!adeWidget.isInternal() && !m_widgetConfigurations.containsKey(widgetName)) { 731 CmsExternalWidgetConfiguration externalConfiguration = new CmsExternalWidgetConfiguration( 732 widgetName, 733 adeWidget.getInitCall(), 734 adeWidget.getJavaScriptResourceLinks(cms), 735 adeWidget.getCssResourceLinks(cms)); 736 m_widgetConfigurations.put(widgetName, externalConfiguration); 737 } 738 if (CmsContentService.LOG.isDebugEnabled()) { 739 CmsContentService.LOG.debug( 740 Messages.get().getBundle().key( 741 Messages.LOG_TAKE_READING_WIDGET_CONFIGURATION_TIME_2, 742 widgetName, 743 "" + (System.currentTimeMillis() - timer))); 744 } 745 746 } 747 m_widgets.add(widget); 748 } 749 if (complexWidget != null) { 750 CmsComplexWidgetData widgetData = complexWidget.getWidgetData(m_cms); 751 CmsExternalWidgetConfiguration externalConfig = widgetData.getExternalWidgetConfiguration(); 752 if (externalConfig != null) { 753 m_widgetConfigurations.put(complexWidget.getName(), externalConfig); 754 } 755 m_complexWidgets.put(CmsContentService.getAttributeName(schemaType), widgetData); 756 } 757 } catch (Exception e) { 758 LOG.error(e.getLocalizedMessage(), e); 759 } 760 761 // remove the leading slash from element path to check visibility 762 boolean visible = !m_contentHandler.hasVisibilityHandlers() 763 || m_contentHandler.isVisible(cms, schemaType, path.substring(1), m_file, m_locale); 764 if (!visible) { 765 // set the has invisible flag 766 m_hasInvisible = true; 767 } 768 boolean localeSynchronized = (m_contentHandler.hasSynchronizedElements() 769 && m_contentHandler.getSynchronizations(true).getSynchronizationPaths().contains(path.substring(1))) 770 || schemaType.getTypeName().equals(CmsXmlDynamicCategoryValue.TYPE_NAME); 771 772 boolean dynamicallyLoaded = (schemaType instanceof CmsXmlDynamicCategoryValue) 773 || (schemaType instanceof CmsXmlAccessRestrictionValue); 774 775 CmsAttributeConfiguration result = new CmsAttributeConfiguration( 776 label, 777 getHelp(schemaType), 778 widgetName, 779 widgetConfig, 780 readDefaultValue(schemaType, path), 781 configuredType.name(), 782 visible, 783 localeSynchronized, 784 dynamicallyLoaded); 785 return new DisplayTypeEvaluator(result, configuredType, defaultType, rule); 786 } 787 788 /** 789 * Reads the default value for the given type.<p> 790 * 791 * @param schemaType the schema type 792 * @param path the element path 793 * 794 * @return the default value 795 */ 796 private String readDefaultValue(I_CmsXmlSchemaType schemaType, String path) { 797 798 return m_contentHandler.getDefault(getCmsObject(), m_file, schemaType, path, m_locale); 799 } 800 801 /** 802 * Reads the types from the given content definition and adds the to the map of already registered 803 * types if necessary.<p> 804 * 805 * @param xmlContentDefinition the XML content definition 806 * @param path the element path 807 * 808 * @return the type 809 */ 810 private CmsType readTypes(CmsXmlContentDefinition xmlContentDefinition, String path) { 811 812 String typeName = (CmsStringUtil.isEmptyOrWhitespaceOnly(path) ? "" : (path + ":")) 813 + CmsContentService.getTypeUri(xmlContentDefinition); 814 if (m_registeredTypes.containsKey(typeName)) { 815 return m_registeredTypes.get(typeName); 816 } 817 CmsType type = new CmsType(typeName); 818 type.setChoiceMaxOccurrence(xmlContentDefinition.getChoiceMaxOccurs()); 819 m_registeredTypes.put(typeName, type); 820 CmsType choiceType = null; 821 if (type.isChoice()) { 822 choiceType = new CmsType(typeName + "/" + CmsType.CHOICE_ATTRIBUTE_NAME); 823 m_registeredTypes.put(choiceType.getId(), choiceType); 824 type.addAttribute(CmsType.CHOICE_ATTRIBUTE_NAME, choiceType, 1, xmlContentDefinition.getChoiceMaxOccurs()); 825 } 826 ArrayList<DisplayTypeEvaluator> evaluators = new ArrayList<DisplayTypeEvaluator>(); 827 for (I_CmsXmlSchemaType subType : xmlContentDefinition.getTypeSequence()) { 828 String subTypeName = null; 829 String childPath = path + "/" + subType.getName(); 830 String subAttributeName = CmsContentService.getAttributeName(subType.getName(), typeName); 831 DisplayTypeEvaluator ev = readConfiguration((A_CmsXmlContentValue)subType, childPath); 832 ev.setAttributeName(subAttributeName); 833 evaluators.add(ev); 834 CmsType subEntityType; 835 if (subType.isSimpleType()) { 836 subTypeName = CmsContentService.TYPE_NAME_PREFIX + subType.getTypeName(); 837 if (!m_registeredTypes.containsKey(subTypeName)) { 838 subEntityType = new CmsType(subTypeName); 839 m_registeredTypes.put(subTypeName, subEntityType); 840 } else { 841 subEntityType = m_registeredTypes.get(subTypeName); 842 } 843 } else { 844 CmsXmlContentDefinition subTypeDefinition = ((CmsXmlNestedContentDefinition)subType).getNestedContentDefinition(); 845 subTypeName = CmsContentService.getTypeUri(subTypeDefinition); 846 subEntityType = readTypes(subTypeDefinition, childPath); 847 } 848 if (choiceType != null) { 849 choiceType.addAttribute( 850 subAttributeName, 851 subEntityType, 852 subType.getMinOccurs(), 853 subType.getMaxOccurs()); 854 } else { 855 int minOccurs = subType.getMinOccurs(); 856 if ((subType instanceof CmsXmlDynamicCategoryValue) && (subType.getMinOccurs() == 0)) { 857 String dynamicCategoryPath; 858 if ("".equals(path)) { 859 dynamicCategoryPath = subType.getName(); 860 } else { 861 dynamicCategoryPath = path + "/" + subType.getName(); 862 } 863 m_dynamicCategoryFields.add(dynamicCategoryPath); 864 minOccurs = 1; 865 } 866 type.addAttribute(subAttributeName, subEntityType, minOccurs, subType.getMaxOccurs()); 867 } 868 } 869 DisplayType predecessor = null; 870 for (int i = 0; i < evaluators.size(); i++) { 871 DisplayTypeEvaluator ev = evaluators.get(i); 872 DisplayType successor = ((i + 1) < evaluators.size()) ? evaluators.get(i + 1).getProposedType() : null; 873 CmsAttributeConfiguration evaluated = ev.getEvaluatedConfiguration(predecessor, successor); 874 m_attributeConfigurations.put(ev.getAttributeName(), evaluated); 875 if (evaluated.isLocaleSynchronized()) { 876 m_localeSynchronizations.add(ev.getAttributeName()); 877 } 878 if (evaluated.isDynamicallyLoaded()) { 879 m_dynamicallyLoaded.add(ev.getAttributeName()); 880 } 881 predecessor = DisplayType.valueOf(evaluated.getDisplayType()); 882 } 883 return type; 884 } 885}