001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH (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 GmbH, 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.widgets; 029 030import org.opencms.main.OpenCms; 031import org.opencms.util.CmsPair; 032import org.opencms.util.CmsStringUtil; 033import org.opencms.util.I_CmsRegexSubstitution; 034import org.opencms.workplace.galleries.CmsAjaxDownloadGallery; 035import org.opencms.workplace.galleries.CmsAjaxHtmlGallery; 036import org.opencms.workplace.galleries.CmsAjaxImageGallery; 037import org.opencms.workplace.galleries.CmsAjaxLinkGallery; 038import org.opencms.workplace.galleries.CmsAjaxTableGallery; 039 040import java.util.ArrayList; 041import java.util.Arrays; 042import java.util.Collections; 043import java.util.Iterator; 044import java.util.List; 045import java.util.Map; 046import java.util.regex.Matcher; 047import java.util.regex.Pattern; 048 049import com.google.common.collect.Maps; 050 051/** 052 * An option of a HTML type widget.<p> 053 * 054 * Options can be defined for each element of the type <code>OpenCmsHtml</code> using the widget <code>HtmlWidget</code>. 055 * They have to be placed in the annotation section of a XSD describing an XML content. The <code>configuration</code> attribute 056 * in the <code>layout</code> node for the element must contain the activated options as a comma separated String value:<p> 057 * 058 * <code><layout element="Text" widget="HtmlWidget" configuration="height:400px,link,anchor,imagegallery,downloadgallery,formatselect,source" /></code><p> 059 * 060 * Available options are: 061 * <ul> 062 * <li><code>anchor</code>: the anchor dialog button</li> 063 * <li><code>buttonbar:${button bar items, separated by ';'}</code>: an individual button bar configuration, 064 * see {@link #BUTTONBAR_DEFAULT} for an example.</li> 065 * <li><code>css:/vfs/path/to/cssfile.css</code>: the absolute path in the OpenCms VFS to the CSS style sheet 066 * to use to render the contents in the editor (availability depends on the integrated editor)</li> 067 * <li><code>formatselect</code>: the format selector for selecting text format like paragraph or headings</li> 068 * <li><code>formatselect.options:${list of options, separated by ';'}</code>: the options that should be available in the format selector, 069 * e.g. <code>formatselect.options:p;h1;h2</code></li> 070 * <li><code>fullpage</code>: the editor creates an entire HTML page code</li> 071 * <li><code>${gallerytype}</code>: Shows a gallery dialog button, e.g. <code>imagegallery</code> displays 072 * the image gallery button or <code>downloadgallery</code> displays the download gallery button</li> 073 * <li><code>height:${editorheight}</code>: the editor height, where the height can be specified in px or %, e.g. <code>400px</code></li> 074 * <li><code>hidebuttons:${list of buttons to hide, separated by ';'}</code>: the buttons to hide that usually appear in 075 * the default button bar, e.g. <code>hidebuttons:bold;italic;underline;strikethrough</code> hides some formatting buttons</li> 076 * <li><code>image</code>: the image dialog button (availability depends on the integrated editor)</li> 077 * <li><code>link</code>: the link dialog button</li> 078 * <li><code>source</code>: shows the source code toggle button(s)</li> 079 * <li><code>stylesxml:/vfs/path/to/stylefile.xml</code>: the absolute path in the OpenCms VFS to the user defined 080 * styles that should be displayed in the style selector (availability depends on the integrated editor)</li> 081 * <li><code>stylesformat:/vfs/path/to/stylefile.xml</code>: the absolute path in the OpenCms VFS to the user defined 082 * styles format that should be displayed in the style selector (availability depends on the integrated editor)</li> 083 * <li><code>table</code>: the table dialog button (availability depends on the integrated editor)</li> 084 * </ul> 085 * Some things like the button bar items should be defined in the global widget configuration of the file <code>opencms-vfs.xml</code>.<p> 086 * 087 * @since 6.0.1 088 */ 089public class CmsHtmlWidgetOption { 090 091 /** The button bar end block indicator. */ 092 public static final String BUTTONBAR_BLOCK_END = "]"; 093 094 /** The button bar start block indicator. */ 095 public static final String BUTTONBAR_BLOCK_START = "["; 096 097 /** The default editor widget button bar configuration. */ 098 public static final String BUTTONBAR_DEFAULT = "[;undo;redo;-;find;replace;-;removeformat;-;pastetext;-;copy;paste;-;bold;italic;underline;strikethrough;-;subscript;superscript;];" 099 + "[;alignleft;aligncenter;alignright;justify;-;orderedlist;unorderedlist;-;outdent;indent;];" 100 + "[;source;-;formatselect;style;editorlink;link;anchor;unlink;];" 101 + "[;imagegallery;downloadgallery;linkgallery;htmlgallery;tablegallery;-;table;-;specialchar;-;print;spellcheck;-;fitwindow;];" 102 // additional buttons from TinyMCE 103 + "[;abbr;absolute;acronym;advhr;attribs;backcolor;blockquote;cite;cleanup;del;emotions;fontselect;fontsizeselect;forecolor;hr" 104 + ";ins;insertdate;insertlayer;inserttime;ltr;media;movebackward;moveforward;newdocument;nonbreaking;pagebreak;rtl" 105 + ";styleprops;template;visualaid;visualchars;];"; 106 107 /** The default button bar configuration as List. */ 108 public static final List<String> BUTTONBAR_DEFAULT_LIST = CmsStringUtil.splitAsList(BUTTONBAR_DEFAULT, ';'); 109 110 /** The button bar separator. */ 111 public static final String BUTTONBAR_SEPARATOR = "-"; 112 113 /** The delimiter to use in the configuration String. */ 114 public static final String DELIMITER_OPTION = ","; 115 116 /** The delimiter to use for separation of option values. */ 117 public static final char DELIMITER_VALUE = ';'; 118 119 /** The editor widget default height to use. */ 120 public static final String EDITOR_DEFAULTHEIGHT = "260px"; 121 122 /** Option for the "abbreviation" button. */ 123 public static final String OPTION_ABBR = "abbr"; 124 125 /** Option for the "absolute" button. */ 126 public static final String OPTION_ABSOLUTE = "absolute"; 127 128 /** Option for the "acronym" button. */ 129 public static final String OPTION_ACRONYM = "acronym"; 130 131 /** Option for the "advanced hr" button. */ 132 public static final String OPTION_ADVHR = "advhr"; 133 134 /** Option for the "anchor" dialog. */ 135 public static final String OPTION_ANCHOR = "anchor"; 136 137 /** Option for the "insert/edit attributes" button. */ 138 public static final String OPTION_ATTRIBS = "attribs"; 139 140 /** Option for the "background color" button. */ 141 public static final String OPTION_BACKCOLOR = "backcolor"; 142 143 /** Option for the "block quote" button. */ 144 public static final String OPTION_BLOCKQUOTE = "blockquote"; 145 146 /** Option for the "buttonbar" configuration. */ 147 public static final String OPTION_BUTTONBAR = "buttonbar:"; 148 149 /** Option for the "citation" button. */ 150 public static final String OPTION_CITE = "cite"; 151 152 /** Option for the "clean up messy code" button. */ 153 public static final String OPTION_CLEANUP = "cleanup"; 154 155 /** Option for the css style sheet VFS path to use in the widget area. */ 156 public static final String OPTION_CSS = "css:"; 157 158 /** Option for the "mark text as deletion" button. */ 159 public static final String OPTION_DEL = "del"; 160 161 /** Option for the "editor link" dialog (editor specific). */ 162 public static final String OPTION_EDITORLINK = "editorlink"; 163 164 /** Option for the "emotions" button. */ 165 public static final String OPTION_EMOTIONS = "emotions"; 166 167 /** Option for the "find" dialog. */ 168 public static final String OPTION_FIND = "find"; 169 170 /** Option for the "font select" button. */ 171 public static final String OPTION_FONTSELECT = "fontselect"; 172 173 /** Option for the "font size" button. */ 174 public static final String OPTION_FONTSIZESELECT = "fontsizeselect"; 175 176 /** Option for the "text color" button. */ 177 public static final String OPTION_FORECOLOR = "forecolor"; 178 179 /** Option for the "formatselect" selector. */ 180 public static final String OPTION_FORMATSELECT = "formatselect"; 181 182 /** Option for the "formatselect" options selector. */ 183 public static final String OPTION_FORMATSELECT_OPTIONS = "formatselect.options:"; 184 185 /** Option for the "fullpage" editor variant. */ 186 public static final String OPTION_FULLPAGE = "fullpage"; 187 188 /** Option for the "height" configuration. */ 189 public static final String OPTION_HEIGHT = "height:"; 190 191 /** Option for the "hidebuttons" configuration. */ 192 public static final String OPTION_HIDEBUTTONS = "hidebuttons:"; 193 194 /** Option for the "hr" button. */ 195 public static final String OPTION_HR = "hr"; 196 197 /** Option for the "image" dialog. */ 198 public static final String OPTION_IMAGE = "image"; 199 200 /** Option to import styles from stylesheet into the style selector. */ 201 public static final String OPTION_IMPORTCSS = "importcss"; 202 203 /** Option for the "mark text as insertion" button. */ 204 public static final String OPTION_INS = "ins"; 205 206 /** Option for the "insert date" button. */ 207 public static final String OPTION_INSERTDATE = "insertdate"; 208 209 /** Option for the "insert layer" button. */ 210 public static final String OPTION_INSERTLAYER = "insertlayer"; 211 212 /** Option for the "insert time" button. */ 213 public static final String OPTION_INSERTTIME = "inserttime"; 214 215 /** Option for the "link" dialog. */ 216 public static final String OPTION_LINK = "link"; 217 218 /** Option for the "left to right text" button. */ 219 public static final String OPTION_LTR = "ltr"; 220 221 /** Option for the "insert media (flash, video, audio)" button. */ 222 public static final String OPTION_MEDIA = "media"; 223 224 /** Option for the "move backward (layer context)" button. */ 225 public static final String OPTION_MOVEBACKWARD = "movebackward"; 226 227 /** Option for the "move forward (layer context)" button. */ 228 public static final String OPTION_MOVEFORWARD = "moveforward"; 229 230 /** Option for the "new document (remove existing content)" button. */ 231 public static final String OPTION_NEWDOCUMENT = "newdocument"; 232 233 /** Option for the "non breaking white space" button. */ 234 public static final String OPTION_NONBREAKING = "nonbreaking"; 235 236 /** Option for the "page break" button. */ 237 public static final String OPTION_PAGEBREAK = "pagebreak"; 238 239 /** Option for the "paste from word" button. */ 240 public static final String OPTION_PASTEWORD = "pasteword"; 241 242 /** Option for the "replace" dialog. */ 243 public static final String OPTION_REPLACE = "replace"; 244 245 /** Option for the "right to left text" button. */ 246 public static final String OPTION_RTL = "rtl"; 247 248 /** Option for the "source" code mode. */ 249 public static final String OPTION_SOURCE = "source"; 250 251 /** Option for the "spell check" dialog. */ 252 public static final String OPTION_SPELLCHECK = "spellcheck"; 253 254 /** Option for the style select box. */ 255 public static final String OPTION_STYLE = "style"; 256 257 /** Option for the "edit CSS style" button. */ 258 public static final String OPTION_STYLEPROPS = "styleprops"; 259 260 /** Option for the styles XML VFS path to use in the widget area. */ 261 public static final String OPTION_STYLES = "stylesxml:"; 262 263 /** Option for the styles format VFS path to use in the widget area. */ 264 public static final String OPTION_STYLES_FORMAT = "stylesformat:"; 265 266 /** Option for the "table" dialog. */ 267 public static final String OPTION_TABLE = "table"; 268 269 /** Option for the "insert predefined template content" button. */ 270 public static final String OPTION_TEMPLATE = "template"; 271 272 /** Option for the "unlink" button. */ 273 public static final String OPTION_UNLINK = "unlink"; 274 275 /** Option for the "show/hide guidelines/invisible elements" button. */ 276 public static final String OPTION_VISUALAID = "visualaid"; 277 278 /** Option for the "show/hide visual control characters" button. */ 279 public static final String OPTION_VISUALCHARS = "visualchars"; 280 281 /** The optional buttons that can be additionally added to the button bar. */ 282 public static final String[] OPTIONAL_BUTTONS = { 283 OPTION_ANCHOR, 284 OPTION_EDITORLINK, 285 OPTION_FIND, 286 OPTION_FORMATSELECT, 287 OPTION_IMAGE, 288 OPTION_LINK, 289 OPTION_REPLACE, 290 OPTION_SOURCE, 291 OPTION_SPELLCHECK, 292 OPTION_STYLE, 293 OPTION_TABLE, 294 OPTION_UNLINK, 295 OPTION_ABBR, 296 OPTION_ABSOLUTE, 297 OPTION_ACRONYM, 298 OPTION_ADVHR, 299 OPTION_ATTRIBS, 300 OPTION_BACKCOLOR, 301 OPTION_CITE, 302 OPTION_CLEANUP, 303 OPTION_DEL, 304 OPTION_EMOTIONS, 305 OPTION_FONTSELECT, 306 OPTION_FONTSIZESELECT, 307 OPTION_FORECOLOR, 308 OPTION_INS, 309 OPTION_INSERTDATE, 310 OPTION_INSERTLAYER, 311 OPTION_INSERTTIME, 312 OPTION_LTR, 313 OPTION_MEDIA, 314 OPTION_MOVEBACKWARD, 315 OPTION_MOVEFORWARD, 316 OPTION_NEWDOCUMENT, 317 OPTION_NONBREAKING, 318 OPTION_PAGEBREAK, 319 OPTION_PASTEWORD, 320 OPTION_RTL, 321 OPTION_STYLEPROPS, 322 OPTION_TEMPLATE, 323 OPTION_VISUALAID, 324 OPTION_VISUALCHARS, 325 CmsAjaxImageGallery.GALLERYTYPE_NAME, 326 CmsAjaxDownloadGallery.GALLERYTYPE_NAME, 327 CmsAjaxHtmlGallery.GALLERYTYPE_NAME, 328 CmsAjaxLinkGallery.GALLERYTYPE_NAME, 329 CmsAjaxTableGallery.GALLERYTYPE_NAME}; 330 331 /** The optional buttons that can be additionally added to the button bar as list. */ 332 public static final List<String> OPTIONAL_BUTTONS_LIST = Arrays.asList(OPTIONAL_BUTTONS); 333 334 /** Pattern used for matching embedded gallery configurations. */ 335 public static final Pattern PATTERN_EMBEDDED_GALLERY_CONFIG = Pattern.compile( 336 "(?<![a-zA-Z0-9_])(imagegallery|downloadgallery)(\\{.*?\\})"); 337 338 /** Holds the global button bar configuration options to increase performance. */ 339 private static List<String> m_globalButtonBarOption; 340 341 /** The additional buttons list. */ 342 private List<String> m_additionalButtons; 343 344 /** The button bar items. */ 345 private List<String> m_buttonBar; 346 347 /** The button bar configuration options. */ 348 private List<String> m_buttonBarOption; 349 350 /** The button bar options. */ 351 private String m_buttonBarOptionString; 352 353 /** The configuration. */ 354 private String m_configuration; 355 356 /** The CSS style sheet path. */ 357 private String m_cssPath; 358 359 /** The editor height. */ 360 private String m_editorHeight; 361 362 /** The embedded configuration strings for galleries, if available. */ 363 private Map<String, String> m_embeddedConfigurations = Maps.newHashMap(); 364 365 /** The format select options. */ 366 private String m_formatSelectOptions; 367 368 /** The full page flag. */ 369 private boolean m_fullPage; 370 371 /** The hidden buttons. */ 372 private List<String> m_hiddenButtons; 373 374 /** True if styles from stylesheet should be imported into the style selector. */ 375 private boolean m_importCss; 376 377 /** The path for custom styles. */ 378 private String m_stylesFormatPath; 379 380 /** The style XML path. */ 381 private String m_stylesXmlPath; 382 383 /** 384 * Creates a new empty HTML widget object object.<p> 385 */ 386 public CmsHtmlWidgetOption() { 387 388 // initialize the options 389 init(null); 390 } 391 392 /** 393 * Creates a new HTML widget object object, configured by the given configuration String.<p> 394 * 395 * @param configuration configuration String to parse 396 */ 397 public CmsHtmlWidgetOption(String configuration) { 398 399 // initialize the options 400 init(configuration); 401 } 402 403 /** 404 * Returns a HTML widget configuration String created from the given HTML widget option.<p> 405 * 406 * @param option the HTML widget options to create the configuration String for 407 * 408 * @return a select widget configuration String created from the given HTML widget option object 409 */ 410 public static String createConfigurationString(CmsHtmlWidgetOption option) { 411 412 StringBuffer result = new StringBuffer(512); 413 boolean added = false; 414 if (!option.getEditorHeight().equals(EDITOR_DEFAULTHEIGHT)) { 415 // append the height configuration 416 result.append(OPTION_HEIGHT); 417 result.append(option.getEditorHeight()); 418 added = true; 419 } 420 if (option.useCss()) { 421 // append the CSS VFS path 422 if (added) { 423 result.append(DELIMITER_OPTION); 424 } 425 result.append(OPTION_CSS); 426 result.append(option.getCssPath()); 427 added = true; 428 } 429 if (option.showStylesXml()) { 430 // append the styles XML VFS path 431 if (added) { 432 result.append(DELIMITER_OPTION); 433 } 434 result.append(OPTION_STYLES); 435 result.append(option.getStylesXmlPath()); 436 added = true; 437 } 438 if (!option.getAdditionalButtons().isEmpty()) { 439 // append the additional buttons to show 440 if (added) { 441 result.append(DELIMITER_OPTION); 442 } 443 result.append( 444 CmsStringUtil.collectionAsString(option.getAdditionalButtons(), String.valueOf(DELIMITER_OPTION))); 445 added = true; 446 } 447 if (!option.getHiddenButtons().isEmpty()) { 448 // append the buttons to hide from tool bar 449 if (added) { 450 result.append(DELIMITER_OPTION); 451 } 452 result.append(OPTION_HIDEBUTTONS); 453 result.append(CmsStringUtil.collectionAsString(option.getHiddenButtons(), String.valueOf(DELIMITER_VALUE))); 454 added = true; 455 } 456 if (CmsStringUtil.isNotEmpty(option.getButtonBarOptionString())) { 457 // append the button bar definition 458 if (added) { 459 result.append(DELIMITER_OPTION); 460 } 461 result.append(OPTION_BUTTONBAR); 462 result.append(option.getButtonBarOptionString()); 463 added = true; 464 } 465 if (option.isImportCss()) { 466 if (added) { 467 result.append(DELIMITER_OPTION); 468 } 469 result.append(OPTION_IMPORTCSS); 470 added = true; 471 } 472 if (CmsStringUtil.isNotEmpty(option.getFormatSelectOptions())) { 473 // append the format select option String 474 if (added) { 475 result.append(DELIMITER_OPTION); 476 } 477 result.append(OPTION_FORMATSELECT_OPTIONS); 478 result.append(option.getFormatSelectOptions()); 479 added = true; 480 } 481 482 return result.toString(); 483 } 484 485 /** 486 * Parses and removes embedded gallery configuration strings. 487 * 488 * @param configuration the configuration string to parse 489 * 490 * @return a map containing both the string resulting from removing the embedded configurations, and the embedded configurations as a a map 491 */ 492 public static CmsPair<String, Map<String, String>> parseEmbeddedGalleryOptions(String configuration) { 493 494 final Map<String, String> galleryOptions = Maps.newHashMap(); 495 String resultConfig = CmsStringUtil.substitute( 496 PATTERN_EMBEDDED_GALLERY_CONFIG, 497 configuration, 498 new I_CmsRegexSubstitution() { 499 500 public String substituteMatch(String string, Matcher matcher) { 501 502 String galleryName = string.substring(matcher.start(1), matcher.end(1)); 503 String embeddedConfig = string.substring(matcher.start(2), matcher.end(2)); 504 galleryOptions.put(galleryName, embeddedConfig); 505 return galleryName; 506 } 507 }); 508 return CmsPair.create(resultConfig, galleryOptions); 509 } 510 511 /** 512 * Returns the buttons to show additionally as list with button names.<p> 513 * 514 * @return the buttons to show additionally as list with button names 515 */ 516 public List<String> getAdditionalButtons() { 517 518 return m_additionalButtons; 519 } 520 521 /** 522 * Returns the specific editor button bar string generated from the configuration.<p> 523 * 524 * The lookup map can contain translations for the button names, the separator and the block names. 525 * The button bar will be automatically surrounded by block start and end items if they are not explicitly defined.<p> 526 * 527 * It may be necessary to write your own method to generate the button bar string for a specific editor widget. 528 * In this case, use the method {@link #getButtonBarShownItems()} to get the calculated list of shown button bar items.<p> 529 * 530 * @param buttonNamesLookUp the lookup map with translations for the button names, the separator and the block names or <code>null</code> 531 * @param itemSeparator the separator for the tool bar items 532 * @return the button bar string generated from the configuration 533 */ 534 public String getButtonBar(Map<String, String> buttonNamesLookUp, String itemSeparator) { 535 536 return getButtonBar(buttonNamesLookUp, itemSeparator, true); 537 } 538 539 /** 540 * Returns the specific editor button bar string generated from the configuration.<p> 541 * 542 * The lookup map can contain translations for the button names, the separator and the block names.<p> 543 * 544 * It may be necessary to write your own method to generate the button bar string for a specific editor widget. 545 * In this case, use the method {@link #getButtonBarShownItems()} to get the calculated list of shown button bar items.<p> 546 * 547 * @param buttonNamesLookUp the lookup map with translations for the button names, the separator and the block names or <code>null</code> 548 * @param itemSeparator the separator for the tool bar items 549 * @param addMissingBlock flag indicating if the button bar should be automatically surrounded by a block if not explicitly defined 550 * @return the button bar string generated from the configuration 551 */ 552 public String getButtonBar(Map<String, String> buttonNamesLookUp, String itemSeparator, boolean addMissingBlock) { 553 554 // first get the calculated button bar items 555 List<String> buttonBar = getButtonBarShownItems(); 556 if (addMissingBlock) { 557 // the button bar has to be surrounded by block items, check it 558 if (!buttonBar.isEmpty()) { 559 if (!buttonBar.get(0).equals(BUTTONBAR_BLOCK_START)) { 560 // add missing start block item 561 buttonBar.add(0, BUTTONBAR_BLOCK_START); 562 } 563 if (!buttonBar.get(buttonBar.size() - 1).equals(BUTTONBAR_BLOCK_END)) { 564 // add missing end block items 565 buttonBar.add(BUTTONBAR_BLOCK_END); 566 } 567 } 568 } 569 StringBuffer result = new StringBuffer(512); 570 boolean isFirst = true; 571 for (Iterator<String> i = buttonBar.iterator(); i.hasNext();) { 572 String barItem = i.next(); 573 if (BUTTONBAR_BLOCK_START.equals(barItem)) { 574 // start a block 575 if (!isFirst) { 576 result.append(itemSeparator); 577 } 578 result.append(getButtonName(barItem, buttonNamesLookUp)); 579 // starting a block means also: next item is the first (of the block) 580 isFirst = true; 581 } else if (BUTTONBAR_BLOCK_END.equals(barItem)) { 582 // end a block (there is no item separator added before ending the block) 583 result.append(getButtonName(barItem, buttonNamesLookUp)); 584 isFirst = false; 585 } else { 586 // button or separator 587 if (!isFirst) { 588 result.append(itemSeparator); 589 } 590 result.append(getButtonName(barItem, buttonNamesLookUp)); 591 isFirst = false; 592 } 593 } 594 return result.toString(); 595 } 596 597 /** 598 * Returns the individual button bar configuration option.<p> 599 * 600 * @return the individual button bar configuration option 601 */ 602 public List<String> getButtonBarOption() { 603 604 if (m_buttonBarOption == null) { 605 // use lazy initializing for performance reasons 606 if (CmsStringUtil.isEmpty(getButtonBarOptionString())) { 607 // no individual configuration defined, create empty list 608 m_buttonBarOption = Collections.emptyList(); 609 } else { 610 // create list of button bar options from configuration string 611 m_buttonBarOption = CmsStringUtil.splitAsList(getButtonBarOptionString(), DELIMITER_VALUE, true); 612 } 613 } 614 return m_buttonBarOption; 615 } 616 617 /** 618 * Returns the individual button bar configuration option string.<p> 619 * 620 * @return the individual button bar configuration option string 621 */ 622 public String getButtonBarOptionString() { 623 624 return m_buttonBarOptionString; 625 } 626 627 /** 628 * Returns the calculated button bar items, including blocks and separators, considering the current widget configuration.<p> 629 * 630 * Use this method to get the calculated list of button bar items if {@link #getButtonBar(Map, String)} can not 631 * be used for a specific editor widget.<p> 632 * 633 * @return the calculated button bar items 634 */ 635 public List<String> getButtonBarShownItems() { 636 637 if (m_buttonBar == null) { 638 // first get individual button bar configuration 639 List<String> buttonBar = getButtonBarOption(); 640 if (buttonBar.isEmpty()) { 641 // no specific button bar defined, try to get global configuration first 642 if (m_globalButtonBarOption == null) { 643 // global configuration not yet parsed, check it now 644 String defaultConf = OpenCms.getXmlContentTypeManager().getWidgetDefaultConfiguration( 645 CmsHtmlWidget.class.getName()); 646 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(defaultConf) 647 && defaultConf.contains(OPTION_BUTTONBAR)) { 648 // found a global configuration containing a button bar definition, parse it 649 CmsHtmlWidgetOption option = new CmsHtmlWidgetOption(defaultConf); 650 // set global configuration in static member 651 m_globalButtonBarOption = option.getButtonBarOption(); 652 } else { 653 // no global configuration present, set static member to empty list 654 m_globalButtonBarOption = Collections.emptyList(); 655 } 656 } 657 if (m_globalButtonBarOption.isEmpty()) { 658 // no global button bar configuration found, use default button bar 659 buttonBar = BUTTONBAR_DEFAULT_LIST; 660 } else { 661 // found a global configuration containing a button bar definition, use it 662 buttonBar = m_globalButtonBarOption; 663 } 664 } 665 666 List<String> result = new ArrayList<String>(buttonBar.size()); 667 int lastSep = -1; 668 int lastBlock = -1; 669 boolean buttonInBlockAdded = false; 670 boolean buttonSinceSepAdded = false; 671 for (Iterator<String> i = buttonBar.iterator(); i.hasNext();) { 672 String barItem = i.next(); 673 if (BUTTONBAR_BLOCK_START.equals(barItem)) { 674 // start a block 675 if ((lastSep != -1) && (lastSep == (result.size() - 1))) { 676 // remove last separator before block start 677 result.remove(lastSep); 678 } 679 lastBlock = result.size(); 680 lastSep = -1; 681 buttonInBlockAdded = false; 682 buttonSinceSepAdded = false; 683 result.add(BUTTONBAR_BLOCK_START); 684 } else if (BUTTONBAR_BLOCK_END.equals(barItem)) { 685 // end a block 686 if (lastBlock != -1) { 687 // block has been started 688 if (lastSep == (result.size() - 1)) { 689 // remove last separator before block end 690 result.remove(lastSep); 691 } 692 //now check if there are items in it 693 if (buttonInBlockAdded) { 694 // block has items, add end 695 result.add(BUTTONBAR_BLOCK_END); 696 } else { 697 // block has no items, remove block start ite, 698 result.remove(lastBlock); 699 } 700 lastBlock = -1; 701 lastSep = -1; 702 buttonInBlockAdded = false; 703 buttonSinceSepAdded = false; 704 } 705 } else if (BUTTONBAR_SEPARATOR.equals(barItem)) { 706 // insert a separator depending on preconditions 707 if (buttonSinceSepAdded) { 708 lastSep = result.size(); 709 result.add(BUTTONBAR_SEPARATOR); 710 buttonSinceSepAdded = false; 711 } 712 } else { 713 // insert a button depending on preconditions 714 if (getHiddenButtons().contains(barItem)) { 715 // skip hidden buttons 716 continue; 717 } 718 if (OPTIONAL_BUTTONS_LIST.contains(barItem)) { 719 // check optional buttons 720 if (CmsAjaxImageGallery.GALLERYTYPE_NAME.equals(barItem)) { 721 // special handling of image button to keep compatibility 722 if (!(getAdditionalButtons().contains(barItem) 723 || getAdditionalButtons().contains(OPTION_IMAGE))) { 724 // skip image gallery as it is not defined as additional button 725 continue; 726 } 727 } else if (OPTION_UNLINK.equals(barItem)) { 728 // special handling of unlink button to show only if anchor, editor link or link button are active 729 if (!(getAdditionalButtons().contains(OPTION_LINK) 730 || getAdditionalButtons().contains(OPTION_EDITORLINK) 731 || getAdditionalButtons().contains(OPTION_ANCHOR))) { 732 // skip unlink button because no link buttons are defined as additional buttons 733 continue; 734 } 735 } else if (OPTION_STYLE.equals(barItem)) { 736 boolean showStyles = getAdditionalButtons().contains(barItem) 737 || (getStylesFormatPath() != null) 738 || (getStylesXmlPath() != null); 739 if (!showStyles) { 740 continue; 741 } 742 } else if (!getAdditionalButtons().contains(barItem)) { 743 // skip all optional buttons that are not defined 744 continue; 745 } 746 } 747 result.add(barItem); 748 buttonSinceSepAdded = true; 749 if (lastBlock != -1) { 750 buttonInBlockAdded = true; 751 } 752 } 753 } 754 m_buttonBar = result; 755 } 756 return m_buttonBar; 757 } 758 759 /** 760 * Returns the original configuration String that was used to initialize the HTML widget options.<p> 761 * 762 * @return the original configuration String 763 */ 764 public String getConfiguration() { 765 766 return m_configuration; 767 } 768 769 /** 770 * Returns the CSS style sheet VFS path to use in the widget area.<p> 771 * 772 * @return the CSS style sheet VFS path to use in the widget area 773 */ 774 public String getCssPath() { 775 776 return m_cssPath; 777 } 778 779 /** 780 * Returns the widget editor height.<p> 781 * 782 * @return the widget editor height 783 */ 784 public String getEditorHeight() { 785 786 return m_editorHeight; 787 } 788 789 /** 790 * Gets the embedded gallery configurations.<p> 791 * 792 * @return the embedded gallery configurations 793 */ 794 public Map<String, String> getEmbeddedConfigurations() { 795 796 return m_embeddedConfigurations; 797 } 798 799 /** 800 * Returns the options for the format select box as String.<p> 801 * 802 * @return the options for the format select box as String 803 */ 804 public String getFormatSelectOptions() { 805 806 return m_formatSelectOptions; 807 } 808 809 /** 810 * Returns the buttons to hide as list with button names.<p> 811 * 812 * @return the buttons to hide as list with button names 813 */ 814 public List<String> getHiddenButtons() { 815 816 return m_hiddenButtons; 817 } 818 819 /** 820 * Returns the styles format VFS path to use in the widget area.<p> 821 * 822 * @return the styles XML format path to use in the widget area 823 */ 824 public String getStylesFormatPath() { 825 826 return m_stylesFormatPath; 827 } 828 829 /** 830 * Returns the styles XML VFS path to use in the widget area.<p> 831 * 832 * @return the styles XML VFS path to use in the widget area 833 */ 834 public String getStylesXmlPath() { 835 836 return m_stylesXmlPath; 837 } 838 839 /** 840 * Initializes the widget options from the given configuration String.<p> 841 * 842 * @param configuration the configuration String 843 */ 844 public void init(String configuration) { 845 846 // initialize the members 847 m_additionalButtons = new ArrayList<String>(OPTIONAL_BUTTONS_LIST.size()); 848 m_configuration = configuration; 849 m_editorHeight = EDITOR_DEFAULTHEIGHT; 850 m_hiddenButtons = new ArrayList<String>(); 851 // initialize the widget options 852 parseOptions(configuration); 853 } 854 855 /** 856 * Returns if the button with the given name should be additionally shown.<p> 857 * 858 * @param buttonName the button name to check 859 * 860 * @return <code>true</code> if the button with the given name should be additionally shown, otherwise <code>false</code> 861 */ 862 public boolean isButtonAdditional(String buttonName) { 863 864 return getAdditionalButtons().contains(buttonName); 865 } 866 867 /** 868 * Returns if the button with the given name should be hidden.<p> 869 * 870 * @param buttonName the button name to check 871 * 872 * @return <code>true</code> if the button with the given name should be hidden, otherwise <code>false</code> 873 */ 874 public boolean isButtonHidden(String buttonName) { 875 876 return getHiddenButtons().contains(buttonName); 877 } 878 879 /** 880 * Returns if the button with the given name is optional.<p> 881 * 882 * @param buttonName the button name to check 883 * 884 * @return <code>true</code> if the button with the given name is optional, otherwise <code>false</code> 885 */ 886 public boolean isButtonOptional(String buttonName) { 887 888 return OPTIONAL_BUTTONS_LIST.contains(buttonName); 889 } 890 891 /** 892 * Returns if the editor should be used in full page mode.<p> 893 * 894 * @return true if the editor should be used in full page mode, otherwise false 895 */ 896 public boolean isFullPage() { 897 898 return m_fullPage; 899 } 900 901 /** 902 * Return true if the content stylesheet's styles should be imported into the style selector.<p> 903 * 904 * @return true if the content stylesheet's styles should be imported into the style selector 905 */ 906 public boolean isImportCss() { 907 908 return m_importCss; 909 } 910 911 /** 912 * Sets the buttons to show additionally as list with button names.<p> 913 * 914 * @param buttons the buttons to show additionally as list with button names 915 */ 916 public void setAdditionalButtons(List<String> buttons) { 917 918 m_additionalButtons = buttons; 919 } 920 921 /** 922 * Sets the individual button bar configuration option.<p> 923 * 924 * @param buttonBar the individual button bar configuration option 925 */ 926 public void setButtonBarOption(List<String> buttonBar) { 927 928 m_buttonBarOption = buttonBar; 929 } 930 931 /** 932 * Sets the individual button bar configuration option string.<p> 933 * 934 * @param buttonBar the individual button bar configuration option string 935 */ 936 public void setButtonBarOptionString(String buttonBar) { 937 938 m_buttonBarOptionString = buttonBar; 939 } 940 941 /** 942 * Sets the CSS style sheet VFS path to use in the widget area.<p> 943 * 944 * @param cssPath the CSS style sheet VFS path to use in the widget area 945 */ 946 public void setCssPath(String cssPath) { 947 948 m_cssPath = cssPath; 949 } 950 951 /** 952 * Sets the widget editor height.<p> 953 * 954 * @param editorHeight the widget editor height 955 */ 956 public void setEditorHeight(String editorHeight) { 957 958 m_editorHeight = editorHeight; 959 } 960 961 /** 962 * Sets the options for the format select box as String.<p> 963 * 964 * @param formatSelectOptions the options for the format select box as String 965 */ 966 public void setFormatSelectOptions(String formatSelectOptions) { 967 968 m_formatSelectOptions = formatSelectOptions; 969 } 970 971 /** 972 * Sets if the editor should be used in full page mode.<p> 973 * 974 * @param fullPage true if the editor should be used in full page mode, otherwise false 975 */ 976 public void setFullPage(boolean fullPage) { 977 978 m_fullPage = fullPage; 979 } 980 981 /** 982 * Sets the buttons to hide as list with button names.<p> 983 * 984 * @param buttons the buttons to hide as list with button names 985 */ 986 public void setHiddenButtons(List<String> buttons) { 987 988 m_hiddenButtons = buttons; 989 } 990 991 /** 992 * Sets the styles format VFS path to use in the widget area.<p> 993 * 994 * @param stylesFormatPath the styles XML VFS path to use in the widget area 995 */ 996 public void setStylesFormatPath(String stylesFormatPath) { 997 998 m_stylesFormatPath = stylesFormatPath; 999 } 1000 1001 /** 1002 * Sets the styles XML VFS path to use in the widget area.<p> 1003 * 1004 * @param stylesXmlPath the styles XML VFS path to use in the widget area 1005 */ 1006 public void setStylesXmlPath(String stylesXmlPath) { 1007 1008 m_stylesXmlPath = stylesXmlPath; 1009 } 1010 1011 /** 1012 * Returns true if the anchor dialog button should be available.<p> 1013 * 1014 * @return if the anchor dialog button should be available 1015 */ 1016 public boolean showAnchorDialog() { 1017 1018 return getAdditionalButtons().contains(OPTION_ANCHOR); 1019 } 1020 1021 /** 1022 * Returns true if the format selector should be available.<p> 1023 * 1024 * @return if the format selector should be available 1025 */ 1026 public boolean showFormatSelect() { 1027 1028 return getAdditionalButtons().contains(OPTION_FORMATSELECT); 1029 } 1030 1031 /** 1032 * Returns true if the specified gallery type dialog button is shown.<p> 1033 * 1034 * @param galleryType the gallery type to check 1035 * @return true if the specified gallery type dialog button is shown, otherwise false 1036 */ 1037 public boolean showGalleryDialog(String galleryType) { 1038 1039 return getAdditionalButtons().contains(galleryType); 1040 } 1041 1042 /** 1043 * Returns true if the image dialog button should be available.<p> 1044 * 1045 * @return if the image dialog button should be available 1046 */ 1047 public boolean showImageDialog() { 1048 1049 return getAdditionalButtons().contains(OPTION_IMAGE); 1050 } 1051 1052 /** 1053 * Returns true if the link dialog button should be available.<p> 1054 * 1055 * @return if the link dialog button should be available 1056 */ 1057 public boolean showLinkDialog() { 1058 1059 return getAdditionalButtons().contains(OPTION_LINK); 1060 } 1061 1062 /** 1063 * Returns true if the source code button should be available.<p> 1064 * 1065 * @return if the source code button should be available 1066 */ 1067 public boolean showSourceEditor() { 1068 1069 return getAdditionalButtons().contains(OPTION_SOURCE); 1070 } 1071 1072 /** 1073 * Returns true if the styles format selector should be available.<p> 1074 * 1075 * @return if the styles format selector should be available 1076 */ 1077 public boolean showStylesFormat() { 1078 1079 return CmsStringUtil.isNotEmpty(getStylesFormatPath()); 1080 } 1081 1082 /** 1083 * Returns true if the styles selector should be available.<p> 1084 * 1085 * @return if the styles selector should be available 1086 */ 1087 public boolean showStylesXml() { 1088 1089 return CmsStringUtil.isNotEmpty(getStylesXmlPath()); 1090 } 1091 1092 /** 1093 * Returns true if the table dialog button should be available.<p> 1094 * 1095 * @return if the table dialog button should be available 1096 */ 1097 public boolean showTableDialog() { 1098 1099 return getAdditionalButtons().contains(OPTION_TABLE); 1100 } 1101 1102 /** 1103 * Returns true if the widget editor should use a defined CSS style sheet.<p> 1104 * 1105 * @return if the widget editor should use a defined CSS style sheet 1106 */ 1107 public boolean useCss() { 1108 1109 return CmsStringUtil.isNotEmpty(getCssPath()); 1110 } 1111 1112 /** 1113 * Adds a button to the list of defined additional buttons.<p> 1114 * 1115 * @param buttonName the button name to add 1116 */ 1117 protected void addAdditionalButton(String buttonName) { 1118 1119 m_additionalButtons.add(buttonName); 1120 } 1121 1122 /** 1123 * Returns the real button name matched with the look up map.<p> 1124 * 1125 * If no value is found in the look up map, the button name is returned unchanged.<p> 1126 * 1127 * @param barItem the button bar item name to look up 1128 * @param buttonNamesLookUp the look up map containing the button names and/or separator name to use 1129 * @return the translated button name 1130 */ 1131 protected String getButtonName(String barItem, Map<String, String> buttonNamesLookUp) { 1132 1133 String result = barItem; 1134 if (buttonNamesLookUp != null) { 1135 String translatedName = buttonNamesLookUp.get(barItem); 1136 if (CmsStringUtil.isNotEmpty(translatedName)) { 1137 result = translatedName; 1138 } 1139 } 1140 return result; 1141 } 1142 1143 /** 1144 * Parses the given configuration String.<p> 1145 * 1146 * @param configuration the configuration String to parse 1147 */ 1148 protected void parseOptions(String configuration) { 1149 1150 if (CmsStringUtil.isNotEmpty(configuration)) { 1151 1152 CmsPair<String, Map<String, String>> simplifiedStringAndGalleryOptions = parseEmbeddedGalleryOptions( 1153 configuration); 1154 configuration = simplifiedStringAndGalleryOptions.getFirst(); 1155 m_embeddedConfigurations = simplifiedStringAndGalleryOptions.getSecond(); 1156 1157 List<String> options = CmsStringUtil.splitAsList(configuration, DELIMITER_OPTION, true); 1158 Iterator<String> i = options.iterator(); 1159 while (i.hasNext()) { 1160 String option = i.next(); 1161 // check which option is defined 1162 if (option.startsWith(OPTION_FORMATSELECT_OPTIONS)) { 1163 // the format select options 1164 option = option.substring(OPTION_FORMATSELECT_OPTIONS.length()); 1165 setFormatSelectOptions(option); 1166 } else if (option.startsWith(OPTION_HEIGHT)) { 1167 // the editor height 1168 option = option.substring(OPTION_HEIGHT.length()); 1169 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(option)) { 1170 setEditorHeight(option); 1171 } 1172 } else if (option.startsWith(OPTION_HIDEBUTTONS)) { 1173 // buttons to hide from the tool bar 1174 option = option.substring(OPTION_HIDEBUTTONS.length()); 1175 setHiddenButtons(CmsStringUtil.splitAsList(option, DELIMITER_VALUE, true)); 1176 } else if (option.startsWith(OPTION_CSS)) { 1177 // the editor CSS 1178 option = option.substring(OPTION_CSS.length()); 1179 setCssPath(option); 1180 } else if (option.startsWith(OPTION_STYLES)) { 1181 // the editor styles XML path 1182 option = option.substring(OPTION_STYLES.length()); 1183 setStylesXmlPath(option); 1184 } else if (option.startsWith(OPTION_STYLES_FORMAT)) { 1185 // the editor styles format path 1186 option = option.substring(OPTION_STYLES_FORMAT.length()); 1187 setStylesFormatPath(option); 1188 } else if (option.startsWith(OPTION_BUTTONBAR)) { 1189 // the button bar definition string 1190 option = option.substring(OPTION_BUTTONBAR.length()); 1191 setButtonBarOptionString(option); 1192 } else if (option.startsWith(OPTION_IMPORTCSS)) { 1193 m_importCss = true; 1194 } else { 1195 // check if option describes an additional button 1196 if (OPTIONAL_BUTTONS_LIST.contains(option)) { 1197 addAdditionalButton(option); 1198 } 1199 } 1200 } 1201 } 1202 } 1203}