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