/* * This library is part of OpenCms - * the Open Source Content Management System * * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * For further information about Alkacon Software GmbH, please see the * company website: http://www.alkacon.com * * For further information about OpenCms, please see the * project website: http://www.opencms.org * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.opencms.workplace; import org.opencms.i18n.CmsEncoder; import org.opencms.jsp.CmsJspActionElement; import org.opencms.main.CmsLog; import org.opencms.main.I_CmsThrowable; import org.opencms.main.OpenCms; import org.opencms.util.CmsRequestUtil; import org.opencms.util.CmsStringUtil; import org.opencms.widgets.A_CmsWidget; import org.opencms.widgets.CmsDisplayWidget; import org.opencms.widgets.I_CmsWidget; import org.opencms.widgets.I_CmsWidgetDialog; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import org.apache.commons.logging.Log; /** * Base class for dialogs that use the OpenCms widgets without XML content.<p> * * @since 6.0.0 */ public abstract class CmsWidgetDialog extends CmsDialog implements I_CmsWidgetDialog { /** Action for optional element creation. */ public static final int ACTION_ELEMENT_ADD = 152; /** Action for optional element removal. */ public static final int ACTION_ELEMENT_REMOVE = 153; /** Value for the action: error in the form validation. */ public static final int ACTION_ERROR = 303; /** Value for the action: save the dialog. */ public static final int ACTION_SAVE = 300; /** Request parameter value for the action: save the dialog. */ public static final String DIALOG_SAVE = "save"; /** Indicates an optional element should be created. */ public static final String EDITOR_ACTION_ELEMENT_ADD = "addelement"; /** Indicates an optional element should be removed. */ public static final String EDITOR_ACTION_ELEMENT_REMOVE = "removeelement"; /** Prefix for "hidden" parameters, required since these must be unescaped later. */ public static final String HIDDEN_PARAM_PREFIX = "hidden."; /** The log object for this class. */ private static final Log LOG = CmsLog.getLog(CmsWidgetDialog.class); /** The errors thrown by commit actions. */ protected List<Throwable> m_commitErrors; /** The object edited with this widget dialog. */ protected Object m_dialogObject; /** The allowed pages for this dialog in a List. */ protected List<String> m_pages; /** Controls which page is currently displayed in the dialog. */ protected String m_paramPage; /** The validation errors for the input form. */ protected List<Throwable> m_validationErrorList; /** Contains all parameter value of this dialog. */ protected Map<String, List<CmsWidgetDialogParameter>> m_widgetParamValues; /** The list of widgets used on the dialog. */ protected List<CmsWidgetDialogParameter> m_widgets; /** The set of help message ids that have already been used. */ private Set<String> m_helpMessageIds; /** * Parameter stores the index of the element to add or remove.<p> * * This must not be <code>null</code>, because it must be available * when calling <code>{@link org.opencms.workplace.CmsWorkplace#paramsAsHidden()}</code>.<p> */ private String m_paramElementIndex = "0"; /** * Parameter stores the name of the element to add or remove.<p> * * This must not be <code>null</code>, because it must be available * when calling <code>{@link org.opencms.workplace.CmsWorkplace#paramsAsHidden()}</code>.<p> */ private String m_paramElementName = "undefined"; /** Optional localized key prefix identificator. */ private String m_prefix; /** * Public constructor with JSP action element.<p> * * @param jsp an initialized JSP action element */ public CmsWidgetDialog(CmsJspActionElement jsp) { super(jsp); } /** * Public constructor with JSP variables.<p> * * @param context the JSP page context * @param req the JSP request * @param res the JSP response */ public CmsWidgetDialog(PageContext context, HttpServletRequest req, HttpServletResponse res) { this(new CmsJspActionElement(context, req, res)); } /** * Deletes the edited dialog object from the session.<p> */ public void actionCancel() { clearDialogObject(); } /** * Commits the edited object after pressing the "OK" button.<p> * * @throws IOException in case of errors forwarding to the required result page * @throws ServletException in case of errors forwarding to the required result page */ public abstract void actionCommit() throws IOException, ServletException; /** * Adds or removes an optional element.<p> * * Depends on the value stored in the <code>{@link CmsDialog#getAction()}</code> method.<p> */ public void actionToggleElement() { // get the necessary parameters to add/remove the element int index = 0; try { index = Integer.parseInt(getParamElementIndex()); } catch (Exception e) { // ignore, should not happen } String name = getParamElementName(); // get the base parameter definition CmsWidgetDialogParameter base = getParameterDefinition(name); if (base != null) { // the requested parameter is valid for this dialog List<CmsWidgetDialogParameter> params = getParameters().get(name); if (getAction() == ACTION_ELEMENT_REMOVE) { // remove the value params.remove(index); } else { List<CmsWidgetDialogParameter> sequence = getParameters().get(base.getName()); if (sequence.size() > 0) { // add the new value after the clicked element index = index + 1; } CmsWidgetDialogParameter newParam = new CmsWidgetDialogParameter(base, index); params.add(index, newParam); } // reset all index value in the parameter list for (int i = 0; i < params.size(); i++) { CmsWidgetDialogParameter param = params.get(i); param.setindex(i); } } } /** * Returns the html for a button to add an optional element.<p> * * @param elementName name of the element * @param insertAfter the index of the element after which the new element should be created * @param enabled if true, the button to add an element is shown, otherwise a spacer is returned * @return the html for a button to add an optional element */ public String buildAddElement(String elementName, int insertAfter, boolean enabled) { if (enabled) { StringBuffer href = new StringBuffer(4); href.append("javascript:addElement('"); href.append(elementName); href.append("', "); href.append(insertAfter); href.append(");"); return button(href.toString(), null, "new.png", Messages.GUI_DIALOG_BUTTON_ADDNEW_0, 0); } else { return ""; } } /** * Builds the HTML for the dialog form.<p> * * @return the HTML for the dialog form */ public String buildDialogForm() { // create the dialog HTML return createDialogHtml(getParamPage()); } /** * Returns the html for a button to remove an optional element.<p> * * @param elementName name of the element * @param index the element index of the element to remove * @param enabled if true, the button to remove an element is shown, otherwise a spacer is returned * @return the html for a button to remove an optional element */ public String buildRemoveElement(String elementName, int index, boolean enabled) { if (enabled) { StringBuffer href = new StringBuffer(4); href.append("javascript:removeElement('"); href.append(elementName); href.append("', "); href.append(index); href.append(");"); return button(href.toString(), null, "deletecontent.png", Messages.GUI_DIALOG_BUTTON_DELETE_0, 0); } else { return ""; } } /** * Clears the "dialog object" for this widget dialog by removing it from the current users session.<p> */ public void clearDialogObject() { setDialogObject(null); } /** * Builds the end HTML for a block with 3D border in the dialog content area.<p> * * @return 3D block start / end segment */ @Override public String dialogBlockEnd() { StringBuffer result = new StringBuffer(8); result.append(super.dialogBlockEnd()); result.append(dialogSpacer()); result.append("</td></tr>\n"); return result.toString(); } /** * Builds the start HTML for a block with 3D border and optional subheadline in the dialog content area.<p> * * @param headline the headline String for the block * @return 3D block start / end segment */ @Override public String dialogBlockStart(String headline) { StringBuffer result = new StringBuffer(8); result.append("<tr><td colspan=\"5\">\n"); result.append(super.dialogBlockStart(headline)); return result.toString(); } /** * Creates the HTML for the buttons on the dialog.<p> * * @return the HTML for the buttons on the dialog.<p> */ public String dialogButtonsCustom() { if (getPages().size() > 1) { // this is a multi page dialog, create buttons according to current page int pageIndex = getPages().indexOf(getParamPage()); if (pageIndex == (getPages().size() - 1)) { // this is the last dialog page return dialogButtons(new int[] {BUTTON_OK, BUTTON_BACK, BUTTON_CANCEL}, new String[3]); } else if (pageIndex > 0) { // this is a dialog page between first and last page return dialogButtons(new int[] {BUTTON_BACK, BUTTON_CONTINUE, BUTTON_CANCEL}, new String[3]); } else { // this is the first dialog page return dialogButtons(new int[] {BUTTON_CONTINUE, BUTTON_CANCEL}, new String[2]); } } boolean onlyDisplay = true; Iterator<CmsWidgetDialogParameter> it = getWidgets().iterator(); while (it.hasNext()) { CmsWidgetDialogParameter wdp = it.next(); if (!(wdp.getWidget() instanceof CmsDisplayWidget)) { onlyDisplay = false; break; } } if (!onlyDisplay) { // this is a single page dialog, create common buttons return dialogButtons(new int[] {BUTTON_OK, BUTTON_CANCEL}, new String[2]); } // this is a display only dialog return ""; } /** * Performs the dialog actions depending on the initialized action and displays the dialog form.<p> * * @throws JspException if dialog actions fail * @throws IOException if writing to the JSP out fails, or in case of errors forwarding to the required result page * @throws ServletException in case of errors forwarding to the required result page */ public void displayDialog() throws JspException, IOException, ServletException { displayDialog(false); } /** * Performs the dialog actions depending on the initialized action and displays the dialog form if needed.<p> * * @param writeLater if <code>true</code> no output is written, * you have to call manually the <code>{@link #defaultActionHtml()}</code> method. * * @throws JspException if dialog actions fail * @throws IOException if writing to the JSP out fails, or in case of errors forwarding to the required result page * @throws ServletException in case of errors forwarding to the required result page */ public void displayDialog(boolean writeLater) throws JspException, IOException, ServletException { if (isForwarded()) { return; } switch (getAction()) { case ACTION_CANCEL: // ACTION: cancel button pressed actionCancel(); actionCloseDialog(); break; case ACTION_ERROR: // ACTION: an error occurred (display nothing) break; case ACTION_SAVE: // ACTION: save edited values setParamAction(DIALOG_OK); actionCommit(); if (closeDialogOnCommit()) { setAction(ACTION_CANCEL); actionCloseDialog(); break; } setAction(ACTION_DEFAULT); //$FALL-THROUGH$ case ACTION_DEFAULT: default: // ACTION: show dialog (default) if (!writeLater) { writeDialog(); } } } /** * @see org.opencms.widgets.I_CmsWidgetDialog#getButtonStyle() */ public int getButtonStyle() { return getSettings().getUserSettings().getEditorButtonStyle(); } /** * Returns the errors that are thrown by save actions or form generation.<p> * * @return the errors that are thrown by save actions or form generation */ public List<Throwable> getCommitErrors() { return m_commitErrors; } /** * Returns the dialog object for this widget dialog, or <code>null</code> * if no dialog object has been set.<p> * * @return the dialog object for this widget dialog, or <code>null</code> */ public Object getDialogObject() { if (m_dialogObject == null) { m_dialogObject = getDialogObjectMap().get(getClass().getName()); } return m_dialogObject; } /** * @see org.opencms.widgets.I_CmsWidgetDialog#getHelpMessageIds() */ public Set<String> getHelpMessageIds() { if (m_helpMessageIds == null) { m_helpMessageIds = new HashSet<String>(); } return m_helpMessageIds; } /** * Returns the index of the element to add or remove.<p> * * @return the index of the element to add or remove */ public String getParamElementIndex() { return m_paramElementIndex; } /** * Returns the name of the element to add or remove.<p> * * @return the name of the element to add or remove */ public String getParamElementName() { return m_paramElementName; } /** * Returns the page parameter.<p> * * @return the page parameter */ public String getParamPage() { return m_paramPage; } /** * Returns the value of the widget parameter with the given name, or <code>null</code> * if no such widget parameter is available.<p> * * @param name the widget parameter name to get the value for * * @return the value of the widget parameter with the given name */ public String getParamValue(String name) { return getParamValue(name, 0); } /** * Returns the value of the widget parameter with the given name and index, or <code>null</code> * if no such widget parameter is available.<p> * * @param name the widget parameter name to get the value for * @param index the widget parameter index * * @return the value of the widget parameter with the given name and index */ public String getParamValue(String name, int index) { List<CmsWidgetDialogParameter> params = m_widgetParamValues.get(name); if (params != null) { if ((index >= 0) && (index < params.size())) { CmsWidgetDialogParameter param = params.get(index); if (param.getId().equals(CmsWidgetDialogParameter.createId(name, index))) { return param.getStringValue(getCms()); } } } return null; } /** * @see org.opencms.widgets.I_CmsWidgetDialog#getUserAgent() */ public String getUserAgent() { return getJsp().getRequest().getHeader(CmsRequestUtil.HEADER_USER_AGENT); } /** * Generates the HTML for the end of the widget dialog.<p> * * This HTML includes additional components, for example the <div> * tags containing the help texts.<p> * * @return the HTML for the end of the widget dialog */ public String getWidgetHtmlEnd() { StringBuffer result = new StringBuffer(32); // iterate over unique widgets from collector Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator(); while (i.hasNext()) { CmsWidgetDialogParameter param = i.next(); result.append(param.getWidget().getDialogHtmlEnd(getCms(), this, param)); } return result.toString(); } /** * Generates the HTML include tags for external JavaScripts files of the used widgets.<p> * * @return the HTML include tags for external JavaScripts files of the used widgets * * @throws JspException if an error occurs during JavaScript generation */ public String getWidgetIncludes() throws JspException { StringBuffer result = new StringBuffer(32); try { // iterate over unique widgets from collector Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator(); Set<I_CmsWidget> set = new HashSet<I_CmsWidget>(); while (i.hasNext()) { I_CmsWidget widget = i.next().getWidget(); if (!set.contains(widget)) { result.append(widget.getDialogIncludes(getCms(), this)); result.append('\n'); set.add(widget); } } } catch (Throwable e) { includeErrorpage(this, e); } return result.toString(); } /** * Generates the JavaScript init calls for the used widgets.<p> * * @return the JavaScript init calls for the used widgets * * @throws JspException the JavaScript init calls for the used widgets */ public String getWidgetInitCalls() throws JspException { StringBuffer result = new StringBuffer(32); try { // iterate over unique widgets from collector Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator(); Set<I_CmsWidget> set = new HashSet<I_CmsWidget>(); while (i.hasNext()) { I_CmsWidget widget = i.next().getWidget(); if (!set.contains(widget)) { result.append(widget.getDialogInitCall(getCms(), this)); set.add(widget); } } } catch (Throwable e) { includeErrorpage(this, e); } return result.toString(); } /** * Generates the JavaScript initialization methods for the used widgets.<p> * * @return the JavaScript initialization methods for the used widgets * * @throws JspException if an error occurs during JavaScript generation */ public String getWidgetInitMethods() throws JspException { StringBuffer result = new StringBuffer(32); try { // iterate over unique widgets from collector Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator(); Set<I_CmsWidget> set = new HashSet<I_CmsWidget>(); while (i.hasNext()) { I_CmsWidget widget = i.next().getWidget(); if (!set.contains(widget)) { result.append(widget.getDialogInitMethod(getCms(), this)); set.add(widget); } } } catch (Throwable e) { includeErrorpage(this, e); } return result.toString(); } /** * @see org.opencms.workplace.CmsWorkplace#paramsAsHidden() */ @Override public String paramsAsHidden() { if (getAction() != ACTION_ERROR) { return super.paramsAsHidden(); } // on an error page, also output the widget parameters StringBuffer result = new StringBuffer(); result.append(super.paramsAsHidden()); result.append('\n'); result.append(widgetParamsAsHidden()); return result.toString(); } /** * Stores the given object as "dialog object" for this widget dialog in the current users session.<p> * * @param dialogObject the object to store */ public void setDialogObject(Object dialogObject) { m_dialogObject = dialogObject; if (dialogObject == null) { // null object: remove the entry from the map getDialogObjectMap().remove(getClass().getName()); } else { getDialogObjectMap().put(getClass().getName(), dialogObject); } } /** * Sets the index of the element to add or remove.<p> * * @param elementIndex the index of the element to add or remove */ public void setParamElementIndex(String elementIndex) { m_paramElementIndex = elementIndex; } /** * Sets the name of the element to add or remove.<p> * * @param elementName the name of the element to add or remove */ public void setParamElementName(String elementName) { m_paramElementName = elementName; } /** * Sets the page parameter.<p> * * @param paramPage the page parameter to set */ public void setParamPage(String paramPage) { m_paramPage = paramPage; } /** * Returns the values of all widget parameters of this dialog as HTML hidden fields.<p> * * @return the values of all widget parameters of this dialog as HTML hidden fields * * @see org.opencms.workplace.CmsWorkplace#paramsAsHidden() */ public String widgetParamsAsHidden() { return widgetParamsAsHidden(null); } /** * Returns the values of all widget parameters of this dialog as HTML hidden fields, * excluding the widget values that are on the given dialog page.<p> * * This can be used to create multi-page dialogs where the values are passed from * one page to another before everything is submitted. If a widget A is used on page X, * there should be no "hidden" HTML field for A since otherwise A would have 2 values when * submitting the dialog page: The one from the widget itself and the one from the hidden * field. This may lead to undefined results when processing the submitted values.<p> * * @param excludeDialogPage the dialog page to exclude the values for * * @return the values of all widget parameters of this dialog as HTML hidden fields, * excluding the widget values that are on the given dialog page * * @see org.opencms.workplace.CmsWorkplace#paramsAsHidden() */ public String widgetParamsAsHidden(String excludeDialogPage) { StringBuffer result = new StringBuffer(); Iterator<String> i = m_widgetParamValues.keySet().iterator(); while (i.hasNext()) { List<CmsWidgetDialogParameter> params = m_widgetParamValues.get(i.next()); Iterator<CmsWidgetDialogParameter> j = params.iterator(); while (j.hasNext()) { CmsWidgetDialogParameter param = j.next(); String value = param.getStringValue(getCms()); if (CmsStringUtil.isNotEmpty(value) && ((excludeDialogPage == null) || (!param.getDialogPage().equals(excludeDialogPage)))) { result.append("<input type=\"hidden\" name=\""); result.append(HIDDEN_PARAM_PREFIX); result.append(param.getId()); result.append("\" value=\""); String encoded = CmsEncoder.encode(value, getCms().getRequestContext().getEncoding()); result.append(encoded); result.append("\">\n"); } } } return result.toString(); } /** * Writes the dialog html code, only if the <code>{@link #ACTION_DEFAULT}</code> is set.<p> * * @throws JspException if dialog actions fail * @throws IOException if writing to the JSP out fails, or in case of errors forwarding to the required result page */ public void writeDialog() throws IOException, JspException { if (isForwarded()) { return; } switch (getAction()) { case ACTION_CANCEL: case ACTION_ERROR: case ACTION_SAVE: break; case ACTION_DEFAULT: default: // ACTION: show dialog (default) setParamAction(DIALOG_SAVE); JspWriter out = getJsp().getJspContext().getOut(); out.print(defaultActionHtml()); } } /** * Adds the given error to the list of errors that are thrown by save actions or form generation.<p> * * If the error list has not been initialized yet, this is done automatically.<p> * * @param error the errors to add */ protected void addCommitError(Exception error) { if (m_commitErrors == null) { m_commitErrors = new ArrayList<Throwable>(); } m_commitErrors.add(error); } /** * Adds a new widget parameter definition to the list of all widgets of this dialog.<p> * * @param param the widget parameter definition to add */ protected void addWidget(CmsWidgetDialogParameter param) { if (m_widgets == null) { m_widgets = new ArrayList<CmsWidgetDialogParameter>(); } param.setKeyPrefix(m_prefix); m_widgets.add(param); } /** * Returns <code>true</code> if the dialog should be closed after the values have been committed.<p> * * The default implementation returns <code>true</code> in case there are no * commit errors.<p> * * @return <code>true</code> if the dialog should be closed after the values have been committed */ protected boolean closeDialogOnCommit() { return !hasCommitErrors(); } /** * Commits all values on the dialog.<p> * * @return a List of all Exceptions that occurred when comitting the dialog.<p> */ protected List<Throwable> commitWidgetValues() { return commitWidgetValues(null); } /** * Commits all values on the given dialog page.<p> * * @param dialogPage the dialog (page) to commit * * @return a List of all Exceptions that occurred when committing the dialog page.<p> */ protected List<Throwable> commitWidgetValues(String dialogPage) { List<Throwable> result = new ArrayList<Throwable>(); Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator(); while (i.hasNext()) { // check for all widget parameters CmsWidgetDialogParameter base = i.next(); if ((dialogPage == null) || (base.getDialogPage() == null) || dialogPage.equals(base.getDialogPage())) { // the parameter is located on the requested dialog base.prepareCommit(); List<CmsWidgetDialogParameter> params = m_widgetParamValues.get(base.getName()); Iterator<CmsWidgetDialogParameter> j = params.iterator(); while (j.hasNext()) { CmsWidgetDialogParameter param = j.next(); try { param.commitValue(this); } catch (Exception e) { result.add(e); } } } } setValidationErrorList(result); return result; } /** * Creates the dialog HTML for all defined widgets of this dialog.<p> * * @return the dialog HTML for all defined widgets of this dialog */ protected String createDialogHtml() { return createDialogHtml(null); } /** * Creates the dialog HTML for all defined widgets of the named dialog (page).<p> * * To get a more complex layout variation, you have to overwrite this method in your dialog class.<p> * * @param dialog the dialog (page) to get the HTML for * @return the dialog HTML for all defined widgets of the named dialog (page) */ protected String createDialogHtml(String dialog) { StringBuffer result = new StringBuffer(1024); // create table result.append(createWidgetTableStart()); // show error header once if there were validation errors result.append(createWidgetErrorHeader()); Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator(); // iterate the type sequence while (i.hasNext()) { // get the current widget base definition CmsWidgetDialogParameter base = i.next(); // check if the element is on the requested dialog page if ((dialog == null) || dialog.equals(base.getDialogPage())) { // add the HTML for the dialog element result.append(createDialogRowHtml(base)); } } // close table result.append(createWidgetTableEnd()); return result.toString(); } /** * Creates the dialog HTML for all occurrences of one widget parameter.<p> * * @param base the widget parameter base * @return the dialog HTML for one widget parameter */ protected String createDialogRowHtml(CmsWidgetDialogParameter base) { StringBuffer result = new StringBuffer(256); List<CmsWidgetDialogParameter> sequence = getParameters().get(base.getName()); int count = sequence.size(); // check if value is optional or multiple boolean addValue = false; if (count < base.getMaxOccurs()) { addValue = true; } boolean removeValue = false; if (count > base.getMinOccurs()) { removeValue = true; } // check if value is present boolean disabledElement = false; if (count < 1) { // no parameter with the value present, but also not optional: use base as parameter sequence = new ArrayList<CmsWidgetDialogParameter>(); sequence.add(base); count = 1; if (base.getMinOccurs() == 0) { disabledElement = true; } } // loop through multiple elements for (int j = 0; j < count; j++) { // get the parameter and the widget CmsWidgetDialogParameter p = sequence.get(j); I_CmsWidget widget = p.getWidget(); // check for an error in this row if (p.hasError()) { // show error message result.append("<tr><td></td><td><img src=\""); result.append(getSkinUri()).append("editors/xmlcontent/"); result.append("error.png"); result.append("\" border=\"0\" alt=\"\"></td><td class=\"xmlTdError\">"); Throwable t = p.getError(); while (t != null) { if (t instanceof I_CmsThrowable) { result.append(CmsEncoder.escapeXml(((I_CmsThrowable)t).getLocalizedMessage(getLocale()))); } else { result.append(CmsEncoder.escapeXml(t.getLocalizedMessage())); } t = t.getCause(); if (t != null) { result.append("<br>"); } } result.append("</td><td colspan=\"2\"></td></tr>\n"); } // create label and help bubble cells result.append("<tr>"); result.append("<td class=\"xmlLabel"); if (disabledElement) { // element is disabled, mark it with css result.append("Disabled"); } result.append("\">"); result.append(keyDefault(A_CmsWidget.getLabelKey(p), p.getName())); if (count > 1) { result.append(" [").append(p.getIndex() + 1).append("]"); } result.append(": </td>"); if (p.getIndex() == 0) { // show help bubble only on first element of each content definition result.append(p.getWidget().getHelpBubble(getCms(), this, p)); } else { // create empty cell for all following elements result.append(dialogHorizontalSpacer(16)); } // append individual widget html cell if element is enabled if (!disabledElement) { // this is a simple type, display widget result.append(widget.getDialogWidget(getCms(), this, p)); } else { // disabled element, show message for optional element result.append("<td class=\"xmlTdDisabled maxwidth\">"); result.append(key(Messages.GUI_EDITOR_WIDGET_OPTIONALELEMENT_0)); result.append("</td>"); } // append add and remove element buttons if required result.append(dialogHorizontalSpacer(5)); result.append("<td>"); if (addValue || removeValue) { result.append("<table border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>"); if (!addValue) { result.append(dialogHorizontalSpacer(25)); } else { result.append("<td><table class=\"editorbuttonbackground\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>"); result.append(buildAddElement(base.getName(), p.getIndex(), addValue)); } if (removeValue) { if (!addValue) { result.append("<td><table class=\"editorbuttonbackground\" border=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr>"); } result.append(buildRemoveElement(base.getName(), p.getIndex(), removeValue)); } result.append("</tr></table></td>"); result.append("</tr></table>"); } result.append("</td>"); // close row result.append("</tr>\n"); } return result.toString(); } /** * Creates the dialog widget rows HTML for the specified widget indices.<p> * * @param startIndex the widget index to start with * @param endIndex the widget index to stop at * * @return the dialog widget rows HTML for the specified widget indices */ protected String createDialogRowsHtml(int startIndex, int endIndex) { StringBuffer result = new StringBuffer((endIndex - startIndex) * 8); for (int i = startIndex; i <= endIndex; i++) { CmsWidgetDialogParameter base = getWidgets().get(i); result.append(createDialogRowHtml(base)); } return result.toString(); } /** * Creates the complete widget dialog end block HTML that finishes a widget block.<p> * * @return the complete widget dialog end block HTML that finishes a widget block */ protected String createWidgetBlockEnd() { StringBuffer result = new StringBuffer(8); result.append(createWidgetTableEnd()); result.append(dialogBlockEnd()); return result.toString(); } /** * Create the complete widget dialog start block HTML that begins a widget block with optional headline.<p> * * @param headline the headline String for the block * * @return the complete widget dialog start block HTML that begins a widget block with optional headline */ protected String createWidgetBlockStart(String headline) { StringBuffer result = new StringBuffer(16); result.append(dialogBlockStart(headline)); result.append(createWidgetTableStart()); return result.toString(); } /** * Creates the HTML for the error message if validation errors were found.<p> * * @return the HTML for the error message if validation errors were found */ protected String createWidgetErrorHeader() { StringBuffer result = new StringBuffer(8); if (hasValidationErrors() || hasCommitErrors()) { result.append("<tr><td colspan=\"5\"> </td></tr>\n"); result.append("<tr><td colspan=\"2\"> </td>"); result.append("<td class=\"xmlTdErrorHeader\">"); result.append(key(Messages.GUI_EDITOR_WIDGET_VALIDATION_ERROR_TITLE_0)); result.append("</td><td colspan=\"2\"> "); result.append("</td></tr>\n"); result.append("<tr><td colspan=\"5\"> </td></tr>\n"); if (hasCommitErrors()) { result.append(dialogBlockStart("")); result.append(createWidgetTableStart()); Iterator<Throwable> i = getCommitErrors().iterator(); while (i.hasNext()) { Throwable t = i.next(); result.append("<tr><td><img src=\""); result.append(getSkinUri()).append("editors/xmlcontent/"); result.append("error.png"); result.append("\" border=\"0\" alt=\"\"></td><td class=\"xmlTdError maxwidth\">"); while (t != null) { String message = ""; if (t instanceof I_CmsThrowable) { message = ((I_CmsThrowable)t).getLocalizedMessage(getLocale()); } else { message = t.getLocalizedMessage(); } if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(message)) { result.append(CmsStringUtil.escapeHtml(message)); } t = t.getCause(); if (t != null) { result.append("<br/>"); } } result.append("</td></tr>\n"); } result.append(createWidgetTableEnd()); result.append(dialogBlockEnd()); } if (hasValidationErrors()) { result.append(dialogBlockStart("")); result.append(createWidgetTableStart()); Iterator<Throwable> i = getValidationErrorList().iterator(); while (i.hasNext()) { Throwable t = i.next(); result.append("<tr><td><img src=\""); result.append(getSkinUri()).append("editors/xmlcontent/"); result.append("error.png"); result.append("\" border=\"0\" alt=\"\"></td><td class=\"xmlTdError maxwidth\">"); while (t != null) { String message = ""; if (t instanceof I_CmsThrowable) { message = ((I_CmsThrowable)t).getLocalizedMessage(getLocale()); } else { message = t.getLocalizedMessage(); } result.append(CmsStringUtil.escapeHtml(message)); t = t.getCause(); if (t != null) { result.append("<br>"); } } result.append("</td></tr>\n"); } result.append(createWidgetTableEnd()); result.append(dialogBlockEnd()); } } return result.toString(); } /** * Creates the HTML for the table around the dialog widgets.<p> * * @return the HTML for the table around the dialog widgets */ protected String createWidgetTableEnd() { return "</table>\n"; } /** * Creates the HTML to close the table around the dialog widgets.<p> * * @return the HTML to close the table around the dialog widgets */ protected String createWidgetTableStart() { return "<table cellspacing='0' cellpadding='0' class='xmlTable'>\n"; } /** * Generates the dialog starting html code.<p> * * @return html code * * @throws JspException if something goes wrong */ protected String defaultActionHtml() throws JspException { StringBuffer result = new StringBuffer(2048); result.append(defaultActionHtmlStart()); result.append(defaultActionHtmlContent()); result.append(defaultActionHtmlEnd()); return result.toString(); } /** * Returns the html code for the default action content.<p> * * @return html code */ protected String defaultActionHtmlContent() { StringBuffer result = new StringBuffer(2048); result.append("<form name=\"EDITOR\" id=\"EDITOR\" method=\"post\" action=\"").append(getDialogRealUri()); result.append("\" class=\"nomargin\" onsubmit=\"return submitAction('").append(DIALOG_OK).append( "', null, 'EDITOR');\">\n"); result.append(dialogContentStart(getDialogTitle())); result.append(buildDialogForm()); result.append(dialogContentEnd()); result.append(dialogButtonsCustom()); result.append(paramsAsHidden()); if (getParamFramename() == null) { result.append("\n<input type=\"hidden\" name=\"").append(PARAM_FRAMENAME).append("\" value=\"\">\n"); } result.append("</form>\n"); result.append(getWidgetHtmlEnd()); return result.toString(); } /** * Generates the dialog ending html code.<p> * * @return html code */ protected String defaultActionHtmlEnd() { StringBuffer result = new StringBuffer(2048); result.append(dialogEnd()); result.append(bodyEnd()); result.append(htmlEnd()); return result.toString(); } /** * Generates the dialog starting html code.<p> * * @return html code * * @throws JspException if something goes wrong */ protected String defaultActionHtmlStart() throws JspException { StringBuffer result = new StringBuffer(2048); result.append(htmlStart("administration/index.html")); result.append("<script type=\"text/javascript\" src=\"").append(getResourceUri()).append( "editors/xmlcontent/edit.js\"></script>\n"); result.append("<script type=\"text/javascript\" src=\"").append(getResourceUri()).append( "editors/xmlcontent/help.js\"></script>\n"); result.append(getWidgetIncludes()); result.append("<script type=\"text/javascript\">\n<!--\n"); result.append("// flag indicating if form initialization is finished\n"); result.append("var initialized = false;\n"); result.append("// the OpenCms context path\n"); result.append("var contextPath = \"").append(OpenCms.getSystemInfo().getOpenCmsContext()).append("\";\n\n"); result.append("// action parameters of the form\n"); result.append("var actionAddElement = \"").append(EDITOR_ACTION_ELEMENT_ADD).append("\";\n"); result.append("var actionRemoveElement = \"").append(EDITOR_ACTION_ELEMENT_REMOVE).append("\";\n"); result.append("function init() {\n"); result.append(getWidgetInitCalls()); result.append("\tsetTimeout(\"scrollForm();\", 200);\n"); result.append("\tinitialized = true;\n"); result.append("\twindow.onbeforeunload=exitEditor;\n"); result.append("}\n\n"); result.append("window.exitEditorCalled = false;\n"); result.append("function exitEditor() {\n"); result.append("\tif (window.exitEditorCalled) return;\n"); result.append("\twindow.exitEditorCalled = true; \n"); result.append("\ttry {\n"); result.append("\t\t// close file selector popup if present\n"); result.append("\t\tcloseTreeWin();\n"); result.append("\t} catch (e) {}\n"); result.append("}\n"); result.append(getWidgetInitMethods()); result.append("\n// -->\n</script>\n"); result.append(bodyStart(null, "onload='init();' onunload='exitEditor();'")); result.append(dialogStart()); return result.toString(); } /** * Defines the list of parameters for this dialog.<p> */ protected abstract void defineWidgets(); /** * Fills all widgets of this widget dialog with the values from the request parameters.<p> * * @param request the current HTTP servlet request */ protected void fillWidgetValues(HttpServletRequest request) { Map<?, ?> parameters = request.getParameterMap(); Map<String, String[]> processedParameters = new HashMap<String, String[]>(); Iterator<?> p = parameters.entrySet().iterator(); // make sure all "hidden" widget parameters are decoded while (p.hasNext()) { Map.Entry<?, ?> entry = (Map.Entry<?, ?>)p.next(); String key = (String)entry.getKey(); String[] values = (String[])entry.getValue(); if (key.startsWith(HIDDEN_PARAM_PREFIX)) { // this is an encoded hidden parameter key = key.substring(HIDDEN_PARAM_PREFIX.length()); String[] newValues = new String[values.length]; for (int l = 0; l < values.length; l++) { newValues[l] = CmsEncoder.decode(values[l], getCms().getRequestContext().getEncoding()); } values = newValues; } processedParameters.put(key, values); } // now process the parameters m_widgetParamValues = new HashMap<String, List<CmsWidgetDialogParameter>>(); Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator(); while (i.hasNext()) { // check for all widget base parameters CmsWidgetDialogParameter base = i.next(); List<CmsWidgetDialogParameter> params = new ArrayList<CmsWidgetDialogParameter>(); int maxOccurs = base.getMaxOccurs(); boolean onPage = false; if (base.isCollectionBase()) { // for a collection base, check if we are on the page where the collection base is shown if (CmsStringUtil.isNotEmpty(getParamAction()) && !DIALOG_INITIAL.equals(getParamAction())) { // if no action set (usually for first display of dialog) make sure all values are shown // DIALOG_INITIAL is a special value for the first display and must be handled the same way String page = getParamPage(); // keep in mind that since the paramPage will be set AFTER the widget values are filled, // so the first time this page is called from another page the following will result to "false", // but for every "submit" on the page this will be "true" onPage = CmsStringUtil.isEmpty(page) || CmsStringUtil.isEmpty(base.getDialogPage()) || base.getDialogPage().equals(page); } } for (int j = 0; j < maxOccurs; j++) { // check for all possible values in the request parameters String id = CmsWidgetDialogParameter.createId(base.getName(), j); boolean required = (params.size() < base.getMinOccurs()) || (processedParameters.get(id) != null) || (!onPage && base.hasValue(j)); if (required) { CmsWidgetDialogParameter param = new CmsWidgetDialogParameter(base, params.size(), j); param.setKeyPrefix(m_prefix); base.getWidget().setEditorValue(getCms(), processedParameters, this, param); params.add(param); } } m_widgetParamValues.put(base.getName(), params); } } /** * Returns the title for this Dialog.<p> * * In the default implementation this method returns <code>null</code>. * Override this if needed.<p> * * @return the title for this Dialog, or <code>null</code> if this dialog has no title */ protected String getDialogTitle() { return null; } /** * Returns the allowed pages for this dialog.<p> * * @return the allowed pages for this dialog */ protected abstract String[] getPageArray(); /** * Returns the allowed pages for this dialog.<p> * * @return the allowed pages for this dialog */ protected List<String> getPages() { if (m_pages == null) { m_pages = Arrays.asList(getPageArray()); } return m_pages; } /** * Returns the parameter widget definition for the given parameter name.<p> * * @param name the parameter name to get the definition for * * @return the parameter widget definition for the given parameter name */ protected CmsWidgetDialogParameter getParameterDefinition(String name) { Iterator<CmsWidgetDialogParameter> i = getWidgets().iterator(); while (i.hasNext()) { // check for all widget parameters CmsWidgetDialogParameter base = i.next(); if (base.getName().equals(name)) { return base; } } return null; } /** * Returns the map with the widget parameter values.<p> * * @return the map with the widget parameter values */ protected Map<String, List<CmsWidgetDialogParameter>> getParameters() { return m_widgetParamValues; } /** * Returns the validation errors for the dialog.<p> * * The method (@link CmsWidgetDialog#commitWidgetValues(String)) has to set this list.<p> * * @return the validation errors for the dialog */ protected List<Throwable> getValidationErrorList() { return m_validationErrorList; } /** * Returns the widget HTML code for the given parameter.<p> * * @param param the name (id) of the parameter to get the widget HTML for * * @return the widget HTML code for the given parameter */ protected String getWidget(CmsWidgetDialogParameter param) { if (param != null) { return param.getWidget().getDialogWidget(getCms(), this, param); } return null; } /** * Returns the list of all widgets used on this widget dialog, the * List must contain Objects of type <code>{@link CmsWidgetDialogParameter}</code>.<p> * * @return the list of all widgets used on this widget dialog */ protected List<CmsWidgetDialogParameter> getWidgets() { if (m_widgets == null) { m_widgets = new ArrayList<CmsWidgetDialogParameter>(); } return m_widgets; } /** * Returns <code>true</code> if the current dialog (page) has commit errors.<p> * * @return <code>true</code> if the current dialog (page) has commit errors */ protected boolean hasCommitErrors() { return (m_commitErrors != null) && (m_commitErrors.size() > 0); } /** * Returns <code>true</code> if the current dialog (page) has validation errors.<p> * * @return <code>true</code> if the current dialog (page) has validation errors */ protected boolean hasValidationErrors() { return (m_validationErrorList != null) && (m_validationErrorList.size() > 0); } /** * @see org.opencms.workplace.CmsWorkplace#initWorkplaceRequestValues(org.opencms.workplace.CmsWorkplaceSettings, javax.servlet.http.HttpServletRequest) */ @Override protected void initWorkplaceRequestValues(CmsWorkplaceSettings settings, HttpServletRequest request) { // set the dialog type setParamDialogtype(getClass().getName()); // fill the parameter values in the get/set methods fillParamValues(request); if (CmsStringUtil.isEmptyOrWhitespaceOnly(getParamPage()) || !getPages().contains(getParamPage())) { // ensure a valid page is set setParamPage(getPages().get(0)); } // test the needed parameters try { validateParamaters(); } catch (Exception e) { if (LOG.isInfoEnabled()) { LOG.info(Messages.get().container(Messages.ERR_WORKPLACE_DIALOG_PARAMS_1, getCurrentToolPath()), e); } // close if parameters not available setAction(ACTION_CANCEL); try { actionCloseDialog(); } catch (JspException e1) { // ignore } return; } // fill the widget map defineWidgets(); fillWidgetValues(request); // set the action for the JSP switch if (DIALOG_SAVE.equals(getParamAction())) { // ok button pressed, save List<Throwable> errors = commitWidgetValues(null); if (errors.size() > 0) { setAction(ACTION_DEFAULT); // found validation errors, redisplay page return; } setAction(ACTION_SAVE); } else if (DIALOG_OK.equals(getParamAction())) { // ok button pressed setAction(ACTION_CANCEL); } else if (DIALOG_CANCEL.equals(getParamAction())) { // cancel button pressed setAction(ACTION_CANCEL); } else if (EDITOR_ACTION_ELEMENT_ADD.equals(getParamAction())) { // add optional input element setAction(ACTION_ELEMENT_ADD); actionToggleElement(); setAction(ACTION_DEFAULT); } else if (EDITOR_ACTION_ELEMENT_REMOVE.equals(getParamAction())) { // remove optional input element setAction(ACTION_ELEMENT_REMOVE); actionToggleElement(); setAction(ACTION_DEFAULT); } else if (DIALOG_BACK.equals(getParamAction())) { // go back one page setAction(ACTION_DEFAULT); List<Throwable> errors = commitWidgetValues(getParamPage()); if (errors.size() > 0) { // found validation errors, redisplay page return; } int pageIndex = getPages().indexOf(getParamPage()) - 1; setParamPage(getPages().get(pageIndex)); } else if (DIALOG_CONTINUE.equals(getParamAction())) { // go to next page setAction(ACTION_DEFAULT); List<Throwable> errors = commitWidgetValues(getParamPage()); if (errors.size() > 0) { // found validation errors, redisplay page return; } int pageIndex = getPages().indexOf(getParamPage()) + 1; setParamPage(getPages().get(pageIndex)); } else { // first dialog call, set the default action setAction(ACTION_DEFAULT); } } /** * Sets the errors that are thrown by save actions or form generation.<p> * * @param errors the errors that are thrown by save actions or form generation */ protected void setCommitErrors(List<Throwable> errors) { m_commitErrors = errors; } /** * Sets an optional localized key prefix identificator for all widgets.<p> * * @param prefix the optional localized key prefix identificator for all widgets * * @see org.opencms.widgets.I_CmsWidgetParameter#setKeyPrefix(java.lang.String) */ protected void setKeyPrefix(String prefix) { m_prefix = prefix; } /** * Sets the allowed pages for this dialog.<p> * * @param pages the allowed pages for this dialog */ protected void setPages(List<String> pages) { m_pages = pages; } /** * Sets the validation errors for the dialog.<p> * * Use this in the method (@link CmsWidgetDialog#commitWidgetValues(String)) to set the list.<p> * * @param errors the validation errors */ protected void setValidationErrorList(List<Throwable> errors) { m_validationErrorList = errors; } /** * Should be overridden for parameter validation.<p> * * The exception is never seen by the user, so it can be just a <code>new {@link Exception}()</code>.<p> * * @throws Exception if the parameters are not valid */ protected void validateParamaters() throws Exception { // valid by default } /** * Returns the (internal use only) map of dialog objects.<p> * * @return the (internal use only) map of dialog objects */ private Map<String, Object> getDialogObjectMap() { @SuppressWarnings("unchecked") Map<String, Object> objects = (Map<String, Object>)getSettings().getDialogObject(); if (objects == null) { // using hash table as most efficient version of a synchronized map objects = new Hashtable<String, Object>(); getSettings().setDialogObject(objects); } return objects; } }