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