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 /** Option for the default protocol for links */ 300 public static final String OPTION_LINKDEFAULTPROTOCOL = "linkdefaultprotocol:"; 301 302 /** The optional buttons that can be additionally added to the button bar. */ 303 public static final String[] OPTIONAL_BUTTONS = { 304 OPTION_ANCHOR, 305 OPTION_EDITORLINK, 306 OPTION_FIND, 307 OPTION_FORMATSELECT, 308 OPTION_IMAGE, 309 OPTION_LINK, 310 OPTION_REPLACE, 311 OPTION_SOURCE, 312 OPTION_SPELLCHECK, 313 OPTION_STYLE, 314 OPTION_TABLE, 315 OPTION_UNLINK, 316 OPTION_HR, 317 OPTION_ABBR, 318 OPTION_ABSOLUTE, 319 OPTION_ACRONYM, 320 OPTION_ADVHR, 321 OPTION_ATTRIBS, 322 OPTION_BACKCOLOR, 323 OPTION_BLOCKQUOTE, 324 OPTION_CITE, 325 OPTION_CLEANUP, 326 OPTION_DEL, 327 OPTION_EMOTIONS, 328 OPTION_FONTSELECT, 329 OPTION_FONTSIZESELECT, 330 OPTION_FORECOLOR, 331 OPTION_INS, 332 OPTION_INSERTDATE, 333 OPTION_INSERTLAYER, 334 OPTION_INSERTTIME, 335 OPTION_LTR, 336 OPTION_MEDIA, 337 OPTION_MOVEBACKWARD, 338 OPTION_MOVEFORWARD, 339 OPTION_NEWDOCUMENT, 340 OPTION_NONBREAKING, 341 OPTION_PAGEBREAK, 342 OPTION_PASTEWORD, 343 OPTION_PRINT, 344 OPTION_RTL, 345 OPTION_STYLEPROPS, 346 OPTION_SPECIALCHAR, 347 OPTION_TEMPLATE, 348 OPTION_VISUALAID, 349 CmsAjaxImageGallery.GALLERYTYPE_NAME, 350 CmsAjaxDownloadGallery.GALLERYTYPE_NAME, 351 CmsAjaxLinkGallery.GALLERYTYPE_NAME}; 352 353 /** The optional buttons that can be additionally added to the button bar as list. */ 354 public static final List<String> OPTIONAL_BUTTONS_LIST = Arrays.asList(OPTIONAL_BUTTONS); 355 356 /** Pattern used for matching embedded gallery configurations. */ 357 public static final Pattern PATTERN_EMBEDDED_GALLERY_CONFIG = Pattern.compile( 358 "(?<![a-zA-Z0-9_])(imagegallery|downloadgallery)(\\{.*?\\})"); 359 360 /** If this is set, the contents of the path following the ':' will be interpreted as JSON and passed to TinyMCE directly. */ 361 public static final String OPTION_EDITORCONFIG = "editorconfig:"; 362 363 /** Holds the global button bar configuration options to increase performance. */ 364 private static List<String> m_globalButtonBarOption; 365 366 /** The additional buttons list. */ 367 private List<String> m_additionalButtons; 368 369 /** Flag which controls whether scripts are allowed in the source code editor. */ 370 private boolean m_allowScripts; 371 372 /** The button bar items. */ 373 private List<String> m_buttonBar; 374 375 /** The button bar configuration options. */ 376 private List<String> m_buttonBarOption; 377 378 /** The button bar options. */ 379 private String m_buttonBarOptionString; 380 381 /** The configuration. */ 382 private String m_configuration; 383 384 /** The CSS style sheet path. */ 385 private String m_cssPath; 386 387 /** The editor height. */ 388 private String m_editorHeight; 389 390 /** The embedded configuration strings for galleries, if available. */ 391 private Map<String, String> m_embeddedConfigurations = Maps.newHashMap(); 392 393 /** The format select options. */ 394 private String m_formatSelectOptions; 395 396 /** The full page flag. */ 397 private boolean m_fullPage; 398 399 /** The hidden buttons. */ 400 private List<String> m_hiddenButtons; 401 402 /** True if styles from stylesheet should be imported into the style selector. */ 403 private boolean m_importCss; 404 405 /** 406 private boolean m_allowScripts; 407 408 /** The path for custom styles. */ 409 private String m_stylesFormatPath; 410 411 /** The style XML path. */ 412 private String m_stylesXmlPath; 413 414 /** Path to an external TinyMCE JSON config file. */ 415 private String m_editorConfigPath; 416 417 /** The link default protocol */ 418 private String m_linkDefaultProtocol; 419 420 /** 421 * Creates a new empty HTML widget object object.<p> 422 */ 423 public CmsHtmlWidgetOption() { 424 425 // initialize the options 426 init(null); 427 } 428 429 /** 430 * Creates a new HTML widget object object, configured by the given configuration String.<p> 431 * 432 * @param configuration configuration String to parse 433 */ 434 public CmsHtmlWidgetOption(String configuration) { 435 436 // initialize the options 437 init(configuration); 438 } 439 440 /** 441 * Returns a HTML widget configuration String created from the given HTML widget option.<p> 442 * 443 * @param option the HTML widget options to create the configuration String for 444 * 445 * @return a select widget configuration String created from the given HTML widget option object 446 */ 447 public static String createConfigurationString(CmsHtmlWidgetOption option) { 448 449 StringBuffer result = new StringBuffer(512); 450 boolean added = false; 451 if (!option.getEditorHeight().equals(EDITOR_DEFAULMAXTHEIGHT)) { 452 // append the height configuration 453 result.append(OPTION_HEIGHT); 454 result.append(option.getEditorHeight()); 455 added = true; 456 } 457 if (option.useCss()) { 458 // append the CSS VFS path 459 if (added) { 460 result.append(DELIMITER_OPTION); 461 } 462 result.append(OPTION_CSS); 463 result.append(option.getCssPath()); 464 added = true; 465 } 466 if (option.showStylesXml()) { 467 // append the styles XML VFS path 468 if (added) { 469 result.append(DELIMITER_OPTION); 470 } 471 result.append(OPTION_STYLES); 472 result.append(option.getStylesXmlPath()); 473 added = true; 474 } 475 if (!option.getAdditionalButtons().isEmpty()) { 476 // append the additional buttons to show 477 if (added) { 478 result.append(DELIMITER_OPTION); 479 } 480 result.append( 481 CmsStringUtil.collectionAsString(option.getAdditionalButtons(), String.valueOf(DELIMITER_OPTION))); 482 added = true; 483 } 484 if (!option.getHiddenButtons().isEmpty()) { 485 // append the buttons to hide from tool bar 486 if (added) { 487 result.append(DELIMITER_OPTION); 488 } 489 result.append(OPTION_HIDEBUTTONS); 490 result.append(CmsStringUtil.collectionAsString(option.getHiddenButtons(), String.valueOf(DELIMITER_VALUE))); 491 added = true; 492 } 493 if (CmsStringUtil.isNotEmpty(option.getButtonBarOptionString())) { 494 // append the button bar definition 495 if (added) { 496 result.append(DELIMITER_OPTION); 497 } 498 result.append(OPTION_BUTTONBAR); 499 result.append(option.getButtonBarOptionString()); 500 added = true; 501 } 502 if (option.isImportCss()) { 503 if (added) { 504 result.append(DELIMITER_OPTION); 505 } 506 result.append(OPTION_IMPORTCSS); 507 added = true; 508 } 509 if (CmsStringUtil.isNotEmpty(option.getFormatSelectOptions())) { 510 // append the format select option String 511 if (added) { 512 result.append(DELIMITER_OPTION); 513 } 514 result.append(OPTION_FORMATSELECT_OPTIONS); 515 result.append(option.getFormatSelectOptions()); 516 added = true; 517 } 518 519 if (null != option.getEditorConfigPath()) { 520 if (added) { 521 result.append(DELIMITER_OPTION); 522 } 523 result.append(OPTION_EDITORCONFIG); 524 result.append(option.getEditorConfigPath()); 525 added = true; 526 } 527 528 if (CmsStringUtil.isNotEmpty(option.getLinkDefaultProtocol())) { 529 result.append(OPTION_LINKDEFAULTPROTOCOL); 530 result.append(option.getLinkDefaultProtocol()); 531 } 532 533 return result.toString(); 534 } 535 536 /** 537 * Parses and removes embedded gallery configuration strings. 538 * 539 * @param configuration the configuration string to parse 540 * 541 * @return a map containing both the string resulting from removing the embedded configurations, and the embedded configurations as a a map 542 */ 543 public static CmsPair<String, Map<String, String>> parseEmbeddedGalleryOptions(String configuration) { 544 545 final Map<String, String> galleryOptions = Maps.newHashMap(); 546 String resultConfig = CmsStringUtil.substitute( 547 PATTERN_EMBEDDED_GALLERY_CONFIG, 548 configuration, 549 new I_CmsRegexSubstitution() { 550 551 public String substituteMatch(String string, Matcher matcher) { 552 553 String galleryName = string.substring(matcher.start(1), matcher.end(1)); 554 String embeddedConfig = string.substring(matcher.start(2), matcher.end(2)); 555 galleryOptions.put(galleryName, embeddedConfig); 556 return galleryName; 557 } 558 }); 559 return CmsPair.create(resultConfig, galleryOptions); 560 } 561 562 /** 563 * Returns the buttons to show additionally as list with button names.<p> 564 * 565 * @return the buttons to show additionally as list with button names 566 */ 567 public List<String> getAdditionalButtons() { 568 569 return m_additionalButtons; 570 } 571 572 /** 573 * Returns the specific editor button bar string generated from the configuration.<p> 574 * 575 * The lookup map can contain translations for the button names, the separator and the block names. 576 * The button bar will be automatically surrounded by block start and end items if they are not explicitly defined.<p> 577 * 578 * It may be necessary to write your own method to generate the button bar string for a specific editor widget. 579 * In this case, use the method {@link #getButtonBarShownItems()} to get the calculated list of shown button bar items.<p> 580 * 581 * @param buttonNamesLookUp the lookup map with translations for the button names, the separator and the block names or <code>null</code> 582 * @param itemSeparator the separator for the tool bar items 583 * @return the button bar string generated from the configuration 584 */ 585 public String getButtonBar(Map<String, String> buttonNamesLookUp, String itemSeparator) { 586 587 return getButtonBar(buttonNamesLookUp, itemSeparator, true); 588 } 589 590 /** 591 * Returns the specific editor button bar string generated from the configuration.<p> 592 * 593 * The lookup map can contain translations for the button names, the separator and the block names.<p> 594 * 595 * It may be necessary to write your own method to generate the button bar string for a specific editor widget. 596 * In this case, use the method {@link #getButtonBarShownItems()} to get the calculated list of shown button bar items.<p> 597 * 598 * @param buttonNamesLookUp the lookup map with translations for the button names, the separator and the block names or <code>null</code> 599 * @param itemSeparator the separator for the tool bar items 600 * @param addMissingBlock flag indicating if the button bar should be automatically surrounded by a block if not explicitly defined 601 * @return the button bar string generated from the configuration 602 */ 603 public String getButtonBar(Map<String, String> buttonNamesLookUp, String itemSeparator, boolean addMissingBlock) { 604 605 // first get the calculated button bar items 606 List<String> buttonBar = getButtonBarShownItems(); 607 if (addMissingBlock) { 608 // the button bar has to be surrounded by block items, check it 609 if (!buttonBar.isEmpty()) { 610 if (!buttonBar.get(0).equals(BUTTONBAR_BLOCK_START)) { 611 // add missing start block item 612 buttonBar.add(0, BUTTONBAR_BLOCK_START); 613 } 614 if (!buttonBar.get(buttonBar.size() - 1).equals(BUTTONBAR_BLOCK_END)) { 615 // add missing end block items 616 buttonBar.add(BUTTONBAR_BLOCK_END); 617 } 618 } 619 } 620 StringBuffer result = new StringBuffer(512); 621 boolean isFirst = true; 622 for (Iterator<String> i = buttonBar.iterator(); i.hasNext();) { 623 String barItem = i.next(); 624 if (BUTTONBAR_BLOCK_START.equals(barItem)) { 625 // start a block 626 if (!isFirst) { 627 result.append(itemSeparator); 628 } 629 result.append(getButtonName(barItem, buttonNamesLookUp)); 630 // starting a block means also: next item is the first (of the block) 631 isFirst = true; 632 } else if (BUTTONBAR_BLOCK_END.equals(barItem)) { 633 // end a block (there is no item separator added before ending the block) 634 result.append(getButtonName(barItem, buttonNamesLookUp)); 635 isFirst = false; 636 } else { 637 // button or separator 638 if (!isFirst) { 639 result.append(itemSeparator); 640 } 641 result.append(getButtonName(barItem, buttonNamesLookUp)); 642 isFirst = false; 643 } 644 } 645 return result.toString(); 646 } 647 648 /** 649 * Returns the individual button bar configuration option.<p> 650 * 651 * @return the individual button bar configuration option 652 */ 653 public List<String> getButtonBarOption() { 654 655 if (m_buttonBarOption == null) { 656 // use lazy initializing for performance reasons 657 if (CmsStringUtil.isEmpty(getButtonBarOptionString())) { 658 // no individual configuration defined, create empty list 659 m_buttonBarOption = Collections.emptyList(); 660 } else { 661 // create list of button bar options from configuration string 662 m_buttonBarOption = CmsStringUtil.splitAsList(getButtonBarOptionString(), DELIMITER_VALUE, true); 663 } 664 } 665 return m_buttonBarOption; 666 } 667 668 /** 669 * Returns the individual button bar configuration option string.<p> 670 * 671 * @return the individual button bar configuration option string 672 */ 673 public String getButtonBarOptionString() { 674 675 return m_buttonBarOptionString; 676 } 677 678 /** 679 * Returns the calculated button bar items, including blocks and separators, considering the current widget configuration.<p> 680 * 681 * Use this method to get the calculated list of button bar items if {@link #getButtonBar(Map, String)} can not 682 * be used for a specific editor widget.<p> 683 * 684 * @return the calculated button bar items 685 */ 686 public List<String> getButtonBarShownItems() { 687 688 if (m_buttonBar == null) { 689 // first get individual button bar configuration 690 List<String> buttonBar = getButtonBarOption(); 691 if (buttonBar.isEmpty()) { 692 // no specific button bar defined, try to get global configuration first 693 if (m_globalButtonBarOption == null) { 694 // global configuration not yet parsed, check it now 695 String defaultConf = OpenCms.getXmlContentTypeManager().getWidgetDefaultConfiguration( 696 CmsHtmlWidget.class.getName()); 697 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(defaultConf) 698 && defaultConf.contains(OPTION_BUTTONBAR)) { 699 // found a global configuration containing a button bar definition, parse it 700 CmsHtmlWidgetOption option = new CmsHtmlWidgetOption(defaultConf); 701 // set global configuration in static member 702 m_globalButtonBarOption = option.getButtonBarOption(); 703 } else { 704 // no global configuration present, set static member to empty list 705 m_globalButtonBarOption = Collections.emptyList(); 706 } 707 } 708 if (m_globalButtonBarOption.isEmpty()) { 709 // no global button bar configuration found, use default button bar 710 buttonBar = BUTTONBAR_DEFAULT_LIST; 711 } else { 712 // found a global configuration containing a button bar definition, use it 713 buttonBar = m_globalButtonBarOption; 714 } 715 } 716 717 List<String> result = new ArrayList<String>(buttonBar.size()); 718 int lastSep = -1; 719 int lastBlock = -1; 720 boolean buttonInBlockAdded = false; 721 boolean buttonSinceSepAdded = false; 722 for (Iterator<String> i = buttonBar.iterator(); i.hasNext();) { 723 String barItem = i.next(); 724 if (BUTTONBAR_BLOCK_START.equals(barItem)) { 725 // start a block 726 if ((lastSep != -1) && (lastSep == (result.size() - 1))) { 727 // remove last separator before block start 728 result.remove(lastSep); 729 } 730 lastBlock = result.size(); 731 lastSep = -1; 732 buttonInBlockAdded = false; 733 buttonSinceSepAdded = false; 734 result.add(BUTTONBAR_BLOCK_START); 735 } else if (BUTTONBAR_BLOCK_END.equals(barItem)) { 736 // end a block 737 if (lastBlock != -1) { 738 // block has been started 739 if (lastSep == (result.size() - 1)) { 740 // remove last separator before block end 741 result.remove(lastSep); 742 } 743 //now check if there are items in it 744 if (buttonInBlockAdded) { 745 // block has items, add end 746 result.add(BUTTONBAR_BLOCK_END); 747 } else { 748 // block has no items, remove block start ite, 749 result.remove(lastBlock); 750 } 751 lastBlock = -1; 752 lastSep = -1; 753 buttonInBlockAdded = false; 754 buttonSinceSepAdded = false; 755 } 756 } else if (BUTTONBAR_SEPARATOR.equals(barItem)) { 757 // insert a separator depending on preconditions 758 if (buttonSinceSepAdded) { 759 lastSep = result.size(); 760 result.add(BUTTONBAR_SEPARATOR); 761 buttonSinceSepAdded = false; 762 } 763 } else { 764 // insert a button depending on preconditions 765 if (getHiddenButtons().contains(barItem)) { 766 // skip hidden buttons 767 continue; 768 } 769 if (OPTIONAL_BUTTONS_LIST.contains(barItem)) { 770 // check optional buttons 771 if (CmsAjaxImageGallery.GALLERYTYPE_NAME.equals(barItem)) { 772 // special handling of image button to keep compatibility 773 if (!(getAdditionalButtons().contains(barItem) 774 || getAdditionalButtons().contains(OPTION_IMAGE))) { 775 // skip image gallery as it is not defined as additional button 776 continue; 777 } 778 } else if (OPTION_UNLINK.equals(barItem)) { 779 // special handling of unlink button to show only if anchor, editor link or link button are active 780 if (!(getAdditionalButtons().contains(OPTION_LINK) 781 || getAdditionalButtons().contains(OPTION_EDITORLINK) 782 || getAdditionalButtons().contains(OPTION_ANCHOR))) { 783 // skip unlink button because no link buttons are defined as additional buttons 784 continue; 785 } 786 } else if (OPTION_STYLE.equals(barItem)) { 787 boolean showStyles = getAdditionalButtons().contains(barItem) 788 || (getStylesFormatPath() != null) 789 || (getStylesXmlPath() != null); 790 if (!showStyles) { 791 continue; 792 } 793 } else if (!getAdditionalButtons().contains(barItem)) { 794 // skip all optional buttons that are not defined 795 continue; 796 } 797 } 798 result.add(barItem); 799 buttonSinceSepAdded = true; 800 if (lastBlock != -1) { 801 buttonInBlockAdded = true; 802 } 803 } 804 } 805 m_buttonBar = result; 806 } 807 return m_buttonBar; 808 } 809 810 /** 811 * Returns the original configuration String that was used to initialize the HTML widget options.<p> 812 * 813 * @return the original configuration String 814 */ 815 public String getConfiguration() { 816 817 return m_configuration; 818 } 819 820 /** 821 * Returns the CSS style sheet VFS path to use in the widget area.<p> 822 * 823 * @return the CSS style sheet VFS path to use in the widget area 824 */ 825 public String getCssPath() { 826 827 return m_cssPath; 828 } 829 830 /** 831 * Gets the path of a JSON file containing options to be passed directly into TinyMCE. 832 * 833 * @return the path of a JSON with direct TinyMCE options 834 */ 835 public String getEditorConfigPath() { 836 837 return m_editorConfigPath; 838 } 839 840 /** 841 * Returns the widget editor height.<p> 842 * 843 * @return the widget editor height 844 */ 845 public String getEditorHeight() { 846 847 return m_editorHeight; 848 } 849 850 /** 851 * Gets the embedded gallery configurations.<p> 852 * 853 * @return the embedded gallery configurations 854 */ 855 public Map<String, String> getEmbeddedConfigurations() { 856 857 return m_embeddedConfigurations; 858 } 859 860 /** 861 * Returns the options for the format select box as String.<p> 862 * 863 * @return the options for the format select box as String 864 */ 865 public String getFormatSelectOptions() { 866 867 return m_formatSelectOptions; 868 } 869 870 /** 871 * Returns the buttons to hide as list with button names.<p> 872 * 873 * @return the buttons to hide as list with button names 874 */ 875 public List<String> getHiddenButtons() { 876 877 return m_hiddenButtons; 878 } 879 880 /** 881 * Returns the link default protocol to use when inserting/editing links via the link dialog. 882 * 883 * @return the link default protocol to use when inserting/editing links via the link dialog 884 */ 885 public String getLinkDefaultProtocol() { 886 887 return m_linkDefaultProtocol; 888 } 889 890 /** 891 * Returns the styles format VFS path to use in the widget area.<p> 892 * 893 * @return the styles XML format path to use in the widget area 894 */ 895 public String getStylesFormatPath() { 896 897 return m_stylesFormatPath; 898 } 899 900 /** 901 * Returns the styles XML VFS path to use in the widget area.<p> 902 * 903 * @return the styles XML VFS path to use in the widget area 904 */ 905 public String getStylesXmlPath() { 906 907 return m_stylesXmlPath; 908 } 909 910 /** 911 * Initializes the widget options from the given configuration String.<p> 912 * 913 * @param configuration the configuration String 914 */ 915 public void init(String configuration) { 916 917 // initialize the members 918 m_additionalButtons = new ArrayList<String>(OPTIONAL_BUTTONS_LIST.size()); 919 m_configuration = configuration; 920 m_editorHeight = EDITOR_DEFAULMAXTHEIGHT; 921 m_hiddenButtons = new ArrayList<String>(); 922 // initialize the widget options 923 parseOptions(configuration); 924 } 925 926 /** 927 * Returns true if scripts should be allowed in the source code editor.<p> 928 * 929 * @return true if scripts should be allowed in the source code editor 930 */ 931 public boolean isAllowScripts() { 932 933 return m_allowScripts; 934 } 935 936 /** 937 * Returns if the button with the given name should be additionally shown.<p> 938 * 939 * @param buttonName the button name to check 940 * 941 * @return <code>true</code> if the button with the given name should be additionally shown, otherwise <code>false</code> 942 */ 943 public boolean isButtonAdditional(String buttonName) { 944 945 return getAdditionalButtons().contains(buttonName); 946 } 947 948 /** 949 * Returns if the button with the given name should be hidden.<p> 950 * 951 * @param buttonName the button name to check 952 * 953 * @return <code>true</code> if the button with the given name should be hidden, otherwise <code>false</code> 954 */ 955 public boolean isButtonHidden(String buttonName) { 956 957 return getHiddenButtons().contains(buttonName); 958 } 959 960 /** 961 * Returns if the button with the given name is optional.<p> 962 * 963 * @param buttonName the button name to check 964 * 965 * @return <code>true</code> if the button with the given name is optional, otherwise <code>false</code> 966 */ 967 public boolean isButtonOptional(String buttonName) { 968 969 return OPTIONAL_BUTTONS_LIST.contains(buttonName); 970 } 971 972 /** 973 * Returns if the editor should be used in full page mode.<p> 974 * 975 * @return true if the editor should be used in full page mode, otherwise false 976 */ 977 public boolean isFullPage() { 978 979 return m_fullPage; 980 } 981 982 /** 983 * Return true if the content stylesheet's styles should be imported into the style selector.<p> 984 * 985 * @return true if the content stylesheet's styles should be imported into the style selector 986 */ 987 public boolean isImportCss() { 988 989 return m_importCss; 990 } 991 992 /** 993 * Sets the buttons to show additionally as list with button names.<p> 994 * 995 * @param buttons the buttons to show additionally as list with button names 996 */ 997 public void setAdditionalButtons(List<String> buttons) { 998 999 m_additionalButtons = buttons; 1000 } 1001 1002 /** 1003 * Sets the individual button bar configuration option.<p> 1004 * 1005 * @param buttonBar the individual button bar configuration option 1006 */ 1007 public void setButtonBarOption(List<String> buttonBar) { 1008 1009 m_buttonBarOption = buttonBar; 1010 } 1011 1012 /** 1013 * Sets the individual button bar configuration option string.<p> 1014 * 1015 * @param buttonBar the individual button bar configuration option string 1016 */ 1017 public void setButtonBarOptionString(String buttonBar) { 1018 1019 m_buttonBarOptionString = buttonBar; 1020 } 1021 1022 /** 1023 * Sets the CSS style sheet VFS path to use in the widget area.<p> 1024 * 1025 * @param cssPath the CSS style sheet VFS path to use in the widget area 1026 */ 1027 public void setCssPath(String cssPath) { 1028 1029 m_cssPath = cssPath; 1030 } 1031 1032 /** 1033 * Sets the path for a file containing JSON options to be passed directly into TinyMCE. 1034 * 1035 * @param optionJsonPath the path of a JSON file 1036 */ 1037 public void setEditorConfigPath(String optionJsonPath) { 1038 1039 m_editorConfigPath = optionJsonPath; 1040 } 1041 1042 /** 1043 * Sets the widget editor height.<p> 1044 * 1045 * @param editorHeight the widget editor height 1046 */ 1047 public void setEditorHeight(String editorHeight) { 1048 1049 m_editorHeight = editorHeight; 1050 } 1051 1052 /** 1053 * Sets the options for the format select box as String.<p> 1054 * 1055 * @param formatSelectOptions the options for the format select box as String 1056 */ 1057 public void setFormatSelectOptions(String formatSelectOptions) { 1058 1059 m_formatSelectOptions = formatSelectOptions; 1060 } 1061 1062 /** 1063 * Sets if the editor should be used in full page mode.<p> 1064 * 1065 * @param fullPage true if the editor should be used in full page mode, otherwise false 1066 */ 1067 public void setFullPage(boolean fullPage) { 1068 1069 m_fullPage = fullPage; 1070 } 1071 1072 /** 1073 * Sets the buttons to hide as list with button names.<p> 1074 * 1075 * @param buttons the buttons to hide as list with button names 1076 */ 1077 public void setHiddenButtons(List<String> buttons) { 1078 1079 m_hiddenButtons = buttons; 1080 } 1081 1082 /** 1083 * Set the link default protocol to use when inserting/editing links via the link dialog 1084 * 1085 * @param linkDefaultProtocol 1086 * the link default protocol to use when inserting/editing links via the link dialog 1087 */ 1088 public void setLinkDefaultProtocol(String linkDefaultProtocol) { 1089 1090 m_linkDefaultProtocol = linkDefaultProtocol; 1091 } 1092 1093 /** 1094 * Sets the styles format VFS path to use in the widget area.<p> 1095 * 1096 * @param stylesFormatPath the styles XML VFS path to use in the widget area 1097 */ 1098 public void setStylesFormatPath(String stylesFormatPath) { 1099 1100 m_stylesFormatPath = stylesFormatPath; 1101 } 1102 1103 /** 1104 * Sets the styles XML VFS path to use in the widget area.<p> 1105 * 1106 * @param stylesXmlPath the styles XML VFS path to use in the widget area 1107 */ 1108 public void setStylesXmlPath(String stylesXmlPath) { 1109 1110 m_stylesXmlPath = stylesXmlPath; 1111 } 1112 1113 /** 1114 * Returns true if the anchor dialog button should be available.<p> 1115 * 1116 * @return if the anchor dialog button should be available 1117 */ 1118 public boolean showAnchorDialog() { 1119 1120 return getAdditionalButtons().contains(OPTION_ANCHOR); 1121 } 1122 1123 /** 1124 * Returns true if the format selector should be available.<p> 1125 * 1126 * @return if the format selector should be available 1127 */ 1128 public boolean showFormatSelect() { 1129 1130 return getAdditionalButtons().contains(OPTION_FORMATSELECT); 1131 } 1132 1133 /** 1134 * Returns true if the specified gallery type dialog button is shown.<p> 1135 * 1136 * @param galleryType the gallery type to check 1137 * @return true if the specified gallery type dialog button is shown, otherwise false 1138 */ 1139 public boolean showGalleryDialog(String galleryType) { 1140 1141 return getAdditionalButtons().contains(galleryType); 1142 } 1143 1144 /** 1145 * Returns true if the image dialog button should be available.<p> 1146 * 1147 * @return if the image dialog button should be available 1148 */ 1149 public boolean showImageDialog() { 1150 1151 return getAdditionalButtons().contains(OPTION_IMAGE); 1152 } 1153 1154 /** 1155 * Returns true if the link dialog button should be available.<p> 1156 * 1157 * @return if the link dialog button should be available 1158 */ 1159 public boolean showLinkDialog() { 1160 1161 return getAdditionalButtons().contains(OPTION_LINK); 1162 } 1163 1164 /** 1165 * Returns true if the source code button should be available.<p> 1166 * 1167 * @return if the source code button should be available 1168 */ 1169 public boolean showSourceEditor() { 1170 1171 return getAdditionalButtons().contains(OPTION_SOURCE); 1172 } 1173 1174 /** 1175 * Returns true if the styles format selector should be available.<p> 1176 * 1177 * @return if the styles format selector should be available 1178 */ 1179 public boolean showStylesFormat() { 1180 1181 return CmsStringUtil.isNotEmpty(getStylesFormatPath()); 1182 } 1183 1184 /** 1185 * Returns true if the styles selector should be available.<p> 1186 * 1187 * @return if the styles selector should be available 1188 */ 1189 public boolean showStylesXml() { 1190 1191 return CmsStringUtil.isNotEmpty(getStylesXmlPath()); 1192 } 1193 1194 /** 1195 * Returns true if the table dialog button should be available.<p> 1196 * 1197 * @return if the table dialog button should be available 1198 */ 1199 public boolean showTableDialog() { 1200 1201 return getAdditionalButtons().contains(OPTION_TABLE); 1202 } 1203 1204 /** 1205 * Returns true if the widget editor should use a defined CSS style sheet.<p> 1206 * 1207 * @return if the widget editor should use a defined CSS style sheet 1208 */ 1209 public boolean useCss() { 1210 1211 return CmsStringUtil.isNotEmpty(getCssPath()); 1212 } 1213 1214 /** 1215 * Adds a button to the list of defined additional buttons.<p> 1216 * 1217 * @param buttonName the button name to add 1218 */ 1219 protected void addAdditionalButton(String buttonName) { 1220 1221 m_additionalButtons.add(buttonName); 1222 } 1223 1224 /** 1225 * Returns the real button name matched with the look up map.<p> 1226 * 1227 * If no value is found in the look up map, the button name is returned unchanged.<p> 1228 * 1229 * @param barItem the button bar item name to look up 1230 * @param buttonNamesLookUp the look up map containing the button names and/or separator name to use 1231 * @return the translated button name 1232 */ 1233 protected String getButtonName(String barItem, Map<String, String> buttonNamesLookUp) { 1234 1235 String result = barItem; 1236 if (buttonNamesLookUp != null) { 1237 String translatedName = buttonNamesLookUp.get(barItem); 1238 if (CmsStringUtil.isNotEmpty(translatedName)) { 1239 result = translatedName; 1240 } 1241 } 1242 return result; 1243 } 1244 1245 /** 1246 * Parses the given configuration String.<p> 1247 * 1248 * @param configuration the configuration String to parse 1249 */ 1250 protected void parseOptions(String configuration) { 1251 1252 if (CmsStringUtil.isNotEmpty(configuration)) { 1253 1254 CmsPair<String, Map<String, String>> simplifiedStringAndGalleryOptions = parseEmbeddedGalleryOptions( 1255 configuration); 1256 configuration = simplifiedStringAndGalleryOptions.getFirst(); 1257 m_embeddedConfigurations = simplifiedStringAndGalleryOptions.getSecond(); 1258 1259 List<String> options = CmsStringUtil.splitAsList(configuration, DELIMITER_OPTION, true); 1260 Iterator<String> i = options.iterator(); 1261 while (i.hasNext()) { 1262 String option = i.next(); 1263 // check which option is defined 1264 if (option.startsWith(OPTION_FORMATSELECT_OPTIONS)) { 1265 // the format select options 1266 option = option.substring(OPTION_FORMATSELECT_OPTIONS.length()); 1267 setFormatSelectOptions(option); 1268 } else if (option.startsWith(OPTION_HEIGHT)) { 1269 // the editor height 1270 option = option.substring(OPTION_HEIGHT.length()); 1271 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(option)) { 1272 setEditorHeight(option); 1273 } 1274 } else if (option.startsWith(OPTION_HIDEBUTTONS)) { 1275 // buttons to hide from the tool bar 1276 option = option.substring(OPTION_HIDEBUTTONS.length()); 1277 setHiddenButtons(CmsStringUtil.splitAsList(option, DELIMITER_VALUE, true)); 1278 } else if (option.startsWith(OPTION_CSS)) { 1279 // the editor CSS 1280 option = option.substring(OPTION_CSS.length()); 1281 setCssPath(option); 1282 } else if (option.startsWith(OPTION_STYLES)) { 1283 // the editor styles XML path 1284 option = option.substring(OPTION_STYLES.length()); 1285 setStylesXmlPath(option); 1286 } else if (option.startsWith(OPTION_STYLES_FORMAT)) { 1287 // the editor styles format path 1288 option = option.substring(OPTION_STYLES_FORMAT.length()); 1289 setStylesFormatPath(option); 1290 } else if (option.startsWith(OPTION_BUTTONBAR)) { 1291 // the button bar definition string 1292 option = option.substring(OPTION_BUTTONBAR.length()); 1293 setButtonBarOptionString(option); 1294 } else if (option.startsWith(OPTION_EDITORCONFIG)) { 1295 option = option.substring(OPTION_EDITORCONFIG.length()); 1296 setEditorConfigPath(option); 1297 } else if (option.startsWith(OPTION_IMPORTCSS)) { 1298 m_importCss = true; 1299 } else if (option.startsWith(OPTION_ALLOWSCRIPTS)) { 1300 m_allowScripts = true; 1301 } else if (option.startsWith(OPTION_LINKDEFAULTPROTOCOL)) { 1302 // the link default protocol 1303 option = option.substring(OPTION_LINKDEFAULTPROTOCOL.length()); 1304 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(option)) { 1305 setLinkDefaultProtocol(option); 1306 } 1307 } else { 1308 // check if option describes an additional button 1309 if (OPTIONAL_BUTTONS_LIST.contains(option)) { 1310 addAdditionalButton(option); 1311 } 1312 } 1313 } 1314 } 1315 } 1316}