Last part of ThemeDirector code fix. Errors in validation of input fields is now displayed along the respective input field. Fixed uncought exception, unclosed result set, various missing globalization.

git-svn-id: https://svn.libreccm.org/ccm/trunk@2682 8810af33-2d31-482b-a856-94f89814c4df
master
pb 2014-06-09 22:25:17 +00:00
parent 6c272debdd
commit a7119bb541
34 changed files with 817 additions and 617 deletions

View File

@ -94,7 +94,7 @@ public class DecisionTreeTargetEditForm extends Form
/** /**
* Constructor. * Constructor.
* *
* @param selArticle the current article * @param selTree
* @param selTarget the current section * @param selTarget the current section
* @param container container which this form is added to * @param container container which this form is added to
*/ */
@ -138,6 +138,7 @@ public class DecisionTreeTargetEditForm extends Form
/** /**
* Returns the save/cancel section from this form. * Returns the save/cancel section from this form.
* @return
*/ */
public SaveCancelSection getSaveCancelSection() { public SaveCancelSection getSaveCancelSection() {
return m_saveCancelSection; return m_saveCancelSection;
@ -213,7 +214,9 @@ public class DecisionTreeTargetEditForm extends Form
} }
/** Form initialisation hook. Sets the options for select widgets. /** Form initialisation hook. Sets the options for select widgets.
* @param fse
*/ */
@Override
public void init(FormSectionEvent fse) { public void init(FormSectionEvent fse) {
PageState state = fse.getPageState(); PageState state = fse.getPageState();
FormData data = fse.getFormData(); FormData data = fse.getFormData();
@ -276,6 +279,7 @@ public class DecisionTreeTargetEditForm extends Form
try { try {
m_targetSectionWidget.addPrintListener(new PrintListener() { m_targetSectionWidget.addPrintListener(new PrintListener() {
@Override
public void prepare(PrintEvent e) { public void prepare(PrintEvent e) {
initTargetOptions(e); initTargetOptions(e);
} }
@ -287,6 +291,7 @@ public class DecisionTreeTargetEditForm extends Form
add(m_targetSectionWidget); add(m_targetSectionWidget);
addValidationListener(new FormValidationListener() { addValidationListener(new FormValidationListener() {
@Override
public final void validate(final FormSectionEvent event) public final void validate(final FormSectionEvent event)
throws FormProcessException { throws FormProcessException {
final PageState state = event.getPageState(); final PageState state = event.getPageState();
@ -312,7 +317,10 @@ public class DecisionTreeTargetEditForm extends Form
/** /**
* Called on form submission. Check to see if the user clicked the * Called on form submission. Check to see if the user clicked the
* cancel button. If they did, don't continue with the form. * cancel button. If they did, don't continue with the form.
* @param event
* @throws com.arsdigita.bebop.FormProcessException
*/ */
@Override
public void submitted(FormSectionEvent event) public void submitted(FormSectionEvent event)
throws FormProcessException { throws FormProcessException {
PageState state = event.getPageState(); PageState state = event.getPageState();
@ -332,7 +340,10 @@ public class DecisionTreeTargetEditForm extends Form
/** /**
* Called after form has been validated. Create the new * Called after form has been validated. Create the new
* DecisionTreeOptionTarget and assign it to the current DecisionTree. * DecisionTreeOptionTarget and assign it to the current DecisionTree.
* @param event
* @throws com.arsdigita.bebop.FormProcessException
*/ */
@Override
public void process(FormSectionEvent event) throws FormProcessException { public void process(FormSectionEvent event) throws FormProcessException {
PageState state = event.getPageState(); PageState state = event.getPageState();
FormData data = event.getFormData(); FormData data = event.getFormData();

View File

@ -238,32 +238,43 @@ public class Form extends FormSection implements BebopConstants {
return form; return form;
} }
/**
*
* @param ps
* @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 errors.addAttribute("message",
("message",
(String)((GlobalizedMessage) it.next()).localize (String)((GlobalizedMessage) it.next()).localize
(ps.getRequest())); (ps.getRequest())
);
errors.addAttribute("id", getName()); errors.addAttribute("id", getName());
} }
} }
/** /**
* <p>Determine whether or not this Form will redirect after its * <p>Determine whether or not this Form will redirect after its
* process listeners are fired.</p> * process listeners are fired.</p>
*
* @return
*/ */
public boolean isRedirecting() { public boolean isRedirecting() {
return m_isRedirecting; return m_isRedirecting;
} }
/** /**
* <p>Setting the redirecting flag will cause the Form to clear * Setting the redirecting flag will cause the Form to clear the
* the control event and redirect back to the current URL, after * control event and redirect back to the current URL, after
* firing all process listeners. Doing so means that a user * firing all process listeners. Doing so means that a user reload
* reload will not cause the form to be resubmitted. The default * will not cause the form to be resubmitted. The default value for
* value for this flag is false.</p> * this flag is false.
*
* @param isRedirecting
*/ */
public void setRedirecting(boolean isRedirecting) { public void setRedirecting(boolean isRedirecting) {
Assert.isUnlocked(this); Assert.isUnlocked(this);
@ -273,10 +284,12 @@ 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 <code>state</code>. * given in <code>state</code>.
* @see #process process(...)
* *
* @param state represents the current request * @param state represents the current request
* @see #process process(...) * @throws javax.servlet.ServletException
*/ */
@Override
public void respond(PageState state) throws ServletException { public void respond(PageState state) throws ServletException {
final FormData data = process(state); final FormData data = process(state);
@ -420,15 +433,16 @@ public class Form extends FormSection implements BebopConstants {
* right set of init, validation, and process listeners, depending on * right set of init, validation, and process listeners, depending on
* whether this is an initial request to the form and whether the form * whether this is an initial request to the form and whether the form
* submission was valid. Submission listeners are always run. * submission was valid. Submission listeners are always run.
* @see #getFormData
* *
* @param state represents the current request * @param state represents the current request
*
* @return the values extracted from the HTTP request contained * @return the values extracted from the HTTP request contained
* in <code>state</code>. * in <code>state</code>.
* @see #getFormData * @throws com.arsdigita.bebop.FormProcessException
* @pre state != null * @pre state != null
* @post return != null * @post return != null
*/ */
@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());
@ -476,6 +490,7 @@ public class Form extends FormSection implements BebopConstants {
*/ */
protected void traverse() { protected void traverse() {
Traversal formRegistrar = new Traversal() { Traversal formRegistrar = new Traversal() {
@Override
protected void act(Component c) { protected void act(Component c) {
if ( c == Form.this ) { if ( c == Form.this ) {
return; return;
@ -497,6 +512,7 @@ public class Form extends FormSection implements BebopConstants {
* *
* @param p page in which to register this form * @param p page in which to register this form
*/ */
@Override
public void register(Page p) { public void register(Page p) {
traverse(); traverse();
p.addComponent(this); p.addComponent(this);
@ -504,6 +520,7 @@ public class Form extends FormSection implements BebopConstants {
/** /**
* TODO * TODO
* @param model
*/ */
public void excludeParameterFromExport(ParameterModel model) { public void excludeParameterFromExport(ParameterModel model) {
getModel().excludeFormParameterFromExport(model); getModel().excludeFormParameterFromExport(model);
@ -516,6 +533,7 @@ public class Form extends FormSection implements BebopConstants {
*/ */
private void initFormData() { private void initFormData() {
m_formData = new RequestLocal() { m_formData = new RequestLocal() {
@Override
protected Object initialValue(PageState s) { 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
@ -524,7 +542,8 @@ public class Form extends FormSection implements BebopConstants {
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: " + e.getMessage(), e); throw new UncheckedWrapperException("Form Process error: "
+ e.getMessage(), e);
} }
} }
}; };
@ -535,6 +554,7 @@ public class Form extends FormSection implements BebopConstants {
* *
* @return a human-readable representation of <code>this</code>. * @return a human-readable representation of <code>this</code>.
*/ */
@Override
public String toString() { public String toString() {
return super.toString() + " " + return super.toString() + " " +
"[" + getName() + "," + getAction() + "," + getMethod() + "," + "[" + getName() + "," + getAction() + "," + getMethod() + "," +
@ -542,10 +562,13 @@ public class Form extends FormSection implements BebopConstants {
} }
/** /**
* Protected access to set the formdata request local * Protected access to set the formdata request local.
* This method is required if a subclass wishes to override the
* process method.
*
* @param state
* @param data
*/ */
// this method is required if a subclass wishes to override
// the process method
protected void setFormData(PageState state, FormData data) { protected void setFormData(PageState state, FormData data) {
m_formData.set(state, data); m_formData.set(state, data);
} }

View File

@ -36,9 +36,8 @@ import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.util.URLRewriter; import com.arsdigita.util.URLRewriter;
/** /**
* Manages the data associated with forms and * Manages the data associated with forms and other remote sources.
* other remote sources.
*
* <p>The basic task of a <code>FormData</code> object is to transform * <p>The basic task of a <code>FormData</code> object is to transform
* a set of key-value string pairs into a validated set of Java data * a set of key-value string pairs into a validated set of Java data
* objects for use in subsequent processing. In most cases the original * objects for use in subsequent processing. In most cases the original
@ -81,8 +80,8 @@ public class FormData implements Map, Cloneable {
private boolean m_isSubmission; private boolean m_isSubmission;
/** /**
* Ensure that no one can create this object from outside the * Ensure that no one can create this object from outside the package
* package without supplying meaningful parameters * without supplying meaningful parameters
*/ */
private FormData() {} private FormData() {}
@ -277,9 +276,8 @@ public class FormData implements Map, Cloneable {
} }
/** /**
* Validates this <code>FormData</code> object according to its * Validates this <code>FormData</code> object according to its form model.
* form model. If the <code>FormData</code> is already valid, * If the <code>FormData</code> is already valid, does nothing.
* does nothing.
* *
* @param state describes the current page state * @param state describes the current page state
* @pre state != null * @pre state != null
@ -300,9 +298,8 @@ public class FormData implements Map, Cloneable {
} }
/** /**
* Validates this <code>FormData</code> object against its * Validates this <code>FormData</code> object against its form model,
* form model, regardless of whether the object is currently * regardless of whether the object is currently valid.
* valid.
* *
* @param state describes the current page state * @param state describes the current page state
* @pre state != null * @pre state != null
@ -325,6 +322,7 @@ public class FormData implements Map, Cloneable {
/** /**
* Reports a validation error on the form as a whole. * Reports a validation error on the form as a whole.
* Uses a GlobalizedMessage for inklusion
* *
* @param message the error message * @param message the error message
* @pre message != null * @pre message != null

View File

@ -52,16 +52,17 @@ import javax.xml.parsers.ParserConfigurationException;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
* <p> * The top-level container for all Bebop components and containers.
* The top-level container for all Bebop components and containers. </p>
* *
* <UL> <LI>Holds references to the components of a page.</LI> <LI>Provides methods for servicing * <UL>
* requests and for notifying other components that a request for this page has been received * <LI>Holds references to the components of a page.</LI>
* through {@link ActionListener ActionListeners}.</LI> * <LI>Provides methods for servicing requests and for notifying other
* components that a request for this page has been received through
* {@link ActionListener ActionListeners}.</LI>
* <LI>Tracks request parameters for stateful components, such as tabbed panes
* and sortable tables.</LI>
* </UL>
* *
* <LI>Tracks request parameters for stateful components, such as tabbed panes and sortable
* tables.</LI> </UL>
* <p>
* A typical <code>Page</code> may be created as follows: null <blockquote><pre><code> * A typical <code>Page</code> may be created as follows: null <blockquote><pre><code>
* Page p = new Page("Hello World"); * Page p = new Page("Hello World");
* p.add(new Label("Hello World"); * p.add(new Label("Hello World");
@ -114,9 +115,10 @@ public class Page extends BlockStylable implements Container {
*/ */
static final String INVISIBLE = INTERNAL + "i"; static final String INVISIBLE = INTERNAL + "i";
/** /**
* Map of stateful components (id --> Component) SortedMap used because component based hash for * Map of stateful components (id --> Component) SortedMap used because
* page is based on concatenation of component ids, and so need to guarantee that they are * component based hash for page is based on concatenation of component ids,
* returned in the same order for the same page - cg. * and so need to guarantee that they are returned in the same order for
* the same page - cg.
*/ */
private SortedMap m_componentMap; private SortedMap m_componentMap;
private List m_components; private List m_components;
@ -132,13 +134,14 @@ public class Page extends BlockStylable implements Container {
private List m_actionListeners; private List m_actionListeners;
private List m_requestListeners; private List m_requestListeners;
/** /**
* The title of the page to be added in the head of HTML output. The title is wrapped in a Label * The title of the page to be added in the head of HTML output. The title
* to allow developers to add PrintListeners to dynamically change the value of the title. * is wrapped in a Label to allow developers to add PrintListeners to
* dynamically change the value of the title.
*/ */
private Label m_title; private Label m_title;
/** /**
* Stores the actual title for the current request. The title may be generated with a * Stores the actual title for the current request. The title may be
* PrintListener of the m_title Label. * generated with a PrintListener of the m_title Label.
*/ */
private RequestLocal m_currentTitle; private RequestLocal m_currentTitle;
/** /**
@ -172,54 +175,58 @@ public class Page extends BlockStylable implements Container {
private boolean m_useHttpSession = false; private boolean m_useHttpSession = false;
/** /**
* Returns <code>true</code> if this page should export state through the HttpSession instead of * Returns <code>true</code> if this page should export state through the
* the URL query string. * HttpSession instead of the URL query string.
* <P> * <P>
* If this returns <code>true</code>, then PageState.stateAsURL() will only export the control * If this returns <code>true</code>, then PageState.stateAsURL() will only
* event as a URL query string. If this returns <code>false</code>, then stateAsURL() will * export the control event as a URL query string. If this returns
* export the entire page state. * <code>false</code>, then stateAsURL() will export the entire page state.
*
*
* @see PageState#stateAsURL * @see PageState#stateAsURL
* *
* @return <code>true</code> if this page should export state through the HttpSession; * @return <code>true</code> if this page should export state through the
* <code>false</code> if it should export using the URL query string. * HttpSession;
* <code>false</code> if it should export using the URL query
* string.
*/ */
public boolean isUsingHttpSession() { public boolean isUsingHttpSession() {
return m_useHttpSession; return m_useHttpSession;
} }
/** /**
* Indicates to this page whether it should export its entire state to subsequent requests * Indicates to this page whether it should export its entire state to
* through the URL query string, or if it should use the HttpSession instead and only use the * subsequent requests through the URL query string, or if it should use the
* URL query string for the control event. * HttpSession instead and only use the URL query string for the control
* event.
* *
* @see PageState#stateAsURL * @see PageState#stateAsURL
* *
* @param b <code>true</code> if PageState.stateAsURL() will export only the control event as a * @param b <code>true</code> if PageState.stateAsURL() will export only the
* URL query string. <code>false</code> if stateAsURL() will export the entire page * control event as a URL query string. <code>false</code> if stateAsURL()
* state. * will export the entire page state.
*/ */
public void setUsingHttpSession(boolean b) { public void setUsingHttpSession(boolean b) {
m_useHttpSession = b; m_useHttpSession = b;
} }
// ////////////////////////////////////////////////////////////////////////
// Constructor Section
// ////////////////////////////////////////////////////////////////////////
/** /**
* Creates an empty page with the specified title and panel. * Constructor, creates an empty page with the specified title and panel.
* *
* @param title title for this page * @param title title for this page
*
* @param panel container for this page * @param panel container for this page
* @deprecated use Page(Lab el, Container) instead.
*/ */
public Page(String title, Container panel) { public Page(String title, Container panel) {
this(new Label(title), panel); this(new Label(title), panel);
} }
/** /**
* Creates an empty page with the specified title and panel. * Constructor, creates an empty page with the specified title and panel.
*
* @param title title for this page
* *
* @param title title for this page as (globalized) Label
* @param panel container for this page * @param panel container for this page
*/ */
public Page(Label title, Container panel) { public Page(Label title, Container panel) {
@ -231,7 +238,7 @@ public class Page extends BlockStylable implements Container {
m_components = new ArrayList(); m_components = new ArrayList();
m_componentMap = new TreeMap(); m_componentMap = new TreeMap();
setErrorDisplay(new PageErrorDisplay()); setErrorDisplay(new PageErrorDisplay());
setTitle(title); m_title = title;
// Initialize the RequestLocal where the title for the current // Initialize the RequestLocal where the title for the current
// request will be kept // request will be kept
@ -270,7 +277,8 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Creates an empty page with the specified title and implicit BoxPanel container. * Creates an empty page with the specified title and implicit BoxPanel
* container.
* *
* @param title title for this page * @param title title for this page
*/ */
@ -281,7 +289,8 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Creates an empty page with the specified title and implicit BoxPanel container. * Creates an empty page with the specified title and implicit BoxPanel
* container.
* *
* @param title title for this page * @param title title for this page
*/ */
@ -301,13 +310,13 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Adds a component with the specified layout constraints to this container. Layout constraints * Adds a component with the specified layout constraints to this container.
* are defined in each layout container as static ints. To specify multiple constraints, use * Layout constraints are defined in each layout container as static ints.
* bitwise OR. * To specify multiple constraints, use bitwise OR.
* *
* @param c component to add to this container * @param c component to add to this container
* * @param constraints layout constraints (a bitwise OR of static ints in the
* @param constraints layout constraints (a bitwise OR of static ints in the particular layout) * particular layout)
*/ */
@Override @Override
public void add(Component c, int constraints) { public void add(Component c, int constraints) {
@ -315,17 +324,20 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Returns <code>true</code> if this list contains the specified element. More formally, returns * Returns <code>true</code> if this list contains the specified element.
* <code>true</code> if and only if this list contains at least one element e such that (o==null * More formally, returns <code>true</code> if and only if this list
* ? e==null : o.equals(e)). * contains at least one element e such that (o==null ? e==null :
* o.equals(e)).
* <P> * <P>
* This method returns <code>true</code> only if the component has been directly added to this * This method returns <code>true</code> only if the component has been
* container. If this container contains another container that contains this component, this * directly added to this container. If this container contains another
* method returns <code>false</code>. * container that contains this component, this method returns
* <code>false</code>.
* *
* @param o element whose presence in this container is to be tested * @param o element whose presence in this container is to be tested
* *
* @return <code>true</code> if this Container contains the specified component directly; * @return <code>true</code> if this Container contains the specified
* component directly;
* <code>false</code> otherwise. * <code>false</code> otherwise.
*/ */
@Override @Override
@ -334,12 +346,12 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Returns the component at the specified position. Each call to the add method increments the * Returns the component at the specified position. Each call to the add
* index. Since the user has no control over the index of added components (other than counting * method increments the index. Since the user has no control over the index
* each call to add), this method should be used in conjunction with indexOf. * of added components (other than counting each call to add), this method
* should be used in conjunction with indexOf.
* *
* @param index the index of the item to be retrieved from this Container * @param index the index of the item to be retrieved from this Container
*
* @return the component at the specified position in this container. * @return the component at the specified position in this container.
*/ */
@Override @Override
@ -351,14 +363,12 @@ public class Page extends BlockStylable implements Container {
* Gets the index of a component. * Gets the index of a component.
* *
* @param c component to search for * @param c component to search for
* * @return the index in this list of the first occurrence of the specified
* @return the index in this list of the first occurrence of the specified element, or -1 if * element, or -1 if this list does not contain this element.
* this list does not contain this element.
* *
* @pre c != null * @pre c != null
* @post contains(c) implies (return >= 0) && (return < size()) * @post contains(c) implies (return >= 0) && (return < size())
* @pos * @post !contains(c) implies return == -1
* t !contains(c) implies return == -1
*/ */
@Override @Override
public int indexOf(Component c) { public int indexOf(Component c) {
@ -368,8 +378,8 @@ public class Page extends BlockStylable implements Container {
/** /**
* Returns <code>true</code> if the container contains no components. * Returns <code>true</code> if the container contains no components.
* *
* @return <code>true</code> if this container contains no * * components; <code>false</code> * @return <code>true</code> if this container contains no components;
* otherwise. * <code>false</code> otherwise.
*/ */
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
@ -377,8 +387,9 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Returns the number of elements in this container. This does not recursively count the * Returns the number of elements in this container. This does not
* components that are indirectly contained in this container. * recursively count the components that are indirectly contained in this
* container.
* *
* @return the number of components directly in this container. * @return the number of components directly in this container.
*/ */
@ -393,7 +404,8 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Returns the panel that the <code>Page</code> uses for rendering its components. * Returns the panel that the <code>Page</code> uses for rendering its
* components.
* *
* @return the panel. * @return the panel.
*/ */
@ -402,9 +414,11 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Set the Container used for rendering components on this page. Caution should be used with * Set the Container used for rendering components on this page. Caution
* this function, as the existing container is simply overwritten. * should be used with this function, as the existing container is simply
* overwritten.
* *
* @param c
* @author Matthew Booth (mbooth@redhat.com) * @author Matthew Booth (mbooth@redhat.com)
*/ */
public void setPanel(Container c) { public void setPanel(Container c) {
@ -412,8 +426,7 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* <p> * Retrieves the title of this page.
* Retrieves the title of this page. </p>
* *
* @return the static title of this page. * @return the static title of this page.
*/ */
@ -425,7 +438,6 @@ public class Page extends BlockStylable implements Container {
* Retrieves the title of this page as a Bebop label component. * Retrieves the title of this page as a Bebop label component.
* *
* @param state the state of the current request * @param state the state of the current request
*
* @return the title of the page for the current request. * @return the title of the page for the current request.
*/ */
public final Label getTitle(PageState state) { public final Label getTitle(PageState state) {
@ -453,12 +465,13 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Sets the {@link Component} that will display the validation errors in the current * Sets the {@link Component} that will display the validation errors in the
* {@link PageState}. Any validation error in the <code>PageState</code> will cause the * current {@link PageState}. Any validation error in the
* <code>Page</code> to completely ignore all other components and render only the error display * <code>PageState</code> will cause the <code>Page</code> to completely
* component. * ignore all other components and render only the error display component.
* <p> * <p>
* By default, a {@link PageErrorDisplay} component is used to display the validation errors. * By default, a {@link PageErrorDisplay} component is used to display the
* validation errors.
* *
* @param c the component that will display the validation errors in the current * @param c the component that will display the validation errors in the current
* <code>PageState</code> * <code>PageState</code>
@ -469,12 +482,13 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Gets the {@link Component} that will display the validation errors in the current * Gets the {@link Component} that will display the validation errors in the
* {@link PageState}. Any validation error in the <code>PageState</code> will cause the * current {@link PageState}. Any validation error in the
* <code>Page</code> to completely ignore all other components and render only the error display * <code>PageState</code> will cause the <code>Page</code> to completely
* component. * ignore all other components and render only the error display component.
* <p> * <p>
* By default, a {@link PageErrorDisplay} component is used to display the validation errors. * By default, a {@link PageErrorDisplay} component is used to display the
* validation errors.
* *
* @return the component that will display the validation errors in the current * @return the component that will display the validation errors in the current
* <code>PageState</code>. * <code>PageState</code>.
@ -484,17 +498,19 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Adds a client-side stylesheet that should be used in HTML output. Arbitrarily many * Adds a client-side stylesheet that should be used in HTML output.
* client-side stylesheets can be added with this method. To use a CSS stylesheet, call * Arbitrarily many client-side stylesheets can be added with this method.
* something like <code>setStyleSheet("style.css", "text/css")</code>. * To use a CSS stylesheet, call something like
* <code>setStyleSheet("style.css", "text/css")</code>.
* *
* <p> * <p>
* These values will ultimately wind up in a <tt>&lt;link&gt;</tt> * These values will ultimately wind up in a <tt>&lt;link&gt;</tt>
* tag in the head of the HTML page. * tag in the head of the HTML page.
* *
* <p> * <p>
* Note that the stylesheet set with this call has nothing to do with the XSLT stylesheet * Note that the stylesheet set with this call has nothing to do with the
* (transformer) that is applied to the XML generated from this page! * XSLT stylesheet (transformer) that is applied to the XML generated from
* this page!
* *
* @param styleSheetURI the location of the stylesheet * @param styleSheetURI the location of the stylesheet
* @param mimeType the MIME type of the stylesheet, usually * @param mimeType the MIME type of the stylesheet, usually
@ -507,19 +523,17 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Adds a global state parameter to this page. Global parameters are values that need to be * Adds a global state parameter to this page. Global parameters are values
* preserved between requests, but that have no special connection to any of the components on * that need to be preserved between requests, but that have no special
* the page. For a page that displays details about an item, a global parameter would be used to * connection to any of the components on the page. For a page that displays
* identify the item. * details about an item, a global parameter would be used to identify the
* * item.
* <p>
* If the parameter was previously added as a component state parameter, its name is unmangled
* and stays unmangled.
*
* @param p the global parameter to add
* *
* If the parameter was previously added as a component state parameter, its
* name is unmangled and stays unmangled.
* @see #addComponentStateParam * @see #addComponentStateParam
* *
* @param p the global parameter to add
* @pre ! isLocked() * @pre ! isLocked()
* @pre parameter != null * @pre parameter != null
*/ */
@ -530,8 +544,8 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Constructs the top nodes of the DOM or JDOM tree. Used by generateXML(PageState, Document) * Constructs the top nodes of the DOM or JDOM tree. Used by
* below. * generateXML(PageState, Document) below.
* <p> * <p>
* Generates DOM fragment: * Generates DOM fragment:
* <pre> * <pre>
@ -541,12 +555,13 @@ public class Page extends BlockStylable implements Container {
* ... page content gnerated by children ... * ... page content gnerated by children ...
* &lt;/bebop:page></pre> The content of the <tt>&lt;title&gt;</tt> * &lt;/bebop:page></pre> The content of the <tt>&lt;title&gt;</tt>
* element can be set by calling {@link #setTitle setTitle}. The * element can be set by calling {@link #setTitle setTitle}. The
* <tt>&lt;stylesheet&gt;</tt> element will only be present if a stylesheet has been set with {@link * <tt>&lt;stylesheet&gt;</tt> element will only be present if a stylesheet
* has been set with {@link
* #setStyleSheet setStyleSheet}. * #setStyleSheet setStyleSheet}.
* *
* @param ps the page state for the current page * @param ps the page state for the current page
* @param parent the DOM node for the whole Document * @param parent the DOM node for the whole Document
* * @return
* @pre isLocked() * @pre isLocked()
*/ */
protected Element generateXMLHelper(PageState ps, Document parent) { protected Element generateXMLHelper(PageState ps, Document parent) {
@ -569,8 +584,9 @@ public class Page extends BlockStylable implements Container {
} }
/** /**
* Constructs a DOM or JDOM tree with all components on the page. The tree represents the page * Constructs a DOM or JDOM tree with all components on the page. The tree
* that results from the {@link javax.servlet.http.HttpServletRequest} kept in the * represents the page that results from the
* {@link javax.servlet.http.HttpServletRequest} kept in the
* <code>state</code>. * <code>state</code>.
* *
* @param state the page state produced by {@link #process} * @param state the page state produced by {@link #process}
@ -611,19 +627,25 @@ public class Page extends BlockStylable implements Container {
/** /**
* Do nothing. Top-level add nodes is meaningless. * Do nothing. Top-level add nodes is meaningless.
*
* @param elt
*/ */
@Override @Override
public void generateXML(PageState state, Element elt) { public void generateXML(PageState state, Element elt) {
} }
/** /**
* Creates a PageState object and processes it by calling the respond method on the selected * Creates a PageState object and processes it by calling the respond method
* component. Processes a request by notifying the component from which the process originated * on the selected component. Processes a request by notifying the component
* and {@link #fireActionEvent * from which the process originated and {@link #fireActionEvent
* broadcasts} an {@link ActionEvent} to all the listeners that registered with * broadcasts} an {@link ActionEvent} to all the listeners that registered
* {@link #addActionListener addActionListener}. * with {@link #addActionListener addActionListener}.
*
* @see #generateXML(PageState,Document) generateXML * @see #generateXML(PageState,Document) generateXML
*
* @param request
* @param response
* @return
* @throws javax.servlet.ServletException
* @pre isLocked() * @pre isLocked()
* @pre request != null * @pre request != null
* @pre response != null * @pre response != null
@ -675,26 +697,14 @@ public class Page extends BlockStylable implements Container {
} }
} }
// /**
// * Does all the servicing of a request except output generation.
// * This includes most of the common duties of buildDocument and print.
// *
// * @deprecated Use {@link
// * #process(HttpServletRequest,HttpServletResponse)} instead.
// */
// protected PageState prepare(HttpServletRequest req, HttpServletResponse res)
// throws ServletException {
// Assert.isLocked(this);
// PageState state = process(req, res);
// return state;
// }
/** /**
* Builds a DOM Document from the current request state by doing a depth-first tree walk on the * Builds a DOM Document from the current request state by doing a
* current set of components in this Page, calling generateXML on each. Does NOT do the * depth-first tree walk on the current set of components in this Page,
* rendering. If the HTTP response has already been committed, does not build the XML document. * calling generateXML on each. Does NOT do the rendering. If the HTTP
* * response has already been committed, does not build the XML document.
* @return a DOM ready for rendering, or null if the response has already been committed.
* *
* @return a DOM ready for rendering, or null if the response has already
* been committed.
* @post res.isCommitted() == (return == null) * @post res.isCommitted() == (return == null)
*/ */
public Document buildDocument(HttpServletRequest req, public Document buildDocument(HttpServletRequest req,

View File

@ -55,7 +55,8 @@ public class FormSectionEvent extends PageEvent {
* @param state the state of the enclosing page * @param state the state of the enclosing page
* @param formData the form data constructed so far * @param formData the form data constructed so far
*/ */
public FormSectionEvent(Object source, PageState state, public FormSectionEvent(Object source,
PageState state,
FormData formData) { FormData formData) {
super(source, state); super(source, state);
_formData = formData; _formData = formData;

View File

@ -29,7 +29,6 @@ import java.util.EventListener;
* @author Uday Mathur * @author Uday Mathur
* @version $Id: FormValidationListener.java 287 2005-02-22 00:29:02Z sskracic $ * @version $Id: FormValidationListener.java 287 2005-02-22 00:29:02Z sskracic $
*/ */
public interface FormValidationListener extends EventListener { public interface FormValidationListener extends EventListener {
/** /**
@ -49,23 +48,14 @@ public interface FormValidationListener extends EventListener {
* be handled internally, or if they are unrecoverable may be * be handled internally, or if they are unrecoverable may be
* rethrown as instances of <code>FormProcessException</code>. * rethrown as instances of <code>FormProcessException</code>.
* *
* @param model The form model describing the structure and properties * @param e FormSectionEvent containing the FormData as well as the
* of the form data included with this request. The validation procedure * PageState.
* may require knowledge of form or parameter properties to complete. * Clients may access the PageState by executing something like
* PageState state = fse.getPageState();
* Method getFormData() allows access to the Form's data.
* *
* @param data The container for all data objects associated with * @exception FormProcessException ff the data does not pass the check.
* the request. All parameters specified in the form model are */
* converted to data objects and stored in this container before
* any form validation procedures are called.
*
* @param request The HTTP request information from which the form
* data was extracted. Note that the request object is supplied
* only in case the validation procedure involves contextual
* information (information extracted from cookies or the peer
* address, for example).
*
* @exception FormProcessException If the data does not pass the
* check. */
void validate(FormSectionEvent e) throws FormProcessException; void validate(FormSectionEvent e) throws FormProcessException;

View File

@ -22,8 +22,8 @@ import com.arsdigita.bebop.FormProcessException;
import java.util.EventListener; import java.util.EventListener;
/** /**
* Defines the interface * Defines the interface for a class that validates the values of a
* for a class that validates the values of a single parameter. * single parameter.
* *
* @author Karl Goldstein * @author Karl Goldstein
* @author Uday Mathur * @author Uday Mathur

View File

@ -18,7 +18,6 @@
*/ */
package com.arsdigita.bebop.form; package com.arsdigita.bebop.form;
import com.arsdigita.bebop.event.PageEvent; import com.arsdigita.bebop.event.PageEvent;
import com.arsdigita.bebop.event.PrintEvent; import com.arsdigita.bebop.event.PrintEvent;
import com.arsdigita.bebop.event.PrintListener; import com.arsdigita.bebop.event.PrintListener;
@ -41,12 +40,12 @@ import java.util.TooManyListenersException;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
* Search and select Bebop widget. This widget is used to allow a user to * Search and select Bebop widget. This widget is used to allow a user to search
* search for a particular item over a potentially very large set. Depending * for a particular item over a potentially very large set. Depending on the
* on the size of the dataset, the user will either see a search box or a * size of the dataset, the user will either see a search box or a selection box
* selection box (with all valid items). The search box will then change to * (with all valid items). The search box will then change to a selection box
* a selection box once the user submits the form, allowing them then to * once the user submits the form, allowing them then to choose the items they
* choose the items they desire. * desire.
* <p> * <p>
* The datasource for SearchAndSelect is provided by an implentation of the * The datasource for SearchAndSelect is provided by an implentation of the
* SearchAndSelectModel interface. SAMPLE IMPLEMENTATION GOES HERE * SearchAndSelectModel interface. SAMPLE IMPLEMENTATION GOES HERE
@ -58,8 +57,8 @@ import org.apache.log4j.Logger;
public class SearchAndSelect extends FormSection public class SearchAndSelect extends FormSection
implements BebopConstants, PrintListener { implements BebopConstants, PrintListener {
private static final Logger s_cat = private static final Logger s_cat
Logger.getLogger(SearchAndSelect.class); = Logger.getLogger(SearchAndSelect.class);
protected String m_name; protected String m_name;
// name of this super-widget // name of this super-widget
@ -120,8 +119,8 @@ public class SearchAndSelect extends FormSection
if (m_useCheckboxes) { if (m_useCheckboxes) {
m_outputSelectWidget = new CheckboxGroup(getName() + ".select"); m_outputSelectWidget = new CheckboxGroup(getName() + ".select");
} else { } else {
m_outputSelectWidget = m_outputSelectWidget
new MultipleSelect(getName() + ".select"); = new MultipleSelect(getName() + ".select");
} }
} else { } else {
m_outputSelectWidget = new SingleSelect(getName() + ".select"); m_outputSelectWidget = new SingleSelect(getName() + ".select");
@ -148,11 +147,11 @@ public class SearchAndSelect extends FormSection
m_results.setQuery(m_query); m_results.setQuery(m_query);
if ( m_isSearchLocked || if (m_isSearchLocked
( ( ( ! m_oldValue.equals("") && || (((!m_oldValue.equals("")
m_oldValue.equals(m_value) ) || && m_oldValue.equals(m_value))
( m_maxViewableResults >= m_results.resultsCount() ) ) && || (m_maxViewableResults >= m_results.resultsCount()))
( m_results.resultsCount() > 0 ) ) ) { && (m_results.resultsCount() > 0))) {
OptionGroup outputWidget = (OptionGroup) e.getTarget(); OptionGroup outputWidget = (OptionGroup) e.getTarget();
@ -219,6 +218,7 @@ public class SearchAndSelect extends FormSection
* user as the error field is also used as a help field. * user as the error field is also used as a help field.
*/ */
super.addValidationListener(new FormValidationListener() { super.addValidationListener(new FormValidationListener() {
@Override
public void validate(FormSectionEvent e) { public void validate(FormSectionEvent e) {
FormData data = e.getFormData(); FormData data = e.getFormData();
@ -271,9 +271,9 @@ public class SearchAndSelect extends FormSection
* If search returns only one hit and is a non-optional single * If search returns only one hit and is a non-optional single
* select, it's done. * select, it's done.
*/ */
if ( ! m_isOptional && if (!m_isOptional
! m_isMultiple && && !m_isMultiple
( m_results.resultsCount() == 1 ) ) { && (m_results.resultsCount() == 1)) {
m_isSearchLocked = true; m_isSearchLocked = true;
m_value = m_results.getID(0); m_value = m_results.getID(0);
} }
@ -284,14 +284,14 @@ public class SearchAndSelect extends FormSection
*/ */
if (m_isSearchLocked) { if (m_isSearchLocked) {
if (!m_isMultiple) { if (!m_isMultiple) {
StringParameter param = StringParameter param
new StringParameter( getName() ); = new StringParameter(getName());
data.setParameter(getName(), data.setParameter(getName(),
new ParameterData(param, m_value)); new ParameterData(param, m_value));
} else { } else {
ArrayParameter param = ArrayParameter param
new ArrayParameter( getName() ); = new ArrayParameter(getName());
String[] tmpArray = (String[]) data String[] tmpArray = (String[]) data
.get(getName() + ".select"); .get(getName() + ".select");
@ -313,8 +313,7 @@ public class SearchAndSelect extends FormSection
}); });
} }
public final void setSearchAndSelectListener public final void setSearchAndSelectListener(SearchAndSelectListener listener) {
(SearchAndSelectListener listener) {
m_listener = listener; m_listener = listener;
} }
@ -347,9 +346,9 @@ public class SearchAndSelect extends FormSection
} }
/** /**
* Determine the type of HTML form element to create. * Determine the type of HTML form element to create. This will not
* This will not necessarily be accurate until generateWidget is called * necessarily be accurate until generateWidget is called as the query will
* as the query will be unavailable until that point. * be unavailable until that point.
* *
* @return "text" or "select" depending on the result size * @return "text" or "select" depending on the result size
*/ */
@ -391,9 +390,9 @@ public class SearchAndSelect extends FormSection
} }
/** /**
* Indicates if the widget is composed of multiple HTML elements. * Indicates if the widget is composed of multiple HTML elements. Always
* Always returns true, as the widget makes use of a hidden element and * returns true, as the widget makes use of a hidden element and another
* another element. * element.
* *
* @return true * @return true
*/ */
@ -402,8 +401,8 @@ public class SearchAndSelect extends FormSection
} }
/** /**
* Generates the XML datastructure for this widget. Adds a hidden, * Generates the XML datastructure for this widget. Adds a hidden, a
* a textbox, checkbox group, or select, and possibly some number of * textbox, checkbox group, or select, and possibly some number of
* formErrors. * formErrors.
* *
* @param state the state of the page * @param state the state of the page
@ -418,11 +417,11 @@ public class SearchAndSelect extends FormSection
return; return;
} }
if ( m_isSearchLocked || if (m_isSearchLocked
( ( ( ! m_oldValue.equals("") && || (((!m_oldValue.equals("")
m_oldValue.equals(m_value) ) || && m_oldValue.equals(m_value))
( m_maxViewableResults >= m_results.resultsCount() ) ) && || (m_maxViewableResults >= m_results.resultsCount()))
( m_results.resultsCount() > 0 ) ) ) { && (m_results.resultsCount() > 0))) {
m_outputSelectWidget.generateXML(state, parent); m_outputSelectWidget.generateXML(state, parent);
} else { } else {
m_outputTextWidget.generateXML(state, parent); m_outputTextWidget.generateXML(state, parent);
@ -437,9 +436,9 @@ public class SearchAndSelect extends FormSection
/** /**
* Generate the error messages for this widget. This widget has some * Generate the error messages for this widget. This widget has some
* specialized error messages, so it is necessary to override the * specialized error messages, so it is necessary to override the default
* default error generator. Basically, the m_results field won't be * error generator. Basically, the m_results field won't be available
* available outside this class, so this needs to be internal. * outside this class, so this needs to be internal.
* *
* @param state the state of the page * @param state the state of the page
* @param parent the parent widget * @param parent the parent widget
@ -458,13 +457,13 @@ public class SearchAndSelect extends FormSection
if ((curValue == null) || (curValue.equals(""))) { if ((curValue == null) || (curValue.equals(""))) {
error.addAttribute("message", error.addAttribute("message",
"Please enter a comma-delimited search"); "Please enter a comma-delimited search");
} else if ( ( ! m_oldValue.equals( curValue ) ) && } else if ((!m_oldValue.equals(curValue))
! m_isSearchLocked ) { && !m_isSearchLocked) {
error.addAttribute("message", error.addAttribute("message",
"Your search returned " + "Your search returned "
m_results.resultsCount() +" matches. " + + m_results.resultsCount() + " matches. "
"Please refine your search or leave the " + + "Please refine your search or leave the "
"search as it is to see all results."); + "search as it is to see all results.");
} }
} }
@ -472,8 +471,8 @@ public class SearchAndSelect extends FormSection
if (!curValue.equals("")) { if (!curValue.equals("")) {
Element error = parent.newChildElement("bebop:formErrors", BEBOP_XML_NS); Element error = parent.newChildElement("bebop:formErrors", BEBOP_XML_NS);
error.addAttribute("message", error.addAttribute("message",
"Your search returned no matches. Please " + "Your search returned no matches. Please "
"try again"); + "try again");
} else { } else {
Element error = parent.newChildElement("bebop:formErrors", BEBOP_XML_NS); Element error = parent.newChildElement("bebop:formErrors", BEBOP_XML_NS);
error.addAttribute("message", "WARNING -- NO DATA FOUND"); error.addAttribute("message", "WARNING -- NO DATA FOUND");

View File

@ -42,7 +42,7 @@ import com.arsdigita.bebop.parameters.ParameterModel;
import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.util.BebopConstants; import com.arsdigita.bebop.util.BebopConstants;
import com.arsdigita.globalization.GlobalizedMessage; import com.arsdigita.globalization.GlobalizedMessage;
import com.arsdigita.kernel.Kernel; // import com.arsdigita.kernel.Kernel;
import com.arsdigita.util.Assert; import com.arsdigita.util.Assert;
import com.arsdigita.xml.Element; import com.arsdigita.xml.Element;

View File

@ -47,7 +47,6 @@ public abstract class GlobalizedParameterListener
return BUNDLE_NAME; return BUNDLE_NAME;
} }
/** /**
* Set the error message for this parameter. * Set the error message for this parameter.
* *
@ -56,6 +55,7 @@ public abstract class GlobalizedParameterListener
protected void setError(GlobalizedMessage error) { protected void setError(GlobalizedMessage error) {
m_error = error; m_error = error;
} }
/** /**
* Get the error message for this parameter. * Get the error message for this parameter.
* *

View File

@ -37,26 +37,48 @@ import com.arsdigita.globalization.GlobalizedMessage;
*/ */
public class NotEmptyValidationListener extends GlobalizedParameterListener { public class NotEmptyValidationListener extends GlobalizedParameterListener {
/**
* Default Constructor setting a predefined label as error message.
*/
public NotEmptyValidationListener() {
setError(new GlobalizedMessage("bebop.parameters.parameter_not_empty",
getBundleBaseName() )
);
}
/**
* Constructor taking a label specified as key into a resource bundle to
* customize the error message.
*
* @param label key into the resource bundle
*/
public NotEmptyValidationListener(String label) { public NotEmptyValidationListener(String label) {
setError(new GlobalizedMessage(label, getBundleBaseName())); setError(new GlobalizedMessage(label, getBundleBaseName()));
} }
public NotEmptyValidationListener() {
setError(new GlobalizedMessage(
"parameter_is_required", getBundleBaseName()
));
}
/**
* Constructor taking a GlobalizedMessage as error message to display.
*
* @param error GloblizedMessage taken as customized error message.
*/
public NotEmptyValidationListener(GlobalizedMessage error) { public NotEmptyValidationListener(GlobalizedMessage error) {
setError(error); setError(error);
} }
/**
* Validate Method required and used to validate input.
*
* @param e ParameterEvent containing the data
*/
@Override
public void validate (ParameterEvent e) { public void validate (ParameterEvent e) {
ParameterData data = e.getParameterData(); ParameterData data = e.getParameterData();
Object value = data.getValue(); Object value = data.getValue();
if (value != null) { if (value != null) {
// all these are possible: // all these are possible values:
// "&nbsp;" // "&nbsp;"
// " &nbsp;" // " &nbsp;"
// " &nbsp; " // " &nbsp; "
@ -68,13 +90,12 @@ public class NotEmptyValidationListener extends GlobalizedParameterListener {
valueString = StringUtils.strip(valueString, Character.toString('\u00A0')); valueString = StringUtils.strip(valueString, Character.toString('\u00A0'));
valueString = StringUtils.strip(valueString, Character.toString('\u2007')); valueString = StringUtils.strip(valueString, Character.toString('\u2007'));
if (valueString.length() > 0) { if (valueString.length() > 0) {
// non-empty value, just return
return; return;
} }
} }
// Empty or null value, add error message to parameter data object.
data.addError(getError()); data.addError(getError());
} }
} }

View File

@ -16,14 +16,14 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
* *
*/ */
package com.arsdigita.bebop.parameters; // package com.arsdigita.bebop.parameters;
import com.arsdigita.globalization.GlobalizedMessage; // import com.arsdigita.globalization.GlobalizedMessage;
/** /**
* @ deprecated Use {@link NotEmptyValidationListener}. * @ deprecated Use {@link NotEmptyValidationListener}.
*/ */
/*
public class NotWhiteSpaceValidationListener extends NotEmptyValidationListener { public class NotWhiteSpaceValidationListener extends NotEmptyValidationListener {
public NotWhiteSpaceValidationListener(String title) { public NotWhiteSpaceValidationListener(String title) {
@ -32,10 +32,11 @@ public class NotWhiteSpaceValidationListener extends NotEmptyValidationListener
public NotWhiteSpaceValidationListener() { public NotWhiteSpaceValidationListener() {
setError(new GlobalizedMessage setError(new GlobalizedMessage
("parameter_is_required", getBundleBaseName())); ("bebop.parameters.parameter_is_required", getBundleBaseName()));
} }
public NotWhiteSpaceValidationListener(GlobalizedMessage error) { public NotWhiteSpaceValidationListener(GlobalizedMessage error) {
setError(error); setError(error);
} }
} }
*/

View File

@ -6,3 +6,4 @@ parameter.only.letters.digits=This parameter can only contain letters and/or dig
file_empty_or_not_found=is empty or was not found. file_empty_or_not_found=is empty or was not found.
file_too_large=is too large file_too_large=is too large
uri_parameter_is_invalid=This parameter must be a URI formatted according to RFC2396 uri_parameter_is_invalid=This parameter must be a URI formatted according to RFC2396
bebop.parameters.parameter_not_empty=This field must not be empty.

View File

@ -6,3 +6,4 @@ parameter.only.letters.digits=Dieser Parameter darf nur Buchstaben und/oder Zahl
file_empty_or_not_found=ist leer oder wurde nicht gefunden. file_empty_or_not_found=ist leer oder wurde nicht gefunden.
file_too_large=ist zu gro\u00df file_too_large=ist zu gro\u00df
uri_parameter_is_invalid=Dieser Parameter muss ein URI sein, das entsprechend RFC2396 formatiert wird uri_parameter_is_invalid=Dieser Parameter muss ein URI sein, das entsprechend RFC2396 formatiert wird
bebop.parameters.parameter_not_empty=Dieser Eintrag darf nicht leer sein!

View File

@ -6,3 +6,4 @@ parameter.only.letters.digits=This parameter can only contain letters and/or dig
file_empty_or_not_found=is empty or was not found. file_empty_or_not_found=is empty or was not found.
file_too_large=is too large file_too_large=is too large
uri_parameter_is_invalid=This parameter must be a URI formatted according to RFC2396 uri_parameter_is_invalid=This parameter must be a URI formatted according to RFC2396
bebop.parameters.parameter_not_empty=This field must not be empty.

View File

@ -1,3 +1,4 @@
bebop.parameters.parameter_is_required=Este par\u00e1metro es requerido bebop.parameters.parameter_is_required=Este par\u00e1metro es requerido
string_in_range=Este par\u00e1metro no tiene entre {0} y {1} caracteres string_in_range=Este par\u00e1metro no tiene entre {0} y {1} caracteres
type_check={0} tiene que ser de clase {1} pero el objecto {2} es de clase {3} type_check={0} tiene que ser de clase {1} pero el objecto {2} es de clase {3}
bebop.parameters.parameter_not_empty=This field must not be empty.

View File

@ -2,3 +2,4 @@ bebop.parameters.parameter_is_required=Ce param\u00e8tre est obligatoire
string_in_range=La longueur de ce param\u00e8tre n'est pas comprise entre {0} et {1} string_in_range=La longueur de ce param\u00e8tre n'est pas comprise entre {0} et {1}
type_check={0} doit \u00eatre de type {1} mais poss\u00e8de {2} de type {3} type_check={0} doit \u00eatre de type {1} mais poss\u00e8de {2} de type {3}
parameter.only.letters.digits=Ce param\u00e8tre ne doit contenir que des lettres ou des chiffres parameter.only.letters.digits=Ce param\u00e8tre ne doit contenir que des lettres ou des chiffres
bebop.parameters.parameter_not_empty=This field must not be empty.

View File

@ -108,7 +108,9 @@ public class GlobalizedMessage {
* @param args An Object[] of arguments to interpolate into the retrieved * @param args An Object[] of arguments to interpolate into the retrieved
* message. * message.
*/ */
public GlobalizedMessage(final String key, final String bundleName, final Object[] args) { public GlobalizedMessage(final String key,
final String bundleName,
final Object[] args) {
this(key, bundleName); this(key, bundleName);
setArgs(args); setArgs(args);
} }
@ -176,7 +178,8 @@ public class GlobalizedMessage {
* GlobalizedObject? * GlobalizedObject?
*/ */
public Object localize() { public Object localize() {
return localize(com.arsdigita.globalization.GlobalizationHelper.getNegotiatedLocale()); return localize(com.arsdigita.globalization.GlobalizationHelper
.getNegotiatedLocale());
} }
/** /**
@ -201,7 +204,8 @@ public class GlobalizedMessage {
* GlobalizedObject? * GlobalizedObject?
*/ */
public Object localize(final HttpServletRequest request) { public Object localize(final HttpServletRequest request) {
return localize(com.arsdigita.globalization.GlobalizationHelper.getNegotiatedLocale()); return localize(com.arsdigita.globalization.GlobalizationHelper
.getNegotiatedLocale());
} }
/** /**
@ -293,7 +297,7 @@ public class GlobalizedMessage {
} }
/** /**
* <p>For debugging, not for localizing!</p> * For debugging, not for localizing.
* *
* If you need a String, use an additional localize() to get an object * If you need a String, use an additional localize() to get an object
* and cast it to String. e.g. * and cast it to String. e.g.

View File

@ -65,9 +65,9 @@ class UserForm extends Form implements FormValidationListener, AdminConstants {
protected TextField m_url; protected TextField m_url;
protected TextField m_screenName; protected TextField m_screenName;
protected EmailList m_emailList; protected EmailList m_emailList;
private PasswordValidationListener m_pwListener; private final PasswordValidationListener m_pwListener;
private NotEmptyValidationListener m_notNullListener; private final NotEmptyValidationListener m_notNullListener;
private SecurityConfig securityConfig = SecurityConfig.getConfig(); private final SecurityConfig securityConfig = SecurityConfig.getConfig();
public UserForm(String formName) { public UserForm(String formName) {
super(formName); super(formName);
@ -184,6 +184,7 @@ class UserForm extends Form implements FormValidationListener, AdminConstants {
* password-confirm field. Also verifies that primary email * password-confirm field. Also verifies that primary email
* address and screen name are unique amoung all users. * address and screen name are unique amoung all users.
*/ */
@Override
public void validate(FormSectionEvent event) public void validate(FormSectionEvent event)
throws FormProcessException { throws FormProcessException {
PageState ps = event.getPageState(); PageState ps = event.getPageState();

View File

@ -170,7 +170,7 @@ public class Web {
/** /**
* Processes an URL String trying to identify a corresponding recource * Processes an URL String trying to identify a corresponding recource
* which is mapped to the given path String. The method ensures that the * which is mapped to the given path String. The method ensures that the
* resource defiunitely exists (using the URL returned) or definitely not * resource definitely exists (using the URL returned) or definitely not
* (returning null). * (returning null).
* *
* The resourcePath may be stored at various sources (file system, jar file, * The resourcePath may be stored at various sources (file system, jar file,

View File

@ -18,12 +18,10 @@
*/ */
package com.arsdigita.simplesurvey.ui.admin; package com.arsdigita.simplesurvey.ui.admin;
import com.arsdigita.bebop.event.FormSectionEvent; import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.FormProcessListener; import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormInitListener; import com.arsdigita.bebop.event.FormInitListener;
import com.arsdigita.bebop.parameters.NotWhiteSpaceValidationListener; import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.Form; import com.arsdigita.bebop.Form;
import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
@ -47,7 +45,6 @@ import com.arsdigita.simplesurvey.util.GlobalizationUtil ;
import java.util.Calendar; import java.util.Calendar;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
/** /**
* *
* *
@ -57,7 +54,6 @@ public class PropertiesForm extends Form {
private SurveySelectionModel m_survey; private SurveySelectionModel m_survey;
private Class m_type; private Class m_type;
private TextField m_surveyName; private TextField m_surveyName;
private TextArea m_description; private TextArea m_description;
private Date m_startDate; private Date m_startDate;
@ -65,6 +61,12 @@ public class PropertiesForm extends Form {
private RadioGroup m_responsesPublic; private RadioGroup m_responsesPublic;
private RadioGroup m_quizType; private RadioGroup m_quizType;
/**
* Constructor.
*
* @param survey
* @param type
*/
public PropertiesForm(SurveySelectionModel survey, public PropertiesForm(SurveySelectionModel survey,
Class type) { Class type) {
@ -74,10 +76,9 @@ public class PropertiesForm extends Form {
m_type = type; m_type = type;
m_surveyName = new TextField("surveyName"); m_surveyName = new TextField("surveyName");
m_surveyName.addValidationListener(new NotWhiteSpaceValidationListener()); m_surveyName.addValidationListener(new NotEmptyValidationListener());
m_description = new TextArea("description"); m_description = new TextArea("description");
m_startDate = new Date("startDate"); m_startDate = new Date("startDate");
m_endDate = new Date("endDate"); m_endDate = new Date("endDate");
@ -95,8 +96,6 @@ public class PropertiesForm extends Form {
add(new Label(GlobalizationUtil.globalize("simplesurvey.ui.admin.end_date"))); add(new Label(GlobalizationUtil.globalize("simplesurvey.ui.admin.end_date")));
add(m_endDate); add(m_endDate);
add(new Label(GlobalizationUtil.globalize("simplesurvey.ui.admin.should_quiz_responses_be_public"))); add(new Label(GlobalizationUtil.globalize("simplesurvey.ui.admin.should_quiz_responses_be_public")));
m_responsesPublic = new RadioGroup("responsesPublic"); m_responsesPublic = new RadioGroup("responsesPublic");
Option o1 = new Option("true", new Label(GlobalizationUtil.globalize("simplesurvey.ui.admin.Yes"))); Option o1 = new Option("true", new Label(GlobalizationUtil.globalize("simplesurvey.ui.admin.Yes")));
@ -120,8 +119,6 @@ public class PropertiesForm extends Form {
} }
/** /**
* *
*/ */
@ -170,8 +167,6 @@ public class PropertiesForm extends Form {
} }
} }
/** /**
* *
*/ */
@ -197,7 +192,6 @@ public class PropertiesForm extends Form {
// PackageInstance is old style application, no longer used. // PackageInstance is old style application, no longer used.
// survey.setPackageInstance(SimpleSurveyUtil.getPackageInstance(state)); // survey.setPackageInstance(SimpleSurveyUtil.getPackageInstance(state));
form = new PersistentForm(); form = new PersistentForm();
survey.setForm(form); survey.setForm(form);
} }

View File

@ -235,7 +235,7 @@ public class ThemeDirectorConfig extends AbstractConfig {
"defaultThemeURLAttribute"; "defaultThemeURLAttribute";
/** /**
* Purpose uncodumented. * Purpose undocumented.
* *
* @return * @return
*/ */

View File

@ -38,3 +38,5 @@ theme.url_hint=Enter the LAST part of the url for the theme, eg 'holiday'. Shoul
theme.save_button_hint=Save the details in the form theme.save_button_hint=Save the details in the form
theme.cancel_button_hint=Abort changes & reset the form. theme.cancel_button_hint=Abort changes & reset the form.
theme.cancel_button_pressed_msg=cancel pressed theme.cancel_button_pressed_msg=cancel pressed
theme.form.url_can_contain_only_characters=The URL can only contain A-Z, a-z, 0-9, _, and -.
theme.form.url_already_exists=A theme with this url already exists.

View File

@ -38,3 +38,5 @@ theme.url_hint=Geben Sie den LETZTEN Teil der URL ein, z.B. 'ferien'. Der Eintra
theme.save_button_hint=Speichern der Angaben in dem Formular. theme.save_button_hint=Speichern der Angaben in dem Formular.
theme.cancel_button_hint=Verwerfen der Eintragungen und R\u00fccksetzen des Formulars. theme.cancel_button_hint=Verwerfen der Eintragungen und R\u00fccksetzen des Formulars.
theme.cancel_button_pressed_msg=Vorgang abgebrochen theme.cancel_button_pressed_msg=Vorgang abgebrochen
theme.form.url_can_contain_only_characters=Die URL darf nur die Zeichen A-Z, a-z, 0-9, _ und - enthalten.
theme.form.url_already_exists=Ein Theme mit dieser URL existiert bereits.

View File

@ -38,3 +38,5 @@ theme.url_hint=Enter the LAST part of tht url for the theme, eg 'holiday'. Shou
theme.save_button_hint=Save the details in the form theme.save_button_hint=Save the details in the form
theme.cancel_button_hint=Abort changes & reset the form. theme.cancel_button_hint=Abort changes & reset the form.
theme.cancel_button_pressed_msg=cancel pressed theme.cancel_button_pressed_msg=cancel pressed
theme.form.url_can_contain_only_characters=The URL can only contain A-Z, a-z, 0-9, _, and -.
theme.form.url_already_exists=A theme with this url already exists.

View File

@ -38,3 +38,5 @@ theme.url_hint=Enter the LAST part of tht url for the theme, eg 'holiday'. Shou
theme.save_button_hint=Save the details in the form theme.save_button_hint=Save the details in the form
theme.cancel_button_hint=Abort changes & reset the form. theme.cancel_button_hint=Abort changes & reset the form.
theme.cancel_button_pressed_msg=cancel pressed theme.cancel_button_pressed_msg=cancel pressed
theme.form.url_can_contain_only_characters=The URL can only contain A-Z, a-z, 0-9, _, and -.
theme.form.url_already_exists=A theme with this url already exists.

View File

@ -105,7 +105,7 @@ public class InternalThemePrefixerServlet extends InternalPrefixerServlet {
} }
} }
String prefix = pathInfo; String prefix;
if (path != null) { if (path != null) {
String themeName = pathInfo.substring(0, pathInfo.indexOf(path)); String themeName = pathInfo.substring(0, pathInfo.indexOf(path));
prefix = m_prefix + themeName; prefix = m_prefix + themeName;

View File

@ -49,9 +49,13 @@ import org.apache.log4j.Logger;
* *
* @author Randy Graebner &lt;randyg@redhat.com&gt; * @author Randy Graebner &lt;randyg@redhat.com&gt;
*/ */
public class ThemeContainer extends SimpleContainer implements ThemeDirectorConstants { public class ThemeContainer extends SimpleContainer
implements ThemeDirectorConstants {
/** A logger instance. */ /** Internal logger instance to faciliate debugging. Enable logging output
* by editing /WEB-INF/conf/log4j.properties int the runtime environment
* and set com.arsdigita.themedirector.ui.ThemeContainer=DEBUG
* by uncommenting or adding the line. */
private static final Logger s_log = private static final Logger s_log =
Logger.getLogger(ThemeContainer.class); Logger.getLogger(ThemeContainer.class);
@ -62,6 +66,7 @@ public class ThemeContainer extends SimpleContainer implements ThemeDirectorCons
/** /**
* Constructor. * Constructor.
*
* @param model * @param model
* @param parent * @param parent
*/ */
@ -228,6 +233,11 @@ public class ThemeContainer extends SimpleContainer implements ThemeDirectorCons
* *
*/ */
private class ProductionFilesDownloadLink extends Link { private class ProductionFilesDownloadLink extends Link {
/**
* Constructor
* @param model
*/
ProductionFilesDownloadLink(final ThemeSelectionModel model) { ProductionFilesDownloadLink(final ThemeSelectionModel model) {
super(new Label(GlobalizationUtil.globalize super(new Label(GlobalizationUtil.globalize
("theme.download_prod_theme_files")), ("theme.download_prod_theme_files")),
@ -245,6 +255,11 @@ public class ThemeContainer extends SimpleContainer implements ThemeDirectorCons
}); });
} }
/**
*
* @param state
* @return
*/
@Override @Override
public boolean isVisible(PageState state) { public boolean isVisible(PageState state) {
return super.isVisible(state) && return super.isVisible(state) &&

View File

@ -52,18 +52,20 @@ import java.util.TooManyListenersException;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
* This is the base page for controlling themes in the system. It * This is the base page for controlling themes in the system. It contains
* contains a list of the themes on the left side and it manages the state * a list of the themes on the left side and it manages the state to show the
* to show the correct forms/containers on the left * correct forms/containers on the right
*/ */
public class ThemeControlPanel extends SelectionPanel implements ThemeDirectorConstants { public class ThemeControlPanel extends SelectionPanel
implements ThemeDirectorConstants {
/** Internal logger instance to faciliate debugging. Enable logging output /** Internal logger instance to faciliate debugging. Enable logging output
* by editing /WEB-INF/conf/log4j.properties int the runtime environment * by editing /WEB-INF/conf/log4j.properties int the runtime environment
* and set com.arsdigita.templating.ui.ThemeControlPanel=DEBUG * and set com.arsdigita.themedirector.ui.ThemeControlPanel=DEBUG
* by uncommenting or adding the line. */ * by uncommenting or adding the line. */
private static final Logger LOGGER = Logger.getLogger(ThemeControlPanel.class); private static final Logger LOGGER = Logger.getLogger(ThemeControlPanel.class);
private final ThemeSelectionModel selectionModel; private final ThemeSelectionModel selectionModel;
/** Stored the theme form object, which shopws widget / data for a theme. */
private final Form themeForm; private final Form themeForm;
private final BigDecimalParameter defaultThemeParam = new BigDecimalParameter("defaultTheme"); private final BigDecimalParameter defaultThemeParam = new BigDecimalParameter("defaultTheme");
@ -121,19 +123,28 @@ public class ThemeControlPanel extends SelectionPanel implements ThemeDirectorCo
addAction(defaultThemeForm); addAction(defaultThemeForm);
} }
/**
*
* @return
*/
private Form createDefaultThemeForm() { private Form createDefaultThemeForm() {
final Form defaultThemeForm = new Form("defaultThemeForm", new SimpleContainer()); final Form defaultThemeForm = new Form("defaultThemeForm",
defaultThemeForm.add(new Label(GlobalizationUtil.globalize("theme.set_default_theme"))); new SimpleContainer());
defaultThemeForm.add(new Label(GlobalizationUtil.globalize(
"theme.set_default_theme")));
final SingleSelect themes = new SingleSelect(defaultThemeParam); final SingleSelect themes = new SingleSelect(defaultThemeParam);
themes.addOption(new Option(null, new Label(GlobalizationUtil.globalize("theme.none")))); themes.addOption(new Option(null, new Label(GlobalizationUtil.globalize(
"theme.none"))));
try { try {
themes.addPrintListener(new PrintListener() { themes.addPrintListener(new PrintListener() {
@Override @Override
public void prepare(final PrintEvent event) { public void prepare(final PrintEvent event) {
final SingleSelect target = (SingleSelect) event.getTarget(); final SingleSelect target = (SingleSelect) event.getTarget();
final DataCollection options = SessionManager.getSession().retrieve(Theme.BASE_DATA_OBJECT_TYPE); final DataCollection options = SessionManager
.getSession()
.retrieve(Theme.BASE_DATA_OBJECT_TYPE);
options.addNotEqualsFilter(Theme.LAST_PUBLISHED_DATE, null); options.addNotEqualsFilter(Theme.LAST_PUBLISHED_DATE, null);
options.addOrder(Theme.TITLE); options.addOrder(Theme.TITLE);
while (options.next()) { while (options.next()) {

View File

@ -29,31 +29,34 @@ import com.arsdigita.bebop.event.FormSubmissionListener;
import com.arsdigita.bebop.event.FormProcessListener; import com.arsdigita.bebop.event.FormProcessListener;
import com.arsdigita.bebop.event.FormValidationListener; import com.arsdigita.bebop.event.FormValidationListener;
import com.arsdigita.bebop.event.FormSectionEvent; import com.arsdigita.bebop.event.FormSectionEvent;
import com.arsdigita.bebop.event.ParameterEvent;
import com.arsdigita.bebop.FormProcessException; import com.arsdigita.bebop.FormProcessException;
import com.arsdigita.bebop.GridPanel; import com.arsdigita.bebop.GridPanel;
import com.arsdigita.bebop.PageState; import com.arsdigita.bebop.PageState;
import com.arsdigita.bebop.parameters.GlobalizedParameterListener;
import com.arsdigita.bebop.parameters.NotEmptyValidationListener; import com.arsdigita.bebop.parameters.NotEmptyValidationListener;
import com.arsdigita.bebop.parameters.ParameterData;
import com.arsdigita.bebop.parameters.StringParameter; import com.arsdigita.bebop.parameters.StringParameter;
import com.arsdigita.bebop.Label; import com.arsdigita.bebop.Label;
import com.arsdigita.persistence.DataCollection; import com.arsdigita.persistence.DataCollection;
import com.arsdigita.persistence.SessionManager; import com.arsdigita.persistence.SessionManager;
import com.arsdigita.util.UncheckedWrapperException; import com.arsdigita.util.UncheckedWrapperException;
import com.arsdigita.util.IO; //import com.arsdigita.util.IO;
import com.arsdigita.themedirector.ThemeDirectorConstants; import com.arsdigita.themedirector.ThemeDirectorConstants;
import com.arsdigita.themedirector.ThemeDirector; import com.arsdigita.themedirector.ThemeDirector;
import com.arsdigita.themedirector.util.GlobalizationUtil; import com.arsdigita.themedirector.util.GlobalizationUtil;
import com.arsdigita.themedirector.util.ManifestReader; //import com.arsdigita.themedirector.util.ManifestReader;
import com.arsdigita.subsite.Subsite; import com.arsdigita.subsite.Subsite;
import com.arsdigita.subsite.Site; import com.arsdigita.subsite.Site;
import com.arsdigita.toolbox.ui.Cancellable; import com.arsdigita.toolbox.ui.Cancellable;
import com.arsdigita.web.Web; import com.arsdigita.web.Web;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; //import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; //import java.io.InputStream;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter; //import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -73,9 +76,17 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
private static final Logger s_log = Logger.getLogger(ThemeForm.class); private static final Logger s_log = Logger.getLogger(ThemeForm.class);
private final ThemeSelectionModel m_theme; private final ThemeSelectionModel m_theme;
/* Holds the theme's title. */
private final TextField m_title; private final TextField m_title;
/* Holds the theme's description. */
private final TextArea m_description; private final TextArea m_description;
/* Holds the theme's URL. */
private final TextField m_url; private final TextField m_url;
/* Instance of the form's Save/Cancel buttons. */
private final SaveCancelSection m_buttons; private final SaveCancelSection m_buttons;
/** /**
@ -119,7 +130,9 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
// Experimental, see above // Experimental, see above
m_url.setLabel(GlobalizationUtil.globalize("theme.url")); m_url.setLabel(GlobalizationUtil.globalize("theme.url"));
m_url.addValidationListener(new NotEmptyValidationListener()); m_url.addValidationListener(new NotEmptyValidationListener());
m_title.setSize(40); m_url.addValidationListener(new URLFormValidationListener());
m_url.addValidationListener(new UniqueURLValidationListener());
m_url.setSize(40);
m_url.setHint(GlobalizationUtil.globalize("theme.url_hint")); m_url.setHint(GlobalizationUtil.globalize("theme.url_hint"));
add(m_url); add(m_url);
@ -133,7 +146,11 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
addSubmissionListener(new ThemeSubmissionListener()); addSubmissionListener(new ThemeSubmissionListener());
addProcessListener(new ThemeProcessListener()); addProcessListener(new ThemeProcessListener());
addInitListener(new ThemeInitListener()); addInitListener(new ThemeInitListener());
addValidationListener(new ThemeValidationListener()); // Form wide validation listener commented out temporarily. All validation
// is done using a parameterValidationListener on each input component.
// A form wide validation listener may be introduced later to display
// an error summerize text and advise what to do.
//addValidationListener(new ThemeValidationListener());
} }
/** /**
@ -282,6 +299,8 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
} }
copyDefaultTheme(newDirectory,null); copyDefaultTheme(newDirectory,null);
// Old way should be removed as soon as thorough testing the
// new method has been completed.
// copyDefaultFiles(newDirectory); // copyDefaultFiles(newDirectory);
if (oldDirectory != null && !oldDirectory.exists()) { if (oldDirectory != null && !oldDirectory.exists()) {
@ -334,100 +353,15 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
} }
} }
/**
* ValöidatgionListener class to check the themedirector form input data.
* It's validate method is the entry point and executed when submitting
* the form.
*/
private class ThemeValidationListener implements FormValidationListener {
/**
*
* @param e
* @throws FormProcessException
*/
@Override
public void validate(FormSectionEvent e)
throws FormProcessException {
PageState state = e.getPageState();
String url = (String)m_url.getValue(state);
validateURLForm(state, url);
validateURLUniqueness(state, url);
// now, validate that the URL does not already exist if this
// is actually a "new" and not an "edit"
Theme theme = m_theme.getSelectedTheme(state);
if (theme == null) {
File currentRoot = new File(Web.getServletContext().getRealPath("/"));
File newDirectory = new File(currentRoot, DEV_THEMES_BASE_DIR +
url);
if (newDirectory.exists()) {
throw new FormProcessException
("A file with the name " + url + " already exists " +
"in the file system. Contact your system " +
"administrator if you think this is an error.");
}
}
}
}
/**
* This checks the form of the url...specifically, we are only allowing
* [A-Z,a-z,0-9,_,-].
* @param state
* @param url
* @throws com.arsdigita.bebop.FormProcessException
*/
public void validateURLForm(PageState state, String url)
throws FormProcessException {
//Obviously, this is not at all globalized and should
//only be used with English text....however, since we are dealing
// with a string that will be in the file system and will not
// be seen by the end user, this should be fine.
for (int i = 0; i < url.length(); i++) {
char c = url.charAt(i);
if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') ||
('0' <= c && c <= '9') || c == '_' || c == '-')) {
throw new FormProcessException
("The URL can only contain A-Z, a-z, 0-9, _, and -.");
}
}
}
/**
* This makes sure no other theme has the same URL
* @param state
* @param url
* @throws com.arsdigita.bebop.FormProcessException
*/
public void validateURLUniqueness(PageState state, String url)
throws FormProcessException {
if ( url != null ) {
DataCollection collection = SessionManager.getSession()
.retrieve(Theme.BASE_DATA_OBJECT_TYPE);
collection.addEqualsFilter("lower(" + Theme.URL + ")",
url.toLowerCase());
Theme currentTheme = (Theme)m_theme.getSelectedObject(state);
if (currentTheme != null) {
collection.addNotEqualsFilter(Theme.ID, currentTheme.getID());
}
if ( collection.size() > 0) {
throw new FormProcessException
("A theme with this url already exists");
}
}
}
/** /**
* Copies a complete directory containing the default theme to a newly * Copies a complete directory containing the default theme to a newly
* created theme's directory without any filtering or other processing. * created theme's directory without any filtering or other processing.
* It assumes, that the source directory contains a complete and working * It assumes, that the source directory contains a complete and working
* set of theme files. * set of theme files.
* *
* Developer's note: Why not move into private class ThemeProcessListener
* where it is used (exclusivly).
*
* @param newThemeDirectory specifies the target directory. Must not * @param newThemeDirectory specifies the target directory. Must not
* be null. * be null.
* @param defaultThemeDirectory Directory containing a complete set of * @param defaultThemeDirectory Directory containing a complete set of
@ -449,13 +383,188 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
newThemeDirectory); newThemeDirectory);
} }
/**
* Themedirector-specific parameter validation listener, verifies that the
* URL parameter value contains only valid characters.
* Helper method to checks the form of the url...specifically, we are
* only allowing [A-Z,a-z,0-9,_,-].
*/
private class URLFormValidationListener
extends GlobalizedParameterListener {
/**
* Default Constructor setting a predefined label as error message.
*/
public URLFormValidationListener() {
setError(GlobalizationUtil.globalize(
"theme.form.url_can_contain_only_characters")
);
}
/**
* Validate Method required and used to validate input.
*
* @param e ParameterEvent containing the data
*/
@Override
public void validate(ParameterEvent e) {
ParameterData data = e.getParameterData();
Object value = data.getValue();
PageState state = e.getPageState();
String url = (String)m_url.getValue(state);
if (value != null) {
String urlString = value.toString().trim();
for (int i = 0; i < urlString.length(); i++) {
char c = urlString.charAt(i);
if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z')
|| ('0' <= c && c <= '9') || c == '_' || c == '-')) {
// urlString contains illegal character(s)
// adds an error message to the widget object.
data.addError(getError());
return;
}
}
// non-empty value, just return
return;
}
// Empty or no illegal character found, just return
return;
} // end method validatge
} // end private class URLFormValidationListener
/**
* Themedirector-specific parameter validation listener, verifies that the
* URL parameter is unique.
*/
private class UniqueURLValidationListener
extends GlobalizedParameterListener {
/**
* Default Constructor setting a predefined label as error message.
*/
public UniqueURLValidationListener() {
setError(GlobalizationUtil.globalize(
"theme.form.url_already_exists")
);
}
/**
* Validate Method required and used to validate input.
*
* @param e ParameterEvent containing the data
* @pre URL parameter is not empty and contains valid characters only
* as ensured by previously invoked ValidationListeners.
*/
@Override
public void validate(ParameterEvent e) {
ParameterData data = e.getParameterData();
Object value = data.getValue();
PageState state = e.getPageState();
String url = (String)m_url.getValue(state);
if (value != null) {
String urlString = value.toString().trim();
DataCollection collection = SessionManager.getSession()
.retrieve(Theme.BASE_DATA_OBJECT_TYPE);
collection.addEqualsFilter("lower(" + Theme.URL + ")",
urlString.toLowerCase());
Theme currentTheme = (Theme)m_theme.getSelectedObject(state);
if (currentTheme != null) {
collection.addNotEqualsFilter(Theme.ID, currentTheme.getID());
}
if ( collection.size() > 0) {
// urlString is not unique but already exists.
// adds an error message to the widget object.
data.addError(getError());
return;
}
// unique value, just return
return;
}
// null or any other condition not able to handle here. Just return
} // end method validate
} // end private class UniqueURLValidationListener
/**
* ValidationListener class to check the themedirector's form input data.
* It's validate method is the entry point and executed when submitting
* the form.
*/
private class ThemeValidationListener implements FormValidationListener {
/**
* Does the actual validation.
*
* @param fse
* @throws FormProcessException
*/
@Override
public void validate(FormSectionEvent fse)
throws FormProcessException {
PageState state = fse.getPageState();
// Nothing to do at the Moment
// We should add something like that:
// - acquire a formdata object
// - iterate ober the formdata widgets and check for an
// added error message
// - Add the error to an error list
// - If at least on error has been found, throw a FormProcessException
// With a summerizing text and a list of errors found.
// Text from a FPE is by default shown just below the action buttons
// (Save / Cancel)
// in case of an error in any of the input form widgets,
// throw a FPE in give a summerizing message and advice.
// throw new FormProcessException
// ("[String]",
// GlobalizationUtil.globalize("key",errorDetails)
// );
// }
}
} // end ThemeValidationListener
// ///////////////////////////////////////////////////////////////////////
// Outdated CODE
// Temporarily retained for easy reference until migration is complete.
// ///////////////////////////////////////////////////////////////////////
/** /**
* Copies the default theme files to the new directory using a * Copies the default theme files to the new directory using a
* Manifest file to determine the files to copy. * Manifest file to determine the files to copy.
* *
* NOTE:
* Old way should be removed as soon as thorough testing the
* new method has been completed.
*
*
* @param newDirectory specifies the target directory * @param newDirectory specifies the target directory
* @throws IOException * @throws IOException
*/ */
/*
private void copyDefaultFiles(File newDirectory) throws IOException { private void copyDefaultFiles(File newDirectory) throws IOException {
ClassLoader loader = Thread.currentThread().getContextClassLoader(); ClassLoader loader = Thread.currentThread().getContextClassLoader();
InputStream is = loader.getResourceAsStream InputStream is = loader.getResourceAsStream
@ -476,21 +585,19 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
new FileWriterManifestReader(is, newDirectory); new FileWriterManifestReader(is, newDirectory);
reader.processFile(); reader.processFile();
} }
*/
/** /**
* *
* NOTE: Part of outdated way to copy a default theme. Copying is now done
* without Manifest file. Class chould be completely removed as soon as
* QA testing is completed.
*/ */
/*
private class FileWriterManifestReader extends ManifestReader { private class FileWriterManifestReader extends ManifestReader {
private final File m_newDirectory; private final File m_newDirectory;
private final String m_directoryFilter; private final String m_directoryFilter;
/**
* Constructor.
*
* @param stream
* @param newDirectory
*/
FileWriterManifestReader(InputStream stream, File newDirectory) { FileWriterManifestReader(InputStream stream, File newDirectory) {
super(stream); super(stream);
m_newDirectory = newDirectory; m_newDirectory = newDirectory;
@ -498,12 +605,6 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
m_directoryFilter = ThemeDirector.getConfig().getDefaultThemePath(); m_directoryFilter = ThemeDirector.getConfig().getDefaultThemePath();
} }
/**
*
* @param is
* @param filePath
* @param isStyleFile
*/
@Override @Override
public void processManifestFileLine(InputStream is, public void processManifestFileLine(InputStream is,
String filePath, String filePath,
@ -541,4 +642,5 @@ public class ThemeForm extends Form implements Cancellable, ThemeDirectorConstan
} }
} }
} }
*/
} }

View File

@ -182,11 +182,11 @@ class ThemeValidationPanel extends GridPanel implements ThemeDirectorConstants {
containing patterns to search for. containing patterns to search for.
Developer's Note: Developer's Note:
We used to use a 'resource' tag involing a resource servlet to We used to use a 'resource' tag involving a resource servlet to
deliver the correct file either from database or filesystem. Ir would deliver the correct file either from database or filesystem. It
require an URL similar to would require an URL similar to
http://localhost:9008/libreccm/resource/themes/heirloom/apps/theme/xsl/index.xs http://localhost:9008/libreccm/resource/themes/heirloom/apps/theme/xsl/index.xs
where librecms is the context ccm happpens to be installed in. where librecms is the context CCM happpens to be installed in.
Currently we bypass the resource servlet and access the filesystem Currently we bypass the resource servlet and access the filesystem
directly. Must be modified as soon as we deliver the theme from db. */ directly. Must be modified as soon as we deliver the theme from db. */
String stylesheetPath = "http://" + Web.getConfig().getHost().toString() String stylesheetPath = "http://" + Web.getConfig().getHost().toString()

View File

@ -86,9 +86,12 @@ public class ThemeDevelopmentFileManager extends ThemeFileManager {
* process the file. A startupDelay of 0 means that this is a no-op * process the file. A startupDelay of 0 means that this is a no-op
* @param pollDelay number of seconds to wait between checks if the file * @param pollDelay number of seconds to wait between checks if the file
* has any entries. * has any entries.
* @param baseDirectory
* @return
*/ */
public static ThemeFileManager startWatchingFiles public static ThemeFileManager startWatchingFiles(int startupDelay,
(int startupDelay, int pollDelay, String baseDirectory) { int pollDelay,
String baseDirectory) {
if (s_manager == null) { if (s_manager == null) {
s_log.info("Starting Theme File Manager Thread with the base " + s_log.info("Starting Theme File Manager Thread with the base " +
"directory of " + baseDirectory); "directory of " + baseDirectory);
@ -110,6 +113,8 @@ public class ThemeDevelopmentFileManager extends ThemeFileManager {
/** /**
* This returns the current thread or null if the thread has not * This returns the current thread or null if the thread has not
* yet been started. * yet been started.
*
* @return
*/ */
public static ThemeFileManager getInstance() { public static ThemeFileManager getInstance() {
return s_manager; return s_manager;
@ -117,8 +122,11 @@ public class ThemeDevelopmentFileManager extends ThemeFileManager {
/** /**
* this typically returns something like "getBaseDirectory() + PROD_DIR" * This typically returns something like "getBaseDirectory() + PROD_DIR".
*
* @return
*/ */
@Override
protected String getManagerSpecificDirectory() { protected String getManagerSpecificDirectory() {
return getBaseDirectory() + DEV_THEMES_BASE_DIR; return getBaseDirectory() + DEV_THEMES_BASE_DIR;
} }
@ -134,6 +142,7 @@ public class ThemeDevelopmentFileManager extends ThemeFileManager {
// it works if we want the thread to auto-update things for us. // it works if we want the thread to auto-update things for us.
// if we decide that we definitely do not want the auto-update // if we decide that we definitely do not want the auto-update
// then we should remove this. // then we should remove this.
@Override
protected void updateTheme(Theme theme) { protected void updateTheme(Theme theme) {
// the first step is to make sure that all files from the theme // the first step is to make sure that all files from the theme
// are in the db. // are in the db.
@ -154,9 +163,11 @@ public class ThemeDevelopmentFileManager extends ThemeFileManager {
/** /**
* This allows subclasses to filter the collection as appropriate * This allows subclasses to filter the collection as appropriate.
* (e.g. only return "live" files or only "draft" files). * (e.g. only return "live" files or only "draft" files).
* @return
*/ */
@Override
protected ThemeFileCollection getThemeFilesCollection(Theme theme) { protected ThemeFileCollection getThemeFilesCollection(Theme theme) {
return theme.getDraftThemeFiles(); return theme.getDraftThemeFiles();
} }

View File

@ -21,7 +21,8 @@ import com.arsdigita.themedirector.ThemeDirectorConstants;
import com.arsdigita.themedirector.ThemeFileCollection; import com.arsdigita.themedirector.ThemeFileCollection;
import com.arsdigita.persistence.SessionManager; import com.arsdigita.persistence.SessionManager;
import com.arsdigita.persistence.TransactionContext; import com.arsdigita.persistence.TransactionContext;
import com.arsdigita.themedirector.dispatcher.InternalThemePrefixerServlet; //import com.arsdigita.themedirector.dispatcher.InternalThemePrefixerServlet;
import com.arsdigita.web.Web;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -205,28 +206,18 @@ public abstract class ThemeFileManager extends Thread
if (m_baseDirectory == null) { if (m_baseDirectory == null) {
// Because the constructor sets the base directory this should // Because the constructor sets the base directory this should
// never happen, but just in case .... // never happen, but just in case ....
// ThemeDirector may execute in a different web application context // ThemeDirector may execute in a different web application context
// as core oder CMS. To determine the actual context we may ask // as core oder CMS.
// Themedirector servlet. // Old non-standard-compliant code had been removed, so currently
// ThemeManager has to be installed into the same context as core.
// To determine the actual context we may ask Themedirector servlet.
// Something like
// ServletContext themeCtx = InternalThemePrefixerServlet
// .getThemedirectorContext();
// We have to ensure the Servlet is initialized.
/* OLD code depending on deprecated ContextRegiserServlet ServletContext themeCtx = Web.getServletContext();
ApplicationCollection collection = Application.retrieveAllApplications();
collection.filterToApplicationType(ThemeDirector.BASE_DATA_OBJECT_TYPE);
Application app = null;
if (collection.next()) {
// it should only be mounted once but the jsp does not
// care about the application so even if it is mounted multiple
// times that is fine.
app = collection.getApplication();
}
collection.close();
String webapp = app.getContextPath();
ServletContext themeCtx = Web.getServletContext(webapp + '/');
m_baseDirectory = themeCtx.getRealPath("/");
*/
ServletContext themeCtx = InternalThemePrefixerServlet
.getThemedirectorContext();
m_baseDirectory = themeCtx.getRealPath("/"); m_baseDirectory = themeCtx.getRealPath("/");
} }

View File

@ -32,11 +32,14 @@ import org.apache.log4j.Logger;
* runs for the first time, it examines all themes. * runs for the first time, it examines all themes.
* *
* @author <a href="mailto:randyg@redhat.com">Randy Graebner</a> * @author <a href="mailto:randyg@redhat.com">Randy Graebner</a>
*
* @version $Revision: #2 $ $DateTime: 2004/03/17 09:56:37 $ * @version $Revision: #2 $ $DateTime: 2004/03/17 09:56:37 $
*/ */
public class ThemePublishedFileManager extends ThemeFileManager { public class ThemePublishedFileManager extends ThemeFileManager {
/** Internal logger instance to faciliate debugging. Enable logging output
* by editing /WEB-INF/conf/log4j.properties int hte runtime environment
* and set com.arsdigita.themedirector.util.ThemePublishedFileManager=DEBUG
* by uncommenting or adding the line. */
private static Logger s_log = private static Logger s_log =
Logger.getLogger(ThemePublishedFileManager.class); Logger.getLogger(ThemePublishedFileManager.class);
@ -46,7 +49,8 @@ public class ThemePublishedFileManager extends ThemeFileManager {
static private ThemeFileManager s_manager = null; static private ThemeFileManager s_manager = null;
protected ThemePublishedFileManager(int startupDelay, int pollDelay, protected ThemePublishedFileManager(int startupDelay,
int pollDelay,
String baseDirectory) { String baseDirectory) {
super(s_log, startupDelay, pollDelay, baseDirectory); super(s_log, startupDelay, pollDelay, baseDirectory);
} }
@ -128,7 +132,7 @@ public class ThemePublishedFileManager extends ThemeFileManager {
/** /**
* This allows subclasses to filter the collection as appropriate * This allows subclasses to filter the collection as appropriate.
* (e.g. only return "live" files or only "draft" files). * (e.g. only return "live" files or only "draft" files).
*/ */
protected ThemeFileCollection getThemeFilesCollection(Theme theme) { protected ThemeFileCollection getThemeFilesCollection(Theme theme) {