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