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.workplace;
029
030import org.opencms.i18n.CmsEncoder;
031import org.opencms.jsp.CmsJspActionElement;
032import org.opencms.main.CmsLog;
033import org.opencms.main.I_CmsThrowable;
034import org.opencms.main.OpenCms;
035import org.opencms.util.CmsRequestUtil;
036import org.opencms.util.CmsStringUtil;
037import org.opencms.widgets.A_CmsWidget;
038import org.opencms.widgets.CmsDisplayWidget;
039import org.opencms.widgets.I_CmsWidget;
040import org.opencms.widgets.I_CmsWidgetDialog;
041
042import java.io.IOException;
043import java.util.ArrayList;
044import java.util.Arrays;
045import java.util.HashMap;
046import java.util.HashSet;
047import java.util.Hashtable;
048import java.util.Iterator;
049import java.util.List;
050import java.util.Map;
051import java.util.Set;
052
053import javax.servlet.ServletException;
054import javax.servlet.http.HttpServletRequest;
055import javax.servlet.http.HttpServletResponse;
056import javax.servlet.jsp.JspException;
057import javax.servlet.jsp.JspWriter;
058import javax.servlet.jsp.PageContext;
059
060import org.apache.commons.logging.Log;
061
062/**
063 * Base class for dialogs that use the OpenCms widgets without XML content.<p>
064 *
065 * @since 6.0.0
066 */
067public abstract class CmsWidgetDialog extends CmsDialog implements I_CmsWidgetDialog {
068
069    /** Action for optional element creation. */
070    public static final int ACTION_ELEMENT_ADD = 152;
071
072    /** Action for optional element removal. */
073    public static final int ACTION_ELEMENT_REMOVE = 153;
074
075    /** Value for the action: error in the form validation. */
076    public static final int ACTION_ERROR = 303;
077
078    /** Value for the action: save the dialog. */
079    public static final int ACTION_SAVE = 300;
080
081    /** Request parameter value for the action: save the dialog. */
082    public static final String DIALOG_SAVE = "save";
083
084    /** Indicates an optional element should be created. */
085    public static final String EDITOR_ACTION_ELEMENT_ADD = "addelement";
086
087    /** Indicates an optional element should be removed. */
088    public static final String EDITOR_ACTION_ELEMENT_REMOVE = "removeelement";
089
090    /** Prefix for "hidden" parameters, required since these must be unescaped later. */
091    public static final String HIDDEN_PARAM_PREFIX = "hidden.";
092
093    /** The log object for this class. */
094    private static final Log LOG = CmsLog.getLog(CmsWidgetDialog.class);
095
096    /** The errors thrown by commit actions. */
097    protected List<Throwable> m_commitErrors;
098
099    /** The object edited with this widget dialog. */
100    protected Object m_dialogObject;
101
102    /** The allowed pages for this dialog in a List. */
103    protected List<String> m_pages;
104
105    /** Controls which page is currently displayed in the dialog. */
106    protected String m_paramPage;
107
108    /** The validation errors for the input form. */
109    protected List<Throwable> m_validationErrorList;
110
111    /** Contains all parameter value of this dialog. */
112    protected Map<String, List<CmsWidgetDialogParameter>> m_widgetParamValues;
113
114    /** The list of widgets used on the dialog. */
115    protected List<CmsWidgetDialogParameter> m_widgets;
116
117    /** The set of help message ids that have already been used. */
118    private Set<String> m_helpMessageIds;
119
120    /**
121     * Parameter stores the index of the element to add or remove.<p>
122     *
123     * This must not be <code>null</code>, because it must be available
124     * when calling <code>{@link org.opencms.workplace.CmsWorkplace#paramsAsHidden()}</code>.<p>
125     */
126    private String m_paramElementIndex = "0";
127
128    /**
129     * Parameter stores the name of the element to add or remove.<p>
130     *
131     * This must not be <code>null</code>, because it must be available
132     * when calling <code>{@link org.opencms.workplace.CmsWorkplace#paramsAsHidden()}</code>.<p>
133     */
134    private String m_paramElementName = "undefined";
135
136    /** Optional localized key prefix identificator. */
137    private String m_prefix;
138
139    /**
140     * Public constructor with JSP action element.<p>
141     *
142     * @param jsp an initialized JSP action element
143     */
144    public CmsWidgetDialog(CmsJspActionElement jsp) {
145
146        super(jsp);
147    }
148
149    /**
150     * Public constructor with JSP variables.<p>
151     *
152     * @param context the JSP page context
153     * @param req the JSP request
154     * @param res the JSP response
155     */
156    public CmsWidgetDialog(PageContext context, HttpServletRequest req, HttpServletResponse res) {
157
158        this(new CmsJspActionElement(context, req, res));
159    }
160
161    /**
162     * Deletes the edited dialog object from the session.<p>
163     */
164    public void actionCancel() {
165
166        clearDialogObject();
167    }
168
169    /**
170     * Commits the edited object after pressing the "OK" button.<p>
171     *
172     * @throws IOException in case of errors forwarding to the required result page
173     * @throws ServletException in case of errors forwarding to the required result page
174     */
175    public abstract void actionCommit() throws IOException, ServletException;
176
177    /**
178     * Adds or removes an optional element.<p>
179     *
180     * Depends on the value stored in the <code>{@link CmsDialog#getAction()}</code> method.<p>
181     */
182    public void actionToggleElement() {
183
184        // get the necessary parameters to add/remove the element
185        int index = 0;
186        try {
187            index = Integer.parseInt(getParamElementIndex());
188        } catch (Exception e) {
189            // ignore, should not happen
190        }
191        String name = getParamElementName();
192        // get the base parameter definition
193        CmsWidgetDialogParameter base = getParameterDefinition(name);
194        if (base != null) {
195            // the requested parameter is valid for this dialog
196            List<CmsWidgetDialogParameter> params = getParameters().get(name);
197            if (getAction() == ACTION_ELEMENT_REMOVE) {
198                // remove the value
199                params.remove(index);
200            } else {
201                List<CmsWidgetDialogParameter> sequence = getParameters().get(base.getName());
202                if (sequence.size() > 0) {
203                    // add the new value after the clicked element
204                    index = index + 1;
205                }
206                CmsWidgetDialogParameter newParam = new CmsWidgetDialogParameter(base, index);
207                params.add(index, newParam);
208            }
209            // reset all index value in the parameter list
210            for (int i = 0; i < params.size(); i++) {
211                CmsWidgetDialogParameter param = params.get(i);
212                param.setindex(i);
213            }
214        }
215    }
216
217    /**
218     * Returns the html for a button to add an optional element.<p>
219     *
220     * @param elementName name of the element
221     * @param insertAfter the index of the element after which the new element should be created
222     * @param enabled if true, the button to add an element is shown, otherwise a spacer is returned
223     * @return the html for a button to add an optional element
224     */
225    public String buildAddElement(String elementName, int insertAfter, boolean enabled) {
226
227        if (enabled) {
228            StringBuffer href = new StringBuffer(4);
229            href.append("javascript:addElement('");
230            href.append(elementName);
231            href.append("', ");
232            href.append(insertAfter);
233            href.append(");");
234            return button(href.toString(), null, "new.png", Messages.GUI_DIALOG_BUTTON_ADDNEW_0, 0);
235        } else {
236            return "";
237        }
238    }
239
240    /**
241     * Builds the HTML for the dialog form.<p>
242     *
243     * @return the HTML for the dialog form
244     */
245    public String buildDialogForm() {
246
247        // create the dialog HTML
248        return createDialogHtml(getParamPage());
249    }
250
251    /**
252     * Returns the html for a button to remove an optional element.<p>
253     *
254     * @param elementName name of the element
255     * @param index the element index of the element to remove
256     * @param enabled if true, the button to remove an element is shown, otherwise a spacer is returned
257     * @return the html for a button to remove an optional element
258     */
259    public String buildRemoveElement(String elementName, int index, boolean enabled) {
260
261        if (enabled) {
262            StringBuffer href = new StringBuffer(4);
263            href.append("javascript:removeElement('");
264            href.append(elementName);
265            href.append("', ");
266            href.append(index);
267            href.append(");");
268            return button(href.toString(), null, "deletecontent.png", Messages.GUI_DIALOG_BUTTON_DELETE_0, 0);
269        } else {
270            return "";
271        }
272    }
273
274    /**
275     * Clears the "dialog object" for this widget dialog by removing it from the current users session.<p>
276     */
277    public void clearDialogObject() {
278
279        setDialogObject(null);
280    }
281
282    /**
283     * Builds the end HTML for a block with 3D border in the dialog content area.<p>
284     *
285     * @return 3D block start / end segment
286     */
287    @Override
288    public String dialogBlockEnd() {
289
290        StringBuffer result = new StringBuffer(8);
291        result.append(super.dialogBlockEnd());
292        result.append(dialogSpacer());
293        result.append("</td></tr>\n");
294        return result.toString();
295    }
296
297    /**
298     * Builds the start HTML for a block with 3D border and optional subheadline in the dialog content area.<p>
299     *
300     * @param headline the headline String for the block
301     * @return 3D block start / end segment
302     */
303    @Override
304    public String dialogBlockStart(String headline) {
305
306        StringBuffer result = new StringBuffer(8);
307        result.append("<tr><td colspan=\"5\">\n");
308        result.append(super.dialogBlockStart(headline));
309        return result.toString();
310    }
311
312    /**
313     * Creates the HTML for the buttons on the dialog.<p>
314     *
315     * @return the HTML for the buttons on the dialog.<p>
316     */
317    public String dialogButtonsCustom() {
318
319        if (getPages().size() > 1) {
320            // this is a multi page dialog, create buttons according to current page
321            int pageIndex = getPages().indexOf(getParamPage());
322            if (pageIndex == (getPages().size() - 1)) {
323                // this is the last dialog page
324                return dialogButtons(new int[] {BUTTON_OK, BUTTON_BACK, BUTTON_CANCEL}, new String[3]);
325            } else if (pageIndex > 0) {
326                // this is a dialog page between first and last page
327                return dialogButtons(new int[] {BUTTON_BACK, BUTTON_CONTINUE, BUTTON_CANCEL}, new String[3]);
328            } else {
329                // this is the first dialog page
330                return dialogButtons(new int[] {BUTTON_CONTINUE, BUTTON_CANCEL}, new String[2]);
331            }
332        }
333        boolean onlyDisplay = true;
334        Iterator<CmsWidgetDialogParameter> it = getWidgets().iterator();
335        while (it.hasNext()) {
336            CmsWidgetDialogParameter wdp = it.next();
337            if (!(wdp.getWidget() instanceof CmsDisplayWidget)) {
338                onlyDisplay = false;
339                break;
340            }
341        }
342        if (!onlyDisplay) {
343            // this is a single page dialog, create common buttons
344            return dialogButtons(new int[] {BUTTON_OK, BUTTON_CANCEL}, new String[2]);
345        }
346        // this is a display only dialog
347        return "";
348    }
349
350    /**
351     * Performs the dialog actions depending on the initialized action and displays the dialog form.<p>
352     *
353     * @throws JspException if dialog actions fail
354     * @throws IOException if writing to the JSP out fails, or in case of errors forwarding to the required result page
355     * @throws ServletException in case of errors forwarding to the required result page
356     */
357    public void displayDialog() throws JspException, IOException, ServletException {
358
359        displayDialog(false);
360    }
361
362    /**
363     * Performs the dialog actions depending on the initialized action and displays the dialog form if needed.<p>
364     *
365     * @param writeLater if <code>true</code> no output is written,
366     *                   you have to call manually the <code>{@link #defaultActionHtml()}</code> method.
367     *
368     * @throws JspException if dialog actions fail
369     * @throws IOException if writing to the JSP out fails, or in case of errors forwarding to the required result page
370     * @throws ServletException in case of errors forwarding to the required result page
371     */
372    public void displayDialog(boolean writeLater) throws JspException, IOException, ServletException {
373
374        if (isForwarded()) {
375            return;
376        }
377        switch (getAction()) {
378
379            case ACTION_CANCEL:
380                // ACTION: cancel button pressed
381                actionCancel();
382                actionCloseDialog();
383                break;
384
385            case ACTION_ERROR:
386                // ACTION: an error occurred (display nothing)
387                break;
388
389            case ACTION_SAVE:
390                // ACTION: save edited values
391                setParamAction(DIALOG_OK);
392                actionCommit();
393                if (closeDialogOnCommit()) {
394                    setAction(ACTION_CANCEL);
395                    actionCloseDialog();
396                    break;
397                }
398                setAction(ACTION_DEFAULT);
399
400                //$FALL-THROUGH$
401            case ACTION_DEFAULT:
402            default:
403                // ACTION: show dialog (default)
404                if (!writeLater) {
405                    writeDialog();
406                }
407        }
408    }
409
410    /**
411     * @see org.opencms.widgets.I_CmsWidgetDialog#getButtonStyle()
412     */
413    public int getButtonStyle() {
414
415        return getSettings().getUserSettings().getEditorButtonStyle();
416    }
417
418    /**
419     * Returns the errors that are thrown by save actions or form generation.<p>
420     *
421     * @return the errors that are thrown by save actions or form generation
422     */
423    public List<Throwable> getCommitErrors() {
424
425        return m_commitErrors;
426    }
427
428    /**
429     * Returns the dialog object for this widget dialog, or <code>null</code>
430     * if no dialog object has been set.<p>
431     *
432     * @return the dialog object for this widget dialog, or <code>null</code>
433     */
434    public Object getDialogObject() {
435
436        if (m_dialogObject == null) {
437            m_dialogObject = getDialogObjectMap().get(getClass().getName());
438        }
439        return m_dialogObject;
440    }
441
442    /**
443     * @see org.opencms.widgets.I_CmsWidgetDialog#getHelpMessageIds()
444     */
445    public Set<String> getHelpMessageIds() {
446
447        if (m_helpMessageIds == null) {
448            m_helpMessageIds = new HashSet<String>();
449        }
450        return m_helpMessageIds;
451    }
452
453    /**
454     * Returns the index of the element to add or remove.<p>
455     *
456     * @return the index of the element to add or remove
457     */
458    public String getParamElementIndex() {
459
460        return m_paramElementIndex;
461    }
462
463    /**
464     * Returns the name of the element to add or remove.<p>
465     *
466     * @return the name of the element to add or remove
467     */
468    public String getParamElementName() {
469
470        return m_paramElementName;
471    }
472
473    /**
474     * Returns the page parameter.<p>
475     *
476     * @return the page parameter
477     */
478    public String getParamPage() {
479
480        return m_paramPage;
481    }
482
483    /**
484     * Returns the value of the widget parameter with the given name, or <code>null</code>
485     * if no such widget parameter is available.<p>
486     *
487     * @param name the widget parameter name to get the value for
488     *
489     * @return the value of the widget parameter with the given name
490     */
491    public String getParamValue(String name) {
492
493        return getParamValue(name, 0);
494    }
495
496    /**
497     * Returns the value of the widget parameter with the given name and index, or <code>null</code>
498     * if no such widget parameter is available.<p>
499     *
500     * @param name the widget parameter name to get the value for
501     * @param index the widget parameter index
502     *
503     * @return the value of the widget parameter with the given name and index
504     */
505    public String getParamValue(String name, int index) {
506
507        List<CmsWidgetDialogParameter> params = m_widgetParamValues.get(name);
508        if (params != null) {
509            if ((index >= 0) && (index < params.size())) {
510                CmsWidgetDialogParameter param = params.get(index);
511                if (param.getId().equals(CmsWidgetDialogParameter.createId(name, index))) {
512                    return param.getStringValue(getCms());
513                }
514            }
515        }
516
517        return null;
518    }
519
520    /**
521     * @see org.opencms.widgets.I_CmsWidgetDialog#getUserAgent()
522     */
523    public String getUserAgent() {
524
525        return getJsp().getRequest().getHeader(CmsRequestUtil.HEADER_USER_AGENT);
526    }
527
528    /**
529     * Generates the HTML for the end of the widget dialog.<p>
530     *
531     * This HTML includes additional components, for example the &lt;div&gt;
532     * tags containing the help texts.<p>
533     *
534     * @return the HTML for the end of the widget dialog
535     */
536    public String getWidgetHtmlEnd() {
537
538        StringBuffer result = new StringBuffer(32);
539        // iterate over unique widgets from collector
540        Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator();
541        while (i.hasNext()) {
542            CmsWidgetDialogParameter param = i.next();
543            result.append(param.getWidget().getDialogHtmlEnd(getCms(), this, param));
544        }
545        return result.toString();
546    }
547
548    /**
549     * Generates the HTML include tags for external JavaScripts files of the used widgets.<p>
550     *
551     * @return the HTML include tags for external JavaScripts files of the used widgets
552     *
553     * @throws JspException if an error occurs during JavaScript generation
554     */
555    public String getWidgetIncludes() throws JspException {
556
557        StringBuffer result = new StringBuffer(32);
558        try {
559            // iterate over unique widgets from collector
560            Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator();
561            Set<I_CmsWidget> set = new HashSet<I_CmsWidget>();
562            while (i.hasNext()) {
563                I_CmsWidget widget = i.next().getWidget();
564                if (!set.contains(widget)) {
565                    result.append(widget.getDialogIncludes(getCms(), this));
566                    result.append('\n');
567                    set.add(widget);
568                }
569            }
570        } catch (Throwable e) {
571
572            includeErrorpage(this, e);
573        }
574        return result.toString();
575    }
576
577    /**
578     * Generates the JavaScript init calls for the used widgets.<p>
579     *
580     * @return the JavaScript init calls for the used widgets
581     *
582     * @throws JspException the JavaScript init calls for the used widgets
583     */
584    public String getWidgetInitCalls() throws JspException {
585
586        StringBuffer result = new StringBuffer(32);
587        try {
588            // iterate over unique widgets from collector
589            Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator();
590            Set<I_CmsWidget> set = new HashSet<I_CmsWidget>();
591            while (i.hasNext()) {
592                I_CmsWidget widget = i.next().getWidget();
593                if (!set.contains(widget)) {
594                    result.append(widget.getDialogInitCall(getCms(), this));
595                    set.add(widget);
596                }
597            }
598        } catch (Throwable e) {
599            includeErrorpage(this, e);
600        }
601        return result.toString();
602    }
603
604    /**
605     * Generates the JavaScript initialization methods for the used widgets.<p>
606     *
607     * @return the JavaScript initialization methods for the used widgets
608     *
609     * @throws JspException if an error occurs during JavaScript generation
610     */
611    public String getWidgetInitMethods() throws JspException {
612
613        StringBuffer result = new StringBuffer(32);
614        try {
615            // iterate over unique widgets from collector
616            Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator();
617            Set<I_CmsWidget> set = new HashSet<I_CmsWidget>();
618            while (i.hasNext()) {
619                I_CmsWidget widget = i.next().getWidget();
620                if (!set.contains(widget)) {
621                    result.append(widget.getDialogInitMethod(getCms(), this));
622                    set.add(widget);
623                }
624            }
625        } catch (Throwable e) {
626            includeErrorpage(this, e);
627        }
628        return result.toString();
629    }
630
631    /**
632     * @see org.opencms.workplace.CmsWorkplace#paramsAsHidden()
633     */
634    @Override
635    public String paramsAsHidden() {
636
637        if (getAction() != ACTION_ERROR) {
638            return super.paramsAsHidden();
639        }
640        // on an error page, also output the widget parameters
641        StringBuffer result = new StringBuffer();
642        result.append(super.paramsAsHidden());
643        result.append('\n');
644        result.append(widgetParamsAsHidden());
645        return result.toString();
646    }
647
648    /**
649     * Stores the given object as "dialog object" for this widget dialog in the current users session.<p>
650     *
651     * @param dialogObject the object to store
652     */
653    public void setDialogObject(Object dialogObject) {
654
655        m_dialogObject = dialogObject;
656        if (dialogObject == null) {
657            // null object: remove the entry from the map
658            getDialogObjectMap().remove(getClass().getName());
659        } else {
660            getDialogObjectMap().put(getClass().getName(), dialogObject);
661        }
662    }
663
664    /**
665     * Sets the index of the element to add or remove.<p>
666     *
667     * @param elementIndex the index of the element to add or remove
668     */
669    public void setParamElementIndex(String elementIndex) {
670
671        m_paramElementIndex = elementIndex;
672    }
673
674    /**
675     * Sets the name of the element to add or remove.<p>
676     *
677     * @param elementName the name of the element to add or remove
678     */
679    public void setParamElementName(String elementName) {
680
681        m_paramElementName = elementName;
682    }
683
684    /**
685     * Sets the page parameter.<p>
686     *
687     * @param paramPage the page parameter to set
688     */
689    public void setParamPage(String paramPage) {
690
691        m_paramPage = paramPage;
692    }
693
694    /**
695     * Returns the values of all widget parameters of this dialog as HTML hidden fields.<p>
696     *
697     * @return the values of all widget parameters of this dialog as HTML hidden fields
698     *
699     * @see org.opencms.workplace.CmsWorkplace#paramsAsHidden()
700     */
701    public String widgetParamsAsHidden() {
702
703        return widgetParamsAsHidden(null);
704    }
705
706    /**
707     * Returns the values of all widget parameters of this dialog as HTML hidden fields,
708     * excluding the widget values that are on the given dialog page.<p>
709     *
710     * This can be used to create multi-page dialogs where the values are passed from
711     * one page to another before everything is submitted. If a widget A is used on page X,
712     * there should be no "hidden" HTML field for A since otherwise A would have 2 values when
713     * submitting the dialog page: The one from the widget itself and the one from the hidden
714     * field. This may lead to undefined results when processing the submitted values.<p>
715     *
716     * @param excludeDialogPage the dialog page to exclude the values for
717     *
718     * @return the values of all widget parameters of this dialog as HTML hidden fields,
719     *      excluding the widget values that are on the given dialog page
720     *
721     * @see org.opencms.workplace.CmsWorkplace#paramsAsHidden()
722     */
723    public String widgetParamsAsHidden(String excludeDialogPage) {
724
725        StringBuffer result = new StringBuffer();
726        Iterator<String> i = m_widgetParamValues.keySet().iterator();
727        while (i.hasNext()) {
728            List<CmsWidgetDialogParameter> params = m_widgetParamValues.get(i.next());
729            Iterator<CmsWidgetDialogParameter> j = params.iterator();
730            while (j.hasNext()) {
731                CmsWidgetDialogParameter param = j.next();
732                String value = param.getStringValue(getCms());
733                if (CmsStringUtil.isNotEmpty(value)
734                    && ((excludeDialogPage == null) || (!param.getDialogPage().equals(excludeDialogPage)))) {
735                    result.append("<input type=\"hidden\" name=\"");
736                    result.append(HIDDEN_PARAM_PREFIX);
737                    result.append(param.getId());
738                    result.append("\" value=\"");
739                    String encoded = CmsEncoder.encode(value, getCms().getRequestContext().getEncoding());
740                    result.append(encoded);
741                    result.append("\">\n");
742                }
743            }
744        }
745        return result.toString();
746    }
747
748    /**
749     * Writes the dialog html code, only if the <code>{@link #ACTION_DEFAULT}</code> is set.<p>
750     *
751     * @throws JspException if dialog actions fail
752     * @throws IOException if writing to the JSP out fails, or in case of errors forwarding to the required result page
753     */
754    public void writeDialog() throws IOException, JspException {
755
756        if (isForwarded()) {
757            return;
758        }
759        switch (getAction()) {
760            case ACTION_CANCEL:
761            case ACTION_ERROR:
762            case ACTION_SAVE:
763                break;
764
765            case ACTION_DEFAULT:
766            default:
767                // ACTION: show dialog (default)
768                setParamAction(DIALOG_SAVE);
769                JspWriter out = getJsp().getJspContext().getOut();
770                out.print(defaultActionHtml());
771        }
772    }
773
774    /**
775     * Adds the given error to the list of errors that are thrown by save actions or form generation.<p>
776     *
777     * If the error list has not been initialized yet, this is done automatically.<p>
778     *
779     * @param error the errors to add
780     */
781    protected void addCommitError(Exception error) {
782
783        if (m_commitErrors == null) {
784            m_commitErrors = new ArrayList<Throwable>();
785        }
786        m_commitErrors.add(error);
787    }
788
789    /**
790     * Adds a new widget parameter definition to the list of all widgets of this dialog.<p>
791     *
792     * @param param the widget parameter definition to add
793     */
794    protected void addWidget(CmsWidgetDialogParameter param) {
795
796        if (m_widgets == null) {
797            m_widgets = new ArrayList<CmsWidgetDialogParameter>();
798        }
799        param.setKeyPrefix(m_prefix);
800        m_widgets.add(param);
801    }
802
803    /**
804     * Returns <code>true</code> if the dialog should be closed after the values have been committed.<p>
805     *
806     * The default implementation returns <code>true</code> in case there are no
807     * commit errors.<p>
808     *
809     * @return <code>true</code> if the dialog should be closed after the values have been committed
810     */
811    protected boolean closeDialogOnCommit() {
812
813        return !hasCommitErrors();
814    }
815
816    /**
817     * Commits all values on the dialog.<p>
818     *
819     * @return a List of all Exceptions that occurred when comitting the dialog.<p>
820     */
821    protected List<Throwable> commitWidgetValues() {
822
823        return commitWidgetValues(null);
824    }
825
826    /**
827     * Commits all values on the given dialog page.<p>
828     *
829     * @param dialogPage the dialog (page) to commit
830     *
831     * @return a List of all Exceptions that occurred when committing the dialog page.<p>
832     */
833    protected List<Throwable> commitWidgetValues(String dialogPage) {
834
835        List<Throwable> result = new ArrayList<Throwable>();
836        Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator();
837        while (i.hasNext()) {
838            // check for all widget parameters
839            CmsWidgetDialogParameter base = i.next();
840            if ((dialogPage == null) || (base.getDialogPage() == null) || dialogPage.equals(base.getDialogPage())) {
841                // the parameter is located on the requested dialog
842                base.prepareCommit();
843                List<CmsWidgetDialogParameter> params = m_widgetParamValues.get(base.getName());
844                Iterator<CmsWidgetDialogParameter> j = params.iterator();
845                while (j.hasNext()) {
846                    CmsWidgetDialogParameter param = j.next();
847                    try {
848                        param.commitValue(this);
849                    } catch (Exception e) {
850                        result.add(e);
851                    }
852                }
853            }
854        }
855        setValidationErrorList(result);
856        return result;
857    }
858
859    /**
860     * Creates the dialog HTML for all defined widgets of this dialog.<p>
861     *
862     * @return the dialog HTML for all defined widgets of this dialog
863     */
864    protected String createDialogHtml() {
865
866        return createDialogHtml(null);
867    }
868
869    /**
870     * Creates the dialog HTML for all defined widgets of the named dialog (page).<p>
871     *
872     * To get a more complex layout variation, you have to overwrite this method in your dialog class.<p>
873     *
874     * @param dialog the dialog (page) to get the HTML for
875     * @return the dialog HTML for all defined widgets of the named dialog (page)
876     */
877    protected String createDialogHtml(String dialog) {
878
879        StringBuffer result = new StringBuffer(1024);
880
881        // create table
882        result.append(createWidgetTableStart());
883
884        // show error header once if there were validation errors
885        result.append(createWidgetErrorHeader());
886
887        Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator();
888        // iterate the type sequence
889        while (i.hasNext()) {
890            // get the current widget base definition
891            CmsWidgetDialogParameter base = i.next();
892            // check if the element is on the requested dialog page
893            if ((dialog == null) || dialog.equals(base.getDialogPage())) {
894                // add the HTML for the dialog element
895                result.append(createDialogRowHtml(base));
896            }
897        }
898        // close table
899        result.append(createWidgetTableEnd());
900
901        return result.toString();
902    }
903
904    /**
905     * Creates the dialog HTML for all occurrences of one widget parameter.<p>
906     *
907     * @param base the widget parameter base
908     * @return the dialog HTML for one widget parameter
909     */
910    protected String createDialogRowHtml(CmsWidgetDialogParameter base) {
911
912        StringBuffer result = new StringBuffer(256);
913
914        List<CmsWidgetDialogParameter> sequence = getParameters().get(base.getName());
915        int count = sequence.size();
916
917        // check if value is optional or multiple
918        boolean addValue = false;
919        if (count < base.getMaxOccurs()) {
920            addValue = true;
921        }
922        boolean removeValue = false;
923        if (count > base.getMinOccurs()) {
924            removeValue = true;
925        }
926
927        // check if value is present
928        boolean disabledElement = false;
929        if (count < 1) {
930            // no parameter with the value present, but also not optional: use base as parameter
931            sequence = new ArrayList<CmsWidgetDialogParameter>();
932            sequence.add(base);
933            count = 1;
934            if (base.getMinOccurs() == 0) {
935                disabledElement = true;
936            }
937        }
938
939        // loop through multiple elements
940        for (int j = 0; j < count; j++) {
941
942            // get the parameter and the widget
943            CmsWidgetDialogParameter p = sequence.get(j);
944            I_CmsWidget widget = p.getWidget();
945
946            // check for an error in this row
947            if (p.hasError()) {
948                // show error message
949                result.append("<tr><td></td><td><img src=\"");
950                result.append(getSkinUri()).append("editors/xmlcontent/");
951                result.append("error.png");
952                result.append("\" border=\"0\" alt=\"\"></td><td class=\"xmlTdError\">");
953                Throwable t = p.getError();
954                while (t != null) {
955                    if (t instanceof I_CmsThrowable) {
956                        result.append(CmsEncoder.escapeXml(((I_CmsThrowable)t).getLocalizedMessage(getLocale())));
957                    } else {
958                        result.append(CmsEncoder.escapeXml(t.getLocalizedMessage()));
959                    }
960                    t = t.getCause();
961                    if (t != null) {
962                        result.append("<br>");
963                    }
964                }
965                result.append("</td><td colspan=\"2\"></td></tr>\n");
966            }
967
968            // create label and help bubble cells
969            result.append("<tr>");
970            result.append("<td class=\"xmlLabel");
971            if (disabledElement) {
972                // element is disabled, mark it with css
973                result.append("Disabled");
974            }
975
976            result.append("\">");
977            result.append(keyDefault(A_CmsWidget.getLabelKey(p), p.getName()));
978            if (count > 1) {
979                result.append(" [").append(p.getIndex() + 1).append("]");
980            }
981            result.append(": </td>");
982            if (p.getIndex() == 0) {
983                // show help bubble only on first element of each content definition
984                result.append(p.getWidget().getHelpBubble(getCms(), this, p));
985            } else {
986                // create empty cell for all following elements
987                result.append(dialogHorizontalSpacer(16));
988            }
989
990            // append individual widget html cell if element is enabled
991            if (!disabledElement) {
992                // this is a simple type, display widget
993                result.append(widget.getDialogWidget(getCms(), this, p));
994            } else {
995                // disabled element, show message for optional element
996                result.append("<td class=\"xmlTdDisabled maxwidth\">");
997                result.append(key(Messages.GUI_EDITOR_WIDGET_OPTIONALELEMENT_0));
998                result.append("</td>");
999            }
1000
1001            // append add and remove element buttons if required
1002            result.append(dialogHorizontalSpacer(5));
1003            result.append("<td>");
1004            if (addValue || removeValue) {
1005                result.append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>");
1006
1007                if (!addValue) {
1008                    result.append(dialogHorizontalSpacer(25));
1009                } else {
1010                    result.append(
1011                        "<td><table class=\"editorbuttonbackground\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>");
1012                    result.append(buildAddElement(base.getName(), p.getIndex(), addValue));
1013                }
1014
1015                if (removeValue) {
1016                    if (!addValue) {
1017                        result.append(
1018                            "<td><table class=\"editorbuttonbackground\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>");
1019                    }
1020                    result.append(buildRemoveElement(base.getName(), p.getIndex(), removeValue));
1021                }
1022
1023                result.append("</tr></table></td>");
1024                result.append("</tr></table>");
1025            }
1026            result.append("</td>");
1027            // close row
1028            result.append("</tr>\n");
1029        }
1030
1031        return result.toString();
1032    }
1033
1034    /**
1035     * Creates the dialog widget rows HTML for the specified widget indices.<p>
1036     *
1037     * @param startIndex the widget index to start with
1038     * @param endIndex the widget index to stop at
1039     *
1040     * @return the dialog widget rows HTML for the specified widget indices
1041     */
1042    protected String createDialogRowsHtml(int startIndex, int endIndex) {
1043
1044        StringBuffer result = new StringBuffer((endIndex - startIndex) * 8);
1045        for (int i = startIndex; i <= endIndex; i++) {
1046            CmsWidgetDialogParameter base = getWidgets().get(i);
1047            result.append(createDialogRowHtml(base));
1048        }
1049        return result.toString();
1050    }
1051
1052    /**
1053     * Creates the complete widget dialog end block HTML that finishes a widget block.<p>
1054     *
1055     * @return the complete widget dialog end block HTML that finishes a widget block
1056     */
1057    protected String createWidgetBlockEnd() {
1058
1059        StringBuffer result = new StringBuffer(8);
1060        result.append(createWidgetTableEnd());
1061        result.append(dialogBlockEnd());
1062        return result.toString();
1063    }
1064
1065    /**
1066     * Create the complete widget dialog start block HTML that begins a widget block with optional headline.<p>
1067     *
1068     * @param headline the headline String for the block
1069     *
1070     * @return the complete widget dialog start block HTML that begins a widget block with optional headline
1071     */
1072    protected String createWidgetBlockStart(String headline) {
1073
1074        StringBuffer result = new StringBuffer(16);
1075        result.append(dialogBlockStart(headline));
1076        result.append(createWidgetTableStart());
1077        return result.toString();
1078    }
1079
1080    /**
1081     * Creates the HTML for the error message if validation errors were found.<p>
1082     *
1083     * @return the HTML for the error message if validation errors were found
1084     */
1085    protected String createWidgetErrorHeader() {
1086
1087        StringBuffer result = new StringBuffer(8);
1088        if (hasValidationErrors() || hasCommitErrors()) {
1089            result.append("<tr><td colspan=\"5\">&nbsp;</td></tr>\n");
1090            result.append("<tr><td colspan=\"2\">&nbsp;</td>");
1091            result.append("<td class=\"xmlTdErrorHeader\">");
1092            result.append(key(Messages.GUI_EDITOR_WIDGET_VALIDATION_ERROR_TITLE_0));
1093            result.append("</td><td colspan=\"2\">&nbsp;");
1094            result.append("</td></tr>\n");
1095            result.append("<tr><td colspan=\"5\">&nbsp;</td></tr>\n");
1096            if (hasCommitErrors()) {
1097                result.append(dialogBlockStart(""));
1098                result.append(createWidgetTableStart());
1099                Iterator<Throwable> i = getCommitErrors().iterator();
1100                while (i.hasNext()) {
1101                    Throwable t = i.next();
1102                    result.append("<tr><td><img src=\"");
1103                    result.append(getSkinUri()).append("editors/xmlcontent/");
1104                    result.append("error.png");
1105                    result.append("\" border=\"0\" alt=\"\"></td><td class=\"xmlTdError maxwidth\">");
1106                    while (t != null) {
1107                        String message = "";
1108                        if (t instanceof I_CmsThrowable) {
1109                            message = ((I_CmsThrowable)t).getLocalizedMessage(getLocale());
1110                        } else {
1111                            message = t.getLocalizedMessage();
1112                        }
1113                        if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(message)) {
1114                            result.append(CmsStringUtil.escapeHtml(message));
1115                        }
1116
1117                        t = t.getCause();
1118                        if (t != null) {
1119                            result.append("<br/>");
1120                        }
1121                    }
1122                    result.append("</td></tr>\n");
1123                }
1124                result.append(createWidgetTableEnd());
1125                result.append(dialogBlockEnd());
1126            }
1127            if (hasValidationErrors()) {
1128                result.append(dialogBlockStart(""));
1129                result.append(createWidgetTableStart());
1130                Iterator<Throwable> i = getValidationErrorList().iterator();
1131                while (i.hasNext()) {
1132                    Throwable t = i.next();
1133                    result.append("<tr><td><img src=\"");
1134                    result.append(getSkinUri()).append("editors/xmlcontent/");
1135                    result.append("error.png");
1136                    result.append("\" border=\"0\" alt=\"\"></td><td class=\"xmlTdError maxwidth\">");
1137                    while (t != null) {
1138                        String message = "";
1139                        if (t instanceof I_CmsThrowable) {
1140                            message = ((I_CmsThrowable)t).getLocalizedMessage(getLocale());
1141                        } else {
1142                            message = t.getLocalizedMessage();
1143                        }
1144                        result.append(CmsStringUtil.escapeHtml(message));
1145                        t = t.getCause();
1146                        if (t != null) {
1147                            result.append("<br>");
1148                        }
1149                    }
1150                    result.append("</td></tr>\n");
1151                }
1152                result.append(createWidgetTableEnd());
1153                result.append(dialogBlockEnd());
1154            }
1155        }
1156        return result.toString();
1157    }
1158
1159    /**
1160     * Creates the HTML for the table around the dialog widgets.<p>
1161     *
1162     * @return the HTML for the table around the dialog widgets
1163     */
1164    protected String createWidgetTableEnd() {
1165
1166        return "</table>\n";
1167    }
1168
1169    /**
1170     * Creates the HTML to close the table around the dialog widgets.<p>
1171     *
1172     * @return the HTML to close the table around the dialog widgets
1173     */
1174    protected String createWidgetTableStart() {
1175
1176        return "<table cellspacing='0' cellpadding='0' class='xmlTable'>\n";
1177    }
1178
1179    /**
1180     * Generates the dialog starting html code.<p>
1181     *
1182     * @return html code
1183     *
1184     * @throws JspException if something goes wrong
1185     */
1186    protected String defaultActionHtml() throws JspException {
1187
1188        StringBuffer result = new StringBuffer(2048);
1189        result.append(defaultActionHtmlStart());
1190        result.append(defaultActionHtmlContent());
1191        result.append(defaultActionHtmlEnd());
1192        return result.toString();
1193    }
1194
1195    /**
1196     * Returns the html code for the default action content.<p>
1197     *
1198     * @return html code
1199     */
1200    protected String defaultActionHtmlContent() {
1201
1202        StringBuffer result = new StringBuffer(2048);
1203        result.append("<form name=\"EDITOR\" id=\"EDITOR\" method=\"post\" action=\"").append(getDialogRealUri());
1204        result.append("\" class=\"nomargin\" onsubmit=\"return submitAction('").append(DIALOG_OK).append(
1205            "', null, 'EDITOR');\">\n");
1206        result.append(dialogContentStart(getDialogTitle()));
1207        result.append(buildDialogForm());
1208        result.append(dialogContentEnd());
1209        result.append(dialogButtonsCustom());
1210        result.append(paramsAsHidden());
1211        if (getParamFramename() == null) {
1212            result.append("\n<input type=\"hidden\" name=\"").append(PARAM_FRAMENAME).append("\" value=\"\">\n");
1213        }
1214        result.append("</form>\n");
1215        result.append(getWidgetHtmlEnd());
1216        return result.toString();
1217    }
1218
1219    /**
1220     * Generates the dialog ending html code.<p>
1221     *
1222     * @return html code
1223     */
1224    protected String defaultActionHtmlEnd() {
1225
1226        StringBuffer result = new StringBuffer(2048);
1227        result.append(dialogEnd());
1228        result.append(bodyEnd());
1229        result.append(htmlEnd());
1230        return result.toString();
1231    }
1232
1233    /**
1234     * Generates the dialog starting html code.<p>
1235     *
1236     * @return html code
1237     *
1238     * @throws JspException if something goes wrong
1239     */
1240    protected String defaultActionHtmlStart() throws JspException {
1241
1242        StringBuffer result = new StringBuffer(2048);
1243        result.append(htmlStart("administration/index.html"));
1244        result.append("<script  src=\"").append(getResourceUri()).append(
1245            "editors/xmlcontent/edit.js\"></script>\n");
1246        result.append("<script  src=\"").append(getResourceUri()).append(
1247            "editors/xmlcontent/help.js\"></script>\n");
1248        result.append(getWidgetIncludes());
1249        result.append("<script >\n<!--\n");
1250        result.append("// flag indicating if form initialization is finished\n");
1251        result.append("var initialized = false;\n");
1252        result.append("// the OpenCms context path\n");
1253        result.append("var contextPath = \"").append(OpenCms.getSystemInfo().getOpenCmsContext()).append("\";\n\n");
1254        result.append("// action parameters of the form\n");
1255        result.append("var actionAddElement = \"").append(EDITOR_ACTION_ELEMENT_ADD).append("\";\n");
1256        result.append("var actionRemoveElement = \"").append(EDITOR_ACTION_ELEMENT_REMOVE).append("\";\n");
1257        result.append("function init() {\n");
1258        result.append(getWidgetInitCalls());
1259        result.append("\tsetTimeout(\"scrollForm();\", 200);\n");
1260        result.append("\tinitialized = true;\n");
1261        result.append("\twindow.onbeforeunload=exitEditor;\n");
1262        result.append("}\n\n");
1263        result.append("window.exitEditorCalled = false;\n");
1264        result.append("function exitEditor() {\n");
1265        result.append("\tif (window.exitEditorCalled) return;\n");
1266        result.append("\twindow.exitEditorCalled = true; \n");
1267        result.append("\ttry {\n");
1268        result.append("\t\t// close file selector popup if present\n");
1269        result.append("\t\tcloseTreeWin();\n");
1270        result.append("\t} catch (e) {}\n");
1271        result.append("}\n");
1272        result.append(getWidgetInitMethods());
1273        result.append("\n// -->\n</script>\n");
1274        result.append(bodyStart(null, "onload='init();'  onunload='exitEditor();'"));
1275        result.append(dialogStart());
1276        return result.toString();
1277    }
1278
1279    /**
1280     * Defines the list of parameters for this dialog.<p>
1281     */
1282    protected abstract void defineWidgets();
1283
1284    /**
1285     * Fills all widgets of this widget dialog with the values from the request parameters.<p>
1286     *
1287     * @param request the current HTTP servlet request
1288     */
1289    protected void fillWidgetValues(HttpServletRequest request) {
1290
1291        Map<?, ?> parameters = request.getParameterMap();
1292        Map<String, String[]> processedParameters = new HashMap<String, String[]>();
1293        Iterator<?> p = parameters.entrySet().iterator();
1294        // make sure all "hidden" widget parameters are decoded
1295        while (p.hasNext()) {
1296            Map.Entry<?, ?> entry = (Map.Entry<?, ?>)p.next();
1297            String key = (String)entry.getKey();
1298            String[] values = (String[])entry.getValue();
1299            if (key.startsWith(HIDDEN_PARAM_PREFIX)) {
1300                // this is an encoded hidden parameter
1301                key = key.substring(HIDDEN_PARAM_PREFIX.length());
1302                String[] newValues = new String[values.length];
1303                for (int l = 0; l < values.length; l++) {
1304                    newValues[l] = CmsEncoder.decode(values[l], getCms().getRequestContext().getEncoding());
1305                }
1306                values = newValues;
1307            }
1308            processedParameters.put(key, values);
1309        }
1310
1311        // now process the parameters
1312        m_widgetParamValues = new HashMap<String, List<CmsWidgetDialogParameter>>();
1313        Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator();
1314
1315        while (i.hasNext()) {
1316            // check for all widget base parameters
1317            CmsWidgetDialogParameter base = i.next();
1318
1319            List<CmsWidgetDialogParameter> params = new ArrayList<CmsWidgetDialogParameter>();
1320            int maxOccurs = base.getMaxOccurs();
1321
1322            boolean onPage = false;
1323            if (base.isCollectionBase()) {
1324                // for a collection base, check if we are on the page where the collection base is shown
1325                if (CmsStringUtil.isNotEmpty(getParamAction()) && !DIALOG_INITIAL.equals(getParamAction())) {
1326                    // if no action set (usually for first display of dialog) make sure all values are shown
1327                    // DIALOG_INITIAL is a special value for the first display and must be handled the same way
1328                    String page = getParamPage();
1329                    // keep in mind that since the paramPage will be set AFTER the widget values are filled,
1330                    // so the first time this page is called from another page the following will result to "false",
1331                    // but for every "submit" on the page this will be "true"
1332                    onPage = CmsStringUtil.isEmpty(page)
1333                        || CmsStringUtil.isEmpty(base.getDialogPage())
1334                        || base.getDialogPage().equals(page);
1335                }
1336            }
1337
1338            for (int j = 0; j < maxOccurs; j++) {
1339                // check for all possible values in the request parameters
1340                String id = CmsWidgetDialogParameter.createId(base.getName(), j);
1341
1342                boolean required = (params.size() < base.getMinOccurs())
1343                    || (processedParameters.get(id) != null)
1344                    || (!onPage && base.hasValue(j));
1345
1346                if (required) {
1347                    CmsWidgetDialogParameter param = new CmsWidgetDialogParameter(base, params.size(), j);
1348                    param.setKeyPrefix(m_prefix);
1349                    base.getWidget().setEditorValue(getCms(), processedParameters, this, param);
1350                    params.add(param);
1351                }
1352            }
1353            m_widgetParamValues.put(base.getName(), params);
1354        }
1355    }
1356
1357    /**
1358     * Returns the title for this Dialog.<p>
1359     *
1360     * In the default implementation this method returns <code>null</code>.
1361     * Override this if needed.<p>
1362     *
1363     * @return the title for this Dialog, or <code>null</code> if this dialog has no title
1364     */
1365    protected String getDialogTitle() {
1366
1367        return null;
1368    }
1369
1370    /**
1371     * Returns the allowed pages for this dialog.<p>
1372     *
1373     * @return the allowed pages for this dialog
1374     */
1375    protected abstract String[] getPageArray();
1376
1377    /**
1378     * Returns the allowed pages for this dialog.<p>
1379     *
1380     * @return the allowed pages for this dialog
1381     */
1382    protected List<String> getPages() {
1383
1384        if (m_pages == null) {
1385            m_pages = Arrays.asList(getPageArray());
1386        }
1387        return m_pages;
1388    }
1389
1390    /**
1391     * Returns the parameter widget definition for the given parameter name.<p>
1392     *
1393     * @param name the parameter name to get the definition for
1394     *
1395     * @return the parameter widget definition for the given parameter name
1396     */
1397    protected CmsWidgetDialogParameter getParameterDefinition(String name) {
1398
1399        Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator();
1400        while (i.hasNext()) {
1401            // check for all widget parameters
1402            CmsWidgetDialogParameter base = i.next();
1403            if (base.getName().equals(name)) {
1404                return base;
1405            }
1406        }
1407        return null;
1408    }
1409
1410    /**
1411     * Returns the map with the widget parameter values.<p>
1412     *
1413     * @return the map with the widget parameter values
1414     */
1415    protected Map<String, List<CmsWidgetDialogParameter>> getParameters() {
1416
1417        return m_widgetParamValues;
1418    }
1419
1420    /**
1421     * Returns the validation errors for the dialog.<p>
1422     *
1423     * The method (@link CmsWidgetDialog#commitWidgetValues(String)) has to set this list.<p>
1424     *
1425     * @return the validation errors for the dialog
1426     */
1427    protected List<Throwable> getValidationErrorList() {
1428
1429        return m_validationErrorList;
1430    }
1431
1432    /**
1433     * Returns the widget HTML code for the given parameter.<p>
1434     *
1435     * @param param the name (id) of the parameter to get the widget HTML for
1436     *
1437     * @return the widget HTML code for the given parameter
1438     */
1439    protected String getWidget(CmsWidgetDialogParameter param) {
1440
1441        if (param != null) {
1442            return param.getWidget().getDialogWidget(getCms(), this, param);
1443        }
1444        return null;
1445    }
1446
1447    /**
1448     * Returns the list of all widgets used on this widget dialog, the
1449     * List must contain Objects of type <code>{@link CmsWidgetDialogParameter}</code>.<p>
1450     *
1451     * @return the list of all widgets used on this widget dialog
1452     */
1453    protected List<CmsWidgetDialogParameter> getWidgets() {
1454
1455        if (m_widgets == null) {
1456            m_widgets = new ArrayList<CmsWidgetDialogParameter>();
1457        }
1458        return m_widgets;
1459    }
1460
1461    /**
1462     * Returns <code>true</code> if the current dialog (page) has commit errors.<p>
1463     *
1464     * @return <code>true</code> if the current dialog (page) has commit errors
1465     */
1466    protected boolean hasCommitErrors() {
1467
1468        return (m_commitErrors != null) && (m_commitErrors.size() > 0);
1469    }
1470
1471    /**
1472     * Returns <code>true</code> if the current dialog (page) has validation errors.<p>
1473     *
1474     * @return <code>true</code> if the current dialog (page) has validation errors
1475     */
1476    protected boolean hasValidationErrors() {
1477
1478        return (m_validationErrorList != null) && (m_validationErrorList.size() > 0);
1479    }
1480
1481    /**
1482     * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest)
1483     */
1484    @Override
1485    protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) {
1486
1487        // set the dialog type
1488        setParamDialogtype(getClass().getName());
1489
1490        // fill the parameter values in the get/set methods
1491        fillParamValues(request);
1492
1493        if (CmsStringUtil.isEmptyOrWhitespaceOnly(getParamPage()) || !getPages().contains(getParamPage())) {
1494            // ensure a valid page is set
1495            setParamPage(getPages().get(0));
1496        }
1497
1498        // test the needed parameters
1499        try {
1500            validateParamaters();
1501        } catch (Exception e) {
1502            if (LOG.isInfoEnabled()) {
1503                LOG.info(Messages.get().container(Messages.ERR_WORKPLACE_DIALOG_PARAMS_1, getCurrentToolPath()), e);
1504            }
1505            // close if parameters not available
1506            setAction(ACTION_CANCEL);
1507            try {
1508                actionCloseDialog();
1509            } catch (JspException e1) {
1510                // ignore
1511            }
1512            return;
1513        }
1514
1515        // fill the widget map
1516        defineWidgets();
1517        fillWidgetValues(request);
1518
1519        // set the action for the JSP switch
1520        if (DIALOG_SAVE.equals(getParamAction())) {
1521            // ok button pressed, save
1522            List<Throwable> errors = commitWidgetValues(null);
1523            if (errors.size() > 0) {
1524                setAction(ACTION_DEFAULT);
1525                // found validation errors, redisplay page
1526                return;
1527            }
1528            setAction(ACTION_SAVE);
1529        } else if (DIALOG_OK.equals(getParamAction())) {
1530            // ok button pressed
1531            setAction(ACTION_CANCEL);
1532        } else if (DIALOG_CANCEL.equals(getParamAction())) {
1533            // cancel button pressed
1534            setAction(ACTION_CANCEL);
1535        } else if (EDITOR_ACTION_ELEMENT_ADD.equals(getParamAction())) {
1536            // add optional input element
1537            setAction(ACTION_ELEMENT_ADD);
1538            actionToggleElement();
1539            setAction(ACTION_DEFAULT);
1540        } else if (EDITOR_ACTION_ELEMENT_REMOVE.equals(getParamAction())) {
1541            // remove optional input element
1542            setAction(ACTION_ELEMENT_REMOVE);
1543            actionToggleElement();
1544            setAction(ACTION_DEFAULT);
1545        } else if (DIALOG_BACK.equals(getParamAction())) {
1546            // go back one page
1547            setAction(ACTION_DEFAULT);
1548            List<Throwable> errors = commitWidgetValues(getParamPage());
1549            if (errors.size() > 0) {
1550                // found validation errors, redisplay page
1551                return;
1552            }
1553            int pageIndex = getPages().indexOf(getParamPage()) - 1;
1554            setParamPage(getPages().get(pageIndex));
1555
1556        } else if (DIALOG_CONTINUE.equals(getParamAction())) {
1557            // go to next page
1558            setAction(ACTION_DEFAULT);
1559            List<Throwable> errors = commitWidgetValues(getParamPage());
1560            if (errors.size() > 0) {
1561                // found validation errors, redisplay page
1562                return;
1563            }
1564            int pageIndex = getPages().indexOf(getParamPage()) + 1;
1565            setParamPage(getPages().get(pageIndex));
1566
1567        } else {
1568            // first dialog call, set the default action
1569            setAction(ACTION_DEFAULT);
1570        }
1571    }
1572
1573    /**
1574     * Sets the errors that are thrown by save actions or form generation.<p>
1575     *
1576     * @param errors the errors that are thrown by save actions or form generation
1577     */
1578    protected void setCommitErrors(List<Throwable> errors) {
1579
1580        m_commitErrors = errors;
1581    }
1582
1583    /**
1584     * Sets an optional localized key prefix identificator for all widgets.<p>
1585     *
1586     * @param prefix the optional localized key prefix identificator for all widgets
1587     *
1588     * @see org.opencms.widgets.I_CmsWidgetParameter#setKeyPrefix(java.lang.String)
1589     */
1590    protected void setKeyPrefix(String prefix) {
1591
1592        m_prefix = prefix;
1593    }
1594
1595    /**
1596     * Sets the allowed pages for this dialog.<p>
1597     *
1598     * @param pages the allowed pages for this dialog
1599     */
1600    protected void setPages(List<String> pages) {
1601
1602        m_pages = pages;
1603    }
1604
1605    /**
1606     * Sets the validation errors for the dialog.<p>
1607     *
1608     * Use this in the method (@link CmsWidgetDialog#commitWidgetValues(String)) to set the list.<p>
1609     *
1610     * @param errors the validation errors
1611     */
1612    protected void setValidationErrorList(List<Throwable> errors) {
1613
1614        m_validationErrorList = errors;
1615    }
1616
1617    /**
1618     * Should be overridden for parameter validation.<p>
1619     *
1620     * The exception is never seen by the user, so it can be just a <code>new {@link Exception}()</code>.<p>
1621     *
1622     * @throws Exception if the parameters are not valid
1623     */
1624    protected void validateParamaters() throws Exception {
1625
1626        // valid by default
1627    }
1628
1629    /**
1630     * Returns the (internal use only) map of dialog objects.<p>
1631     *
1632     * @return the (internal use only) map of dialog objects
1633     */
1634    private Map<String, Object> getDialogObjectMap() {
1635
1636        @SuppressWarnings("unchecked")
1637        Map<String, Object> objects = (Map<String, Object>)getSettings().getDialogObject();
1638        if (objects == null) {
1639            // using hash table as most efficient version of a synchronized map
1640            objects = new Hashtable<String, Object>();
1641            getSettings().setDialogObject(objects);
1642        }
1643        return objects;
1644    }
1645}