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