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