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