001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (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.jsp.util; 029 030import org.opencms.acacia.shared.CmsTabInfo; 031import org.opencms.ade.contenteditor.CmsContentTypeVisitor; 032import org.opencms.ade.contenteditor.CmsWidgetUtil; 033import org.opencms.ade.contenteditor.CmsWidgetUtil.WidgetInfo; 034import org.opencms.file.CmsObject; 035import org.opencms.i18n.CmsMessages; 036import org.opencms.i18n.CmsMultiMessages; 037import org.opencms.jsp.util.I_CmsFormatterInfo.ResolveMode; 038import org.opencms.main.CmsLog; 039import org.opencms.main.OpenCms; 040import org.opencms.util.CmsMacroResolver; 041import org.opencms.util.CmsStringUtil; 042import org.opencms.widgets.A_CmsWidget; 043import org.opencms.widgets.CmsSelectWidgetOption; 044import org.opencms.xml.CmsXmlContentDefinition; 045import org.opencms.xml.content.CmsDefaultXmlContentHandler; 046import org.opencms.xml.content.I_CmsXmlContentHandler; 047import org.opencms.xml.types.CmsXmlNestedContentDefinition; 048import org.opencms.xml.types.I_CmsXmlSchemaType; 049 050import java.util.ArrayList; 051import java.util.Collection; 052import java.util.HashMap; 053import java.util.LinkedHashMap; 054import java.util.List; 055import java.util.Locale; 056import java.util.Map; 057import java.util.Set; 058import java.util.function.Function; 059import java.util.stream.Collectors; 060 061import org.apache.commons.logging.Log; 062 063/** 064 * Bean for accessing XML content schema information from JSPs. 065 */ 066public class CmsSchemaInfo { 067 068 /** 069 * Represents information about a single field in a content schema. 070 */ 071 public class Field implements I_CmsInfoWrapper { 072 073 /** The nested fields. */ 074 private LinkedHashMap<String, Field> m_children = new LinkedHashMap<>(); 075 076 /** The content definition (may be null). */ 077 private CmsXmlContentDefinition m_contentDefinition; 078 079 /** The field. */ 080 private I_CmsXmlSchemaType m_field; 081 082 /** The path of the field. */ 083 private String m_path = ""; 084 085 /** The widget information. */ 086 private WidgetInfo m_widgetInfo; 087 088 /** 089 * Creates a new instance for the root level of a schema. 090 * 091 * @param contentDef the content definition 092 */ 093 public Field(CmsXmlContentDefinition contentDef) { 094 095 m_contentDefinition = contentDef; 096 m_path = ""; 097 processContentDefinition(m_contentDefinition); 098 } 099 100 /** 101 * Creates a new instance. 102 * 103 * @param field the schema type for the field 104 * @param path the path of the field 105 */ 106 public Field(I_CmsXmlSchemaType field, String path) { 107 108 m_path = path; 109 m_field = field; 110 if (field instanceof CmsXmlNestedContentDefinition) { 111 CmsXmlNestedContentDefinition nestedDef = (CmsXmlNestedContentDefinition)field; 112 m_contentDefinition = nestedDef.getNestedContentDefinition(); 113 processContentDefinition(m_contentDefinition); 114 } 115 } 116 117 /** 118 * Gets the nested fields, if any. 119 * 120 * 121 * @return the nested fields 122 */ 123 public Collection<Field> getChildren() { 124 125 return m_children.values(); 126 } 127 128 /** 129 * Gets the content definition. 130 * 131 * @return the content definition 132 */ 133 public CmsXmlContentDefinition getContentDefinition() { 134 135 return m_contentDefinition; 136 } 137 138 /** 139 * Gets the default value. 140 * 141 * @return the default value 142 */ 143 @SuppressWarnings("synthetic-access") 144 public String getDefaultValue() { 145 146 if (m_field == null) { 147 // root node 148 return null; 149 } 150 return m_root.getContentDefinition().getContentHandler().getDefault(m_cms, null, m_field, m_path, m_locale); 151 } 152 153 /** 154 * Gets the description. 155 * 156 * @return the description 157 */ 158 public String getDescription() { 159 160 return getDescription(ResolveMode.text, m_cms.getRequestContext().getLocale()); 161 } 162 163 /** 164 * Gets the localized description 165 * 166 * @param locale the locale 167 * @return the description for the given locale 168 */ 169 public String getDescription(Locale locale) { 170 171 return getDescription(ResolveMode.text, locale); 172 173 } 174 175 /** 176 * Gets the description key. 177 * 178 * @return the description key 179 */ 180 public String getDescriptionKey() { 181 182 return getDescription(ResolveMode.key, m_cms.getRequestContext().getLocale()); 183 } 184 185 /** 186 * Gets the raw configured description. 187 * 188 * @return the raw configured description 189 */ 190 public String getDescriptionRaw() { 191 192 return getDescription(ResolveMode.raw, m_cms.getRequestContext().getLocale()); 193 } 194 195 /** 196 * Gets the display name. 197 * 198 * @return the display name 199 */ 200 public String getDisplayName() { 201 202 return getDisplayName(ResolveMode.text); 203 } 204 205 /** 206 * Gets the display name key. 207 * 208 * @return the display name key 209 */ 210 public String getDisplayNameKey() { 211 212 return getDisplayName(ResolveMode.key); 213 } 214 215 /** 216 * Gets the raw configured string for the display name. 217 * 218 * @return the raw display name 219 */ 220 public String getDisplayNameRaw() { 221 222 return getDisplayName(ResolveMode.raw); 223 224 } 225 226 /** 227 * Checks if this is a choice type. 228 * 229 * @return true if this is a choice type 230 */ 231 public boolean getIsChoice() { 232 233 return m_field.isChoiceType(); 234 } 235 236 /** 237 * Checks if this is a nested content type. 238 * 239 * @return true if this is a nested content type 240 */ 241 public boolean getIsNestedContent() { 242 243 return m_contentDefinition != null; 244 } 245 246 /** 247 * Gets the maximum number of occurrences. 248 * 249 * @return the maximum number of occurrences 250 */ 251 public int getMaxOccurs() { 252 253 return m_field.getMaxOccurs(); 254 } 255 256 /** 257 * Gets the minimum number of occurrences. 258 * 259 * @return the minimum number of occurrences 260 */ 261 public int getMinOccurs() { 262 263 return m_field.getMinOccurs(); 264 } 265 266 /** 267 * Gets the field name. 268 * 269 * @return the name 270 */ 271 public String getName() { 272 273 if (m_field == null) { 274 return null; 275 } 276 return m_field.getName(); 277 } 278 279 /** 280 * Tries to interpret the widget configuration as a select option configuration and returns the list of select options if this succeeds, and null otherwise. 281 * 282 * @return the list of parsed select options, or null if the widget configuration couldn't be interpreted that way 283 */ 284 @SuppressWarnings({"synthetic-access"}) 285 public List<CmsSelectWidgetOption> getParsedSelectOptions() { 286 287 String widgetConfig = getWidgetConfig(); 288 if (CmsStringUtil.isEmptyOrWhitespaceOnly(widgetConfig)) { 289 // passing an empty/null configuration to parseOptions would result in an empty list, not null, and we want null here 290 return null; 291 } 292 try { 293 List<CmsSelectWidgetOption> options = org.opencms.widgets.CmsSelectWidgetOption.parseOptions( 294 widgetConfig); 295 List<CmsSelectWidgetOption> result = new ArrayList<>(); 296 Set<String> values = options.stream().map(option -> option.getValue()).collect(Collectors.toSet()); 297 String defaultValue = getDefaultValue(); 298 Locale locale = m_cms.getRequestContext().getLocale(); 299 if (CmsStringUtil.isEmptyOrWhitespaceOnly(defaultValue) || !values.contains(defaultValue)) { 300 CmsSelectWidgetOption noValue = new CmsSelectWidgetOption( 301 "", 302 true, 303 org.opencms.gwt.Messages.get().getBundle(locale).key( 304 org.opencms.gwt.Messages.GUI_SELECTBOX_EMPTY_SELECTION_0)); 305 result.add(noValue); 306 } 307 308 result.addAll(options); 309 return result; 310 } catch (Exception e) { 311 LOG.info(e.getLocalizedMessage(), e); 312 return null; 313 } 314 } 315 316 /** 317 * Gets the field type. 318 * 319 * @return the type 320 */ 321 public String getType() { 322 323 return m_field.getTypeName(); 324 } 325 326 /** 327 * Gets the validation error message. 328 * 329 * @return the validation error message 330 */ 331 @SuppressWarnings("synthetic-access") 332 public String getValidationError() { 333 334 if (m_field == null) { 335 return null; 336 } 337 I_CmsXmlContentHandler handler = m_field.getContentDefinition().getContentHandler(); 338 if (!(handler instanceof CmsDefaultXmlContentHandler)) { 339 return null; 340 } 341 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 342 return defaultHandler.getValidationWarningOrErrorMessage(m_cms, m_locale, m_field.getName(), false, false); 343 } 344 345 /** 346 * Gets the validation error localization key. 347 * 348 * @return the validation error localization key 349 */ 350 @SuppressWarnings("synthetic-access") 351 public String getValidationErrorKey() { 352 353 if (m_field == null) { 354 return null; 355 } 356 I_CmsXmlContentHandler handler = m_field.getContentDefinition().getContentHandler(); 357 if (!(handler instanceof CmsDefaultXmlContentHandler)) { 358 return null; 359 } 360 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 361 return defaultHandler.getValidationWarningOrErrorMessage(m_cms, m_locale, m_field.getName(), false, true); 362 } 363 364 /** 365 * Gets the validation warning message. 366 * 367 * @return the validation warning message 368 */ 369 @SuppressWarnings({"synthetic-access"}) 370 public String getValidationWarning() { 371 372 if (m_field == null) { 373 return null; 374 } 375 I_CmsXmlContentHandler handler = m_field.getContentDefinition().getContentHandler(); 376 if (!(handler instanceof CmsDefaultXmlContentHandler)) { 377 return null; 378 } 379 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 380 return defaultHandler.getValidationWarningOrErrorMessage(m_cms, m_locale, m_field.getName(), true, false); 381 } 382 383 /** 384 * Gets the validation warning message key. 385 * 386 * @return the validation warning message key 387 */ 388 @SuppressWarnings("synthetic-access") 389 public String getValidationWarningKey() { 390 391 if (m_field == null) { 392 return null; 393 } 394 I_CmsXmlContentHandler handler = m_field.getContentDefinition().getContentHandler(); 395 if (!(handler instanceof CmsDefaultXmlContentHandler)) { 396 return null; 397 } 398 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 399 return defaultHandler.getValidationWarningOrErrorMessage(m_cms, m_locale, m_field.getName(), true, true); 400 } 401 402 /** 403 * Gets the visibility configuration string for the field. 404 * 405 * @return the visibility configuration string 406 */ 407 public String getVisibility() { 408 409 I_CmsXmlContentHandler handler = m_root.getContentDefinition().getContentHandler(); 410 if (handler instanceof CmsDefaultXmlContentHandler) { 411 return ((CmsDefaultXmlContentHandler)handler).getVisibilityConfigString(m_path); 412 } 413 return null; 414 } 415 416 /** 417 * Gets the widget. 418 * 419 * @return the widget 420 */ 421 @SuppressWarnings("synthetic-access") 422 public String getWidget() { 423 424 if (m_contentDefinition != null) { 425 return null; 426 } 427 WidgetInfo widgetInfo = getWidgetInfo(); 428 if (widgetInfo != null) { 429 return widgetInfo.getWidget().getClass().getName(); 430 } else { 431 LOG.error("Widget info not defined for " + m_path + " in " + m_root.getContentDefinition()); 432 return null; 433 } 434 } 435 436 /** 437 * Gets the widget configuration. 438 * 439 * @return the widget configuration 440 */ 441 @SuppressWarnings("synthetic-access") 442 public String getWidgetConfig() { 443 444 if (m_contentDefinition != null) { 445 return null; 446 } 447 WidgetInfo widgetInfo = getWidgetInfo(); 448 if (widgetInfo != null) { 449 return widgetInfo.getWidget().getConfiguration(); 450 } else { 451 LOG.error("Widget info not defined for " + m_path + " in " + m_root.getContentDefinition()); 452 return null; 453 } 454 455 } 456 457 /** 458 * Gets the description or description key. 459 * 460 * @param resolveMode the resolve mode 461 * @param locale the locale to use 462 * @return the description or localization key 463 */ 464 @SuppressWarnings("synthetic-access") 465 private String getDescription(ResolveMode resolveMode, Locale locale) { 466 467 if (m_field == null) { 468 return null; 469 } 470 StringBuffer result = new StringBuffer(64); 471 I_CmsXmlContentHandler handler = m_field.getContentDefinition().getContentHandler(); 472 if (handler instanceof CmsDefaultXmlContentHandler) { 473 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 474 String help = defaultHandler.getFieldHelp().get(m_field.getName()); 475 if (help != null) { 476 CmsMacroResolver resolver = new CmsMacroResolver(); 477 if (resolveMode == ResolveMode.raw) { 478 return help; 479 } else { 480 if (resolveMode == ResolveMode.key) { 481 resolver = new CmsKeyDummyMacroResolver(resolver); 482 } 483 resolver.setCmsObject(m_cms); 484 resolver.setKeepEmptyMacros(true); 485 resolver.setMessages(getMessages(locale)); 486 487 String val = resolver.resolveMacros(help); 488 if (resolveMode == ResolveMode.key) { 489 return CmsKeyDummyMacroResolver.getKey(val); 490 } else { 491 return val; 492 } 493 } 494 } 495 } 496 result.append(A_CmsWidget.LABEL_PREFIX); 497 result.append(getTypeKey(m_field)); 498 result.append(A_CmsWidget.HELP_POSTFIX); 499 switch (resolveMode) { 500 case key: 501 case raw: 502 return result.toString(); 503 case text: 504 default: 505 return getMessages(locale).keyDefault(result.toString(), null); 506 } 507 } 508 509 /** 510 * Gets the display name or localization key. 511 * 512 * @param keyOnly true if only the localization key should be returned rather than the localized display name 513 * @return the display name or localization key 514 */ 515 @SuppressWarnings("synthetic-access") 516 private String getDisplayName(ResolveMode resolveMode) { 517 518 if (m_field == null) { 519 return null; 520 } 521 I_CmsXmlContentHandler handler = m_field.getContentDefinition().getContentHandler(); 522 if (handler instanceof CmsDefaultXmlContentHandler) { 523 CmsDefaultXmlContentHandler defaultHandler = (CmsDefaultXmlContentHandler)handler; 524 String label = defaultHandler.getFieldLabels().get(m_field.getName()); 525 if (label != null) { 526 if (resolveMode == ResolveMode.raw) { 527 return label; 528 } else { 529 CmsMacroResolver resolver = new CmsMacroResolver(); 530 if (resolveMode == ResolveMode.key) { 531 resolver = new CmsKeyDummyMacroResolver(resolver); 532 } 533 resolver.setCmsObject(m_cms); 534 resolver.setKeepEmptyMacros(true); 535 resolver.setMessages(m_messages); 536 String val = resolver.resolveMacros(label); 537 if (resolveMode == ResolveMode.key) { 538 return CmsKeyDummyMacroResolver.getKey(val); 539 } else { 540 return val; 541 } 542 } 543 } 544 } 545 StringBuffer result = new StringBuffer(64); 546 result.append(A_CmsWidget.LABEL_PREFIX); 547 result.append(getTypeKey(m_field)); 548 switch (resolveMode) { 549 case raw: 550 case key: 551 return result.toString(); 552 case text: 553 default: 554 return m_messages.keyDefault(result.toString(), m_field.getName()); 555 } 556 } 557 558 /** 559 * Returns the schema type message key.<p> 560 * 561 * @param value the schema type 562 * 563 * @return the schema type message key 564 */ 565 private String getTypeKey(I_CmsXmlSchemaType value) { 566 567 StringBuffer result = new StringBuffer(64); 568 result.append(value.getContentDefinition().getInnerName()); 569 result.append('.'); 570 result.append(value.getName()); 571 return result.toString(); 572 } 573 574 /** 575 * Gets the widget info. 576 * 577 * @return the widget info 578 */ 579 @SuppressWarnings("synthetic-access") 580 private WidgetInfo getWidgetInfo() { 581 582 if (m_widgetInfo == null) { 583 m_widgetInfo = CmsWidgetUtil.collectWidgetInfo( 584 m_cms, 585 m_root.getContentDefinition(), 586 m_path, 587 null, 588 m_cms.getRequestContext().getLocale()); 589 } 590 return m_widgetInfo; 591 592 } 593 594 /** 595 * Process content definition. 596 * 597 * @param contentDef the content definition 598 */ 599 private void processContentDefinition(CmsXmlContentDefinition contentDef) { 600 601 List<I_CmsXmlSchemaType> fields = contentDef.getTypeSequence(); 602 for (I_CmsXmlSchemaType field : fields) { 603 String name = field.getName(); 604 m_children.put(name, new Field(field, combinePaths(m_path, name))); 605 } 606 } 607 608 } 609 610 /** 611 * Represents the a single editor tab and its fields. 612 */ 613 public class Tab implements I_CmsInfoWrapper { 614 615 private Locale m_defaultLocale; 616 617 /** The fields. */ 618 private List<Field> m_fields = new ArrayList<>(); 619 620 private int m_tabIndex = -1; 621 622 private Function<Locale, List<CmsTabInfo>> m_tabInfoProvider; 623 624 /** 625 * Default constructor - doesn't initialize anything. 626 * 627 * <p>Used for 'dummy' tab that is generated when no actual tabs are configured. 628 * 629 */ 630 public Tab() { 631 632 } 633 634 /** 635 * Constructor for tab that is actually configured in the schema. 636 * 637 * @param defaultLocale the default locale 638 * @param tabIndex the current tab index 639 * @param tabInfoProvider the function that provides the list of localized tab info objects 640 */ 641 public Tab(Locale defaultLocale, int tabIndex, Function<Locale, List<CmsTabInfo>> tabInfoProvider) { 642 643 m_tabInfoProvider = tabInfoProvider; 644 m_tabIndex = tabIndex; 645 m_defaultLocale = defaultLocale; 646 } 647 648 /** 649 * Adds a field. 650 * 651 * @param field the field to add 652 */ 653 public void add(Field field) { 654 655 m_fields.add(field); 656 } 657 658 /** 659 * Gets the description. 660 * 661 * @return the description 662 */ 663 public String getDescription() { 664 665 return localizedTabInfo(null, null, tabInfo -> tabInfo.getDescription()); 666 } 667 668 /** 669 * Gets the localized description. 670 * 671 * @param locale the locale 672 * @return the description for the locale 673 */ 674 @Override 675 public String getDescription(Locale locale) { 676 677 return localizedTabInfo(locale, null, tab -> tab.getDescription()); 678 } 679 680 /** 681 * Gets the description key. 682 * 683 * @return the description key 684 */ 685 public String getDescriptionKey() { 686 687 return localizedTabInfo(null, null, tabInfo -> tabInfo.getDescriptionKey()); 688 } 689 690 /** 691 * Gets the raw description string. 692 * 693 * @return the raw description 694 */ 695 public String getDescriptionRaw() { 696 697 return localizedTabInfo(null, null, tab -> tab.getDescriptionRaw()); 698 } 699 700 /** 701 * Gets the display name. 702 * 703 * @return the display name 704 */ 705 public String getDisplayName() { 706 707 return localizedTabInfo(null, null, tab -> tab.getTabName()); 708 } 709 710 /** 711 * Gets the display name key. 712 * 713 * @return the display name key 714 */ 715 public String getDisplayNameKey() { 716 717 return localizedTabInfo(null, null, tab -> tab.getTabNameKey()); 718 } 719 720 /** 721 * Gets the raw display name string. 722 * 723 * @return the raw display name string 724 */ 725 public String getDisplayNameRaw() { 726 727 return localizedTabInfo(null, null, tab -> tab.getTabNameRaw()); 728 729 } 730 731 /** 732 * Gets the fields. 733 * 734 * @return the fields 735 */ 736 public List<Field> getFields() { 737 738 return m_fields; 739 } 740 741 /** 742 * Helper method for reading localized tab information. 743 * 744 * @param locale the locale (null for current locale) 745 * @param defaultValue the default value 746 * @param function a function to extract the relevant information from a localized CmsTabInfo instance 747 * 748 * @return the result 749 */ 750 private String localizedTabInfo(Locale locale, String defaultValue, Function<CmsTabInfo, String> function) { 751 752 if (locale == null) { 753 locale = m_defaultLocale; 754 } 755 if ((m_tabIndex != -1) && (m_tabInfoProvider != null)) { 756 CmsTabInfo tabInfo = m_tabInfoProvider.apply(locale).get(m_tabIndex); 757 return function.apply(tabInfo); 758 } 759 return defaultValue; 760 } 761 762 } 763 764 /** The logger instance for this class. */ 765 private static final Log LOG = CmsLog.getLog(CmsSchemaInfo.class); 766 767 /** The CMS context. */ 768 private CmsObject m_cms; 769 770 private CmsXmlContentDefinition m_contentDefinition; 771 772 /** The locale. */ 773 private Locale m_locale; 774 775 /** The messages. */ 776 private CmsMultiMessages m_messages; 777 778 /** Cache of message objects. */ 779 private Map<Locale, CmsMultiMessages> m_messagesByLocale = new HashMap<>(); 780 781 /** The root field instanec representing the whole schema. */ 782 private Field m_root; 783 784 private Map<Locale, List<CmsTabInfo>> m_tabInfoCache = new HashMap<>(); 785 786 /** The tabs. */ 787 private List<Tab> m_tabs; 788 789 /** 790 * Creates a new instance. 791 * 792 * @param cms the CMS context 793 * @param contentDef the content definition 794 */ 795 public CmsSchemaInfo(CmsObject cms, CmsXmlContentDefinition contentDef) { 796 797 m_cms = cms; 798 m_locale = cms.getRequestContext().getLocale(); 799 m_root = new Field(contentDef); 800 m_contentDefinition = contentDef; 801 802 Locale locale = cms.getRequestContext().getLocale(); 803 m_messages = getMessages(locale); 804 initTabs(); 805 806 } 807 808 /** 809 * Combine schema paths. 810 * 811 * @param a the first path 812 * @param b the second path 813 * @return the combined paths 814 */ 815 static String combinePaths(String a, String b) { 816 817 if ("".equals(a)) { 818 return b; 819 } 820 return a + "/" + b; 821 } 822 823 /** 824 * Gets the root node. 825 * 826 * @return the root node 827 */ 828 public Field getRoot() { 829 830 return m_root; 831 } 832 833 /** 834 * Gets the tabs. 835 * 836 * @return the tabs 837 */ 838 public List<Tab> getTabs() { 839 840 return m_tabs; 841 } 842 843 /** 844 * Checks for tabs. 845 * 846 * @return true, if there are tabs 847 */ 848 public boolean hasTabs() { 849 850 return m_tabs.get(0).getDisplayName() == null; 851 } 852 853 /** 854 * Creates the message object for the given locale. 855 * 856 * @param locale the locale 857 * @return the messages for the given locale 858 */ 859 private CmsMultiMessages createMessages(Locale locale) { 860 861 CmsMessages messages; 862 CmsMultiMessages resultMessages = new CmsMultiMessages(locale); 863 resultMessages.setFallbackHandler(m_contentDefinition.getContentHandler().getMessageKeyHandler()); 864 try { 865 messages = OpenCms.getWorkplaceManager().getMessages(locale); 866 if (messages != null) { 867 resultMessages.addMessages(messages); 868 } 869 messages = m_contentDefinition.getContentHandler().getMessages(locale); 870 if (messages != null) { 871 resultMessages.addMessages(messages); 872 } 873 } catch (Exception e) { 874 LOG.debug(e.getMessage(), e); 875 } 876 return resultMessages; 877 } 878 879 /** 880 * Gets or creates the messages object for the given locale. 881 * 882 * @param locale the locale to use 883 * @return the messages for the locale 884 */ 885 private CmsMultiMessages getMessages(Locale locale) { 886 887 return m_messagesByLocale.computeIfAbsent(locale, l -> { 888 return createMessages(l); 889 }); 890 } 891 892 /** 893 * Gets or creates the localized tab information for the given locale. 894 * 895 * @param locale the locale 896 * @return the localized tab information 897 */ 898 private List<CmsTabInfo> getTabInfos(Locale locale) { 899 900 return m_tabInfoCache.computeIfAbsent( 901 locale, 902 l -> CmsContentTypeVisitor.collectTabInfos(m_cms, m_root.getContentDefinition(), getMessages(l))); 903 } 904 905 /** 906 * Initializes the tabs. 907 */ 908 private void initTabs() { 909 910 List<CmsTabInfo> tabs = CmsContentTypeVisitor.collectTabInfos(m_cms, m_root.getContentDefinition(), m_messages); 911 912 List<Tab> result = new ArrayList<>(); 913 if ((tabs == null) || (tabs.size() == 0)) { 914 Tab defaultTab = new Tab(); 915 result.add(defaultTab); 916 defaultTab.getFields().addAll(m_root.getChildren()); 917 } else { 918 int index = 0; 919 for (Field node : m_root.getChildren()) { 920 if ((index < tabs.size()) && node.getName().equals(tabs.get(index).getStartName())) { 921 Tab tab = new Tab(m_cms.getRequestContext().getLocale(), index, locale -> getTabInfos(locale)); 922 result.add(tab); 923 index += 1; 924 } 925 if (result.size() > 0) { 926 result.get(result.size() - 1).add(node); 927 } 928 } 929 } 930 m_tabs = result; 931 } 932}