Improvded error handling for ContentItemPortlet

git-svn-id: https://svn.libreccm.org/ccm/trunk@3079 8810af33-2d31-482b-a856-94f89814c4df
master
jensp 2015-01-15 16:03:15 +00:00
parent 8cb473a69f
commit c431141540
2 changed files with 165 additions and 182 deletions

View File

@ -19,6 +19,7 @@
package com.arsdigita.cms.ui.portlet; package com.arsdigita.cms.ui.portlet;
import com.arsdigita.bebop.ColumnPanel; import com.arsdigita.bebop.ColumnPanel;
import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Label;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
@ -42,6 +43,7 @@ import com.arsdigita.cms.dispatcher.CMSDispatcher;
import com.arsdigita.cms.dispatcher.ItemResolver; import com.arsdigita.cms.dispatcher.ItemResolver;
import com.arsdigita.cms.dispatcher.MultilingualItemResolver; import com.arsdigita.cms.dispatcher.MultilingualItemResolver;
import com.arsdigita.cms.portlet.ContentItemPortlet; import com.arsdigita.cms.portlet.ContentItemPortlet;
import com.arsdigita.globalization.GlobalizedMessage;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.util.StringTokenizer; import java.util.StringTokenizer;
@ -176,10 +178,11 @@ public class ContentItemPortletEditor extends PortletConfigFormSection {
Portlet portlet) Portlet portlet)
throws FormProcessException { throws FormProcessException {
super.validateWidgets(state, portlet); super.validateWidgets(state, portlet);
Object item = m_contentItem.get( state ); Object item = m_contentItem.get( state );
if (item == null) if (item == null) {
throw new FormProcessException("cannot find content item"); throw new FormProcessException(new GlobalizedMessage("Can't find content item"));
}
} }
public void processWidgets(PageState state, public void processWidgets(PageState state,

View File

@ -36,50 +36,34 @@ import java.util.Iterator;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
* Represents the visual structure of an HTML form. Forms can be constructed * Represents the visual structure of an HTML form. Forms can be constructed with a Container
* with a Container argument to specify the type of layout this form will * argument to specify the type of layout this form will adhere to. The default is a column panel.
* adhere to. The default is a column panel.
* *
* <p> As an example, a form that accepts a first and last name may be set * <p>
* up as follows: * As an example, a form that accepts a first and last name may be set up as follows:
* *
* <pre style="background: #cccccc"> * <pre style="background: #cccccc">
* public class MyForm extends Form * public class MyForm extends Form implements FormProcessListener {
* implements FormProcessListener {
* *
* private Text m_firstName; * private Text m_firstName; private Text m_lastName;
* private Text m_lastName;
* *
* public MyForm() { * public MyForm() { super("myform"); add(new Label("First Name:")); m_firstName = new
* super("myform"); * Text("firstName"); m_firstName.setDefaultValue("John"); add(m_firstName);
* add(new Label("First Name:"));
* m_firstName = new Text("firstName");
* m_firstName.setDefaultValue("John");
* add(m_firstName);
* *
* add(new Label("Last Name:")); * add(new Label("Last Name:")); m_lastName = new Text("lastName");
* m_lastName = new Text("lastName"); * m_lastName.setDefaultValue("Doe"); m_lastName.addValidationListener(new NotNullValidationListener
* m_lastName.setDefaultValue("Doe"); * ("The last name")); add(m_lastName);
* m_lastName.addValidationListener(new NotNullValidationListener
* ("The last name"));
* add(m_lastName);
* *
* add(new Submit("save", "Save")); * add(new Submit("save", "Save")); addProcessListener(this); }
* addProcessListener(this);
* }
* *
* public void process(FormSectionEvent e) { * public void process(FormSectionEvent e) { PageState s = e.getPageState();
* PageState s = e.getPageState();
* *
* System.out.println("You are " + m_firstName.getValue(s) * System.out.println("You are " + m_firstName.getValue(s) + " " + m_lastName.getValue(s)); } }
* + " " + m_lastName.getValue(s));
* }
* }
* </pre> * </pre>
* *
* <p> This form automatically checks that the user supplied a last name. Only * <p>
* then does it call the <code>process</code> method, which prints the * This form automatically checks that the user supplied a last name. Only then does it call the
* user-supplied values. * <code>process</code> method, which prints the user-supplied values.
* *
* @author Karl Goldstein * @author Karl Goldstein
* @author Uday Mathur * @author Uday Mathur
@ -90,28 +74,25 @@ import org.apache.log4j.Logger;
* @version $Id: Form.java 287 2005-02-22 00:29:02Z sskracic $ * @version $Id: Form.java 287 2005-02-22 00:29:02Z sskracic $
*/ */
public class Form extends FormSection implements BebopConstants { public class Form extends FormSection implements BebopConstants {
/** Internal logger instance to faciliate debugging. Enable logging output /**
* by editing /WEB-INF/conf/log4j.properties int hte runtime environment * Internal logger instance to faciliate debugging. Enable logging output by editing
* and set com.arsdigita.bebop.Form=DEBUG by uncommenting * /WEB-INF/conf/log4j.properties int hte runtime environment and set
* or adding the line. */ * com.arsdigita.bebop.Form=DEBUG by uncommenting or adding the line.
*/
private static final Logger s_log = Logger.getLogger(Form.class); private static final Logger s_log = Logger.getLogger(Form.class);
/** /**
* Constant for specifying a <code>get</code> submission method for this * Constant for specifying a <code>get</code> submission method for this form. See the <a href=
* form. See the <a href= * "http://www.w3.org/TR/html4/interact/forms.html#submit-format">W3C HTML specification</a> for
* "http://www.w3.org/TR/html4/interact/forms.html#submit-format">W3C * a description of what this attribute does.
* HTML specification</a> for a description of what this attribute
* does.
*/ */
public final static String GET = "get"; public final static String GET = "get";
/** /**
* Constant for specifying a <code>post</code> submission method for this * Constant for specifying a <code>post</code> submission method for this form. See the <a href=
* form. See the <a href= * "http://www.w3.org/TR/html4/interact/forms.html#submit-format">W3C HTML specification</a> for
* "http://www.w3.org/TR/html4/interact/forms.html#submit-format">W3C HTML * a description of what this attribute does.
* specification</a> for a description of what this attribute does.
*/ */
public final static String POST = "post"; public final static String POST = "post";
@ -134,18 +115,15 @@ public class Form extends FormSection implements BebopConstants {
private RequestLocal m_formData; private RequestLocal m_formData;
/** /**
* Determines whether or not a form is 'redirecting', meaning that * Determines whether or not a form is 'redirecting', meaning that it will clear the control
* it will clear the control event and redirect to the resulting * event and redirect to the resulting state after form processing, so that a page reload won't
* state after form processing, so that a page reload won't cause * cause the form to be resubmitted.
* the form to be resubmitted.
*/ */
private boolean m_isRedirecting = false; private boolean m_isRedirecting = false;
/** /**
* Constructs a new form with the specified name. * Constructs a new form with the specified name. At the time of creation, instantiates a new
* At the time of creation, instantiates a new form model for the form and * form model for the form and instantiates a default ColumnPanel to contain the components.
* instantiates a default ColumnPanel to contain the components.
* *
* @param name the name of the form * @param name the name of the form
*/ */
@ -154,12 +132,11 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Constructs a new form with the specified name and container. At the time * Constructs a new form with the specified name and container. At the time of creation,
* of creation, instantiates a new form model for the form and replaces * instantiates a new form model for the form and replaces the default ColumnPanel with the
* the default ColumnPanel with the specified container as the implicit * specified container as the implicit container of the components.
* container of the components.
* *
* @param name the name attribute of the form * @param name the name attribute of the form
* @param panel the implicit container that will hold the components * @param panel the implicit container that will hold the components
*/ */
public Form(String name, Container panel) { public Form(String name, Container panel) {
@ -170,29 +147,29 @@ public class Form extends FormSection implements BebopConstants {
addMagicTag(); addMagicTag();
} }
/** /**
* Writes the output to a DOM to be used with the XSLT template to produce * Writes the output to a DOM to be used with the XSLT template to produce the appropriate
* the appropriate output. If the form is not visible, no output is * output. If the form is not visible, no output is generated.
* generated.
* *
* <p>Generates a DOM fragment: * <p>
* <p><code><pre> * Generates a DOM fragment:
* <p>
* <code><pre>
* &lt;bebop:form action=%url; %bebopAttr;> * &lt;bebop:form action=%url; %bebopAttr;>
* .. XML for panel .. * .. XML for panel ..
* .. XML for page state .. * .. XML for page state ..
* &lt;/bebop:form> * &lt;/bebop:form>
* </pre></code> * </pre></code>
* *
* @param s the page state used to determine the values of form * @param s the page state used to determine the values of form widgets and page state
* widgets and page state attributes * attributes
* @param parent the XML element to which the form adds its XML * @param parent the XML element to which the form adds its XML representation
* representation *
* @see PageState#generateXML * @see PageState#generateXML
*/ */
@Override @Override
public void generateXML(PageState s, Element parent) { public void generateXML(PageState s, Element parent) {
if ( isVisible(s) ) { if (isVisible(s)) {
Element form = generateXMLSansState(s, parent); Element form = generateXMLSansState(s, parent);
s.setControlEvent(this); s.setControlEvent(this);
@ -202,11 +179,12 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Generates the XML representing the form and its widgets, but not the * Generates the XML representing the form and its widgets, but not the state information from
* state information from <code>s</code>. * <code>s</code>.
* *
* @param s represents the curent request * @param s represents the curent request
* @param parent * @param parent
*
* @return the top-level element for the form * @return the top-level element for the form
*/ */
protected Element generateXMLSansState(PageState s, Element parent) { protected Element generateXMLSansState(PageState s, Element parent) {
@ -215,7 +193,6 @@ public class Form extends FormSection implements BebopConstants {
// Encode the URL with the servlet session information; // Encode the URL with the servlet session information;
// do not use DispatcherHelper.encodeURL because the // do not use DispatcherHelper.encodeURL because the
// ACS global parameters are provided via the FormData. // ACS global parameters are provided via the FormData.
String url = null; String url = null;
if (m_action == null) { if (m_action == null) {
@ -242,41 +219,43 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* *
* @param ps * @param ps
* @param parent * @param parent
*/ */
protected void generateErrors(PageState ps, Element parent) { protected void generateErrors(PageState ps, Element parent) {
for (Iterator it = getFormData(ps).getErrors(); it.hasNext(); ) { for (Iterator it = getFormData(ps).getErrors(); it.hasNext();) {
Element errors = parent.newChildElement(BEBOP_FORMERRORS, Element errors = parent.newChildElement(BEBOP_FORMERRORS,
BEBOP_XML_NS); BEBOP_XML_NS);
errors.addAttribute("message", Object msg = it.next();
(String)((GlobalizedMessage) it.next()).localize
(ps.getRequest()) if (msg == null) {
); errors.addAttribute("message", "Unknown error");
} else {
errors.addAttribute("message",
(String) ((GlobalizedMessage) msg).localize(ps.getRequest()));
}
errors.addAttribute("id", getName()); errors.addAttribute("id", getName());
} }
} }
/** /**
* <p>Determine whether or not this Form will redirect after its * <p>
* process listeners are fired.</p> * Determine whether or not this Form will redirect after its process listeners are fired.</p>
* *
* @return * @return
*/ */
public boolean isRedirecting() { public boolean isRedirecting() {
return m_isRedirecting; return m_isRedirecting;
} }
/** /**
* Setting the redirecting flag will cause the Form to clear the * Setting the redirecting flag will cause the Form to clear the control event and redirect back
* control event and redirect back to the current URL, after * to the current URL, after firing all process listeners. Doing so means that a user reload
* firing all process listeners. Doing so means that a user reload * will not cause the form to be resubmitted. The default value for this flag is false.
* will not cause the form to be resubmitted. The default value for *
* this flag is false.
*
* @param isRedirecting * @param isRedirecting
*/ */
public void setRedirecting(boolean isRedirecting) { public void setRedirecting(boolean isRedirecting) {
@ -285,11 +264,13 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Responds to the request by processing this form with the HTTP request * Responds to the request by processing this form with the HTTP request given in
* given in <code>state</code>. * <code>state</code>.
*
* @see #process process(...) * @see #process process(...)
* *
* @param state represents the current request * @param state represents the current request
*
* @throws javax.servlet.ServletException * @throws javax.servlet.ServletException
*/ */
@Override @Override
@ -306,11 +287,11 @@ public class Form extends FormSection implements BebopConstants {
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * *
// Methods to set the HTML attributes of the FORM element // Methods to set the HTML attributes of the FORM element
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * // * * * * * * * * * * * * * * * * * * * * * * * * * * * *
/** /**
* Sets the <code>name</code> attribute for the form. * Sets the <code>name</code> attribute for the form.
* *
* @param name the name for the form * @param name the name for the form
*
* @pre ! isLocked() * @pre ! isLocked()
*/ */
public void setName(String name) { public void setName(String name) {
@ -328,11 +309,11 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Sets the <code>enctype</code> attribute used in the * Sets the <code>enctype</code> attribute used in the <code>form</code> element. No encoding
* <code>form</code> element. No encoding type is specified by * type is specified by default.
* default.
* *
* @param encType the encoding type * @param encType the encoding type
*
* @pre ! isLocked() * @pre ! isLocked()
*/ */
public void setEncType(String encType) { public void setEncType(String encType) {
@ -341,12 +322,11 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Sets the <code>onSubmit</code> attribute used in the * Sets the <code>onSubmit</code> attribute used in the <code>form</code> element. No onsubmit
* <code>form</code> element. No onsubmit handler is specified by * handler is specified by default.
* default. *
* @param javascriptCode the javascript code associated with this attribute
* *
* @param javascriptCode the javascript code associated with this
* attribute
* @pre ! isLocked() * @pre ! isLocked()
*/ */
public void setOnSubmit(String javascriptCode) { public void setOnSubmit(String javascriptCode) {
@ -355,12 +335,11 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Sets the <code>ONRESET</code> attribute used in the * Sets the <code>ONRESET</code> attribute used in the <code>FORM</code> element. No onreset
* <code>FORM</code> element. No onreset handler is specified by * handler is specified by default.
* default. *
* @param javascriptCode the javascript code associated with this attribute
* *
* @param javascriptCode the javascript code associated with this
* attribute
* @pre ! isLocked() * @pre ! isLocked()
*/ */
public void setOnReset(String javascriptCode) { public void setOnReset(String javascriptCode) {
@ -372,6 +351,7 @@ public class Form extends FormSection implements BebopConstants {
* Sets the HTTP method used to submit the form. * Sets the HTTP method used to submit the form.
* *
* @param method either <code>GET</code> or <code>POST</code> * @param method either <code>GET</code> or <code>POST</code>
*
* @pre ! isLocked() * @pre ! isLocked()
*/ */
public void setMethod(String method) { public void setMethod(String method) {
@ -384,12 +364,10 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Returns true if form processing is turned on when the form is * Returns true if form processing is turned on when the form is invisible.
* invisible.
* *
* @return true if the form listeners should be * @return true if the form listeners should be processed even when the form is not visible on
* processed even when the form is not visible on the page, * the page, false otherwise
* false otherwise
*/ */
protected boolean getProcessInvisible() { protected boolean getProcessInvisible() {
return m_processInvisible; return m_processInvisible;
@ -398,24 +376,24 @@ public class Form extends FormSection implements BebopConstants {
/** /**
* Turns form processing on/off when the form is invisible. * Turns form processing on/off when the form is invisible.
* *
* @param processInvisible true if the form listeners should be * @param processInvisible true if the form listeners should be processed even when the form is
* processed even when the form is not visible on the page * not visible on the page
*/ */
protected void setProcessInvisible(boolean processInvisible) { protected void setProcessInvisible(boolean processInvisible) {
m_processInvisible = processInvisible; m_processInvisible = processInvisible;
} }
/** /**
* Sets the URL for the form's <code>action</code> attribute. This * Sets the URL for the form's <code>action</code> attribute. This is the URL to which
* is the URL to which submissions will be sent when the user * submissions will be sent when the user clicks a submit button on the form. By default, the
* clicks a submit button on the form. By default, the action is * action is <code>null</code>, instructing the form to set the action to the URL of the page in
* <code>null</code>, instructing the form to set the action to * which it is used. If the action is set to a different URL, none of the listeners registered
* the URL of the page in which it is used. If the action is set * with this form will be run.
* to a different URL, none of the listeners registered with this
* form will be run.
* *
* @param action the URL to submit this form to * @param action the URL to submit this form to
* @pre ! isLocked() */ *
* @pre ! isLocked()
*/
public void setAction(String action) { public void setAction(String action) {
Assert.isUnlocked(this); Assert.isUnlocked(this);
m_action = action; m_action = action;
@ -425,22 +403,24 @@ public class Form extends FormSection implements BebopConstants {
* Returns the URL for the form's <code>action</code> attribute. * Returns the URL for the form's <code>action</code> attribute.
* *
* @return the URL to which to submit this form. * @return the URL to which to submit this form.
*
* @see #setAction setAction * @see #setAction setAction
*/ */
public final String getAction(){ public final String getAction() {
return m_action; return m_action;
} }
/** /**
* Processes this form, creating a <code>FormData</code> object. Runs the * Processes this form, creating a <code>FormData</code> object. Runs the right set of init,
* right set of init, validation, and process listeners, depending on * validation, and process listeners, depending on whether this is an initial request to the
* whether this is an initial request to the form and whether the form * form and whether the form submission was valid. Submission listeners are always run.
* submission was valid. Submission listeners are always run. *
* @see #getFormData * @see #getFormData
* *
* @param state represents the current request * @param state represents the current request
* @return the values extracted from the HTTP request contained *
* in <code>state</code>. * @return the values extracted from the HTTP request contained in <code>state</code>.
*
* @throws com.arsdigita.bebop.FormProcessException * @throws com.arsdigita.bebop.FormProcessException
* @pre state != null * @pre state != null
* @post return != null * @post return != null
@ -448,7 +428,7 @@ public class Form extends FormSection implements BebopConstants {
@Override @Override
public FormData process(PageState state) throws FormProcessException { public FormData process(PageState state) throws FormProcessException {
Assert.exists(state, "PageState"); Assert.exists(state, "PageState");
FormData result = new FormData(getModel(), state.getRequest()); FormData result = new FormData(getModel(), state.getRequest());
setFormData(state, result); setFormData(state, result);
// Unless invisible form processing is turned on, don't run any // Unless invisible form processing is turned on, don't run any
@ -461,14 +441,14 @@ public class Form extends FormSection implements BebopConstants {
/** /**
* Returns the form data constructed by the {@link #process * Returns the form data constructed by the {@link #process
* process} method for the request described by * process} method for the request described by <code>state</code>. Processes the form if it has
* <code>state</code>. Processes the form if it has not already * not already been processed.
* been processed.
* *
* @param state describes the current request * @param state describes the current request
* @return the values extracted from the HTTP request contained *
* in <code>state</code>, or <code>null</code> if the form has not been * @return the values extracted from the HTTP request contained in <code>state</code>, or
* processed yet. * <code>null</code> if the form has not been processed yet.
*
* @pre state != null * @pre state != null
* @post return != null * @post return != null
*/ */
@ -476,10 +456,9 @@ public class Form extends FormSection implements BebopConstants {
return (FormData) m_formData.get(state); return (FormData) m_formData.get(state);
} }
/** /**
* Adds a Hidden Tag to this form so that our controller can determine if * Adds a Hidden Tag to this form so that our controller can determine if this is an initial
* this is an initial request. * request.
*/ */
protected void addMagicTag() { protected void addMagicTag() {
Hidden h = new Hidden(getModel().getMagicTagName()); Hidden h = new Hidden(getModel().getMagicTagName());
@ -488,30 +467,30 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Traverses the components contained in this form, collecting * Traverses the components contained in this form, collecting parameterModels and Listeners
* parameterModels and Listeners into this form's FormModel. * into this form's FormModel.
*/ */
protected void traverse() { protected void traverse() {
Traversal formRegistrar = new Traversal() { Traversal formRegistrar = new Traversal() {
@Override
protected void act(Component c) { @Override
if ( c == Form.this ) { protected void act(Component c) {
return; if (c == Form.this) {
} return;
if ( c instanceof Form ) {
throw new IllegalStateException
("Forms cannot contain other Forms");
}
c.register(Form.this, getModel());
} }
}; if (c instanceof Form) {
throw new IllegalStateException("Forms cannot contain other Forms");
}
c.register(Form.this, getModel());
}
};
formRegistrar.preorder(this); formRegistrar.preorder(this);
} }
/** /**
* Adds this form to the page and traverses the components contained in * Adds this form to the page and traverses the components contained in this form, collecting
* this form, collecting parameterModels and Listeners into this form's * parameterModels and Listeners into this form's FormModel.
* FormModel.
* *
* @param p page in which to register this form * @param p page in which to register this form
*/ */
@ -522,7 +501,8 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* TODO * TODO
*
* @param model * @param model
*/ */
public void excludeParameterFromExport(ParameterModel model) { public void excludeParameterFromExport(ParameterModel model) {
@ -530,26 +510,27 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Initialize <code>m_formData</code> so that accessing the per-request * Initialize <code>m_formData</code> so that accessing the per-request form data forces the
* form data forces the form to be processed on the first access and * form to be processed on the first access and caches the form data for subsequent requests.
* caches the form data for subsequent requests.
*/ */
private void initFormData() { private void initFormData() {
m_formData = new RequestLocal() { m_formData = new RequestLocal() {
@Override
protected Object initialValue(PageState s) { @Override
protected Object initialValue(PageState s) {
// TODO: We need to come up with the right strategy for // TODO: We need to come up with the right strategy for
// how we deal with FormProcessExceptions. Are they fatal // how we deal with FormProcessExceptions. Are they fatal
// ? Do we just add them to the form validation errors ? // ? Do we just add them to the form validation errors ?
try { try {
return process(s); return process(s);
} catch (FormProcessException e) { } catch (FormProcessException e) {
s_log.error("Form Process exception", e); s_log.error("Form Process exception", e);
throw new UncheckedWrapperException("Form Process error: " throw new UncheckedWrapperException("Form Process error: "
+ e.getMessage(), e); + e.getMessage(), e);
}
} }
}; }
};
} }
/** /**
@ -559,20 +540,19 @@ public class Form extends FormSection implements BebopConstants {
*/ */
@Override @Override
public String toString() { public String toString() {
return super.toString() + " " + return super.toString() + " " + "[" + getName() + "," + getAction() + "," + getMethod()
"[" + getName() + "," + getAction() + "," + getMethod() + "," + + "," + isRedirecting() + "]";
isRedirecting() + "]";
} }
/** /**
* Protected access to set the formdata request local. * Protected access to set the formdata request local. This method is required if a subclass
* This method is required if a subclass wishes to override the * wishes to override the process method.
* process method. *
*
* @param state * @param state
* @param data * @param data
*/ */
protected void setFormData(PageState state, FormData data) { protected void setFormData(PageState state, FormData data) {
m_formData.set(state, data); m_formData.set(state, data);
} }
} }