Thanks a lot Richard for your Code. I think instead of marking
Just the final Steps, we should define the whole order of the EventHandlers.

This would allow stripes to ensure that the user passes trough step by step.
Another benefit we would get is, that we can allow "backward navigation" without
Displaying invalid error messages.

I have opened a jira ticket and illustrated my thoughts in a gist:

Ticket: http://www.stripesframework.org/jira/browse/STS-737
Gist: https://gist.github.com/1072357cbedbcbf0f587

Cheers,
Yves Senn


-----Ursprüngliche Nachricht-----
Von: Richard Hauswald [mailto:[email protected]] 
Gesendet: Dienstag, 9. Februar 2010 14:27
An: Stripes Users List
Betreff: Re: [Stripes-users] Skip pages in Wizards

Hello Stripes Users,
I set up a test case and it proves Yves Senn 's statement.
You can solve this issue temporarily by using a custom PropertyBinder
and a new Annotation marking the method called at the end of a wizard.
Cheers,
Richard

Example:

web.xml
<init-param>
        <param-name>ActionBeanPropertyBinder.Class</param-name>
        <param-value>x.y.z.WizardPropertyBinder</param-value>
</init-param>


LastWizardPage.java

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LastWizardPage {

}

WizardPropertyBinder.java
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;

import net.sourceforge.stripes.action.ActionBean;
import net.sourceforge.stripes.action.ActionBeanContext;
import net.sourceforge.stripes.action.Wizard;
import net.sourceforge.stripes.config.Configuration;
import net.sourceforge.stripes.controller.ActionResolver;
import net.sourceforge.stripes.controller.DefaultActionBeanPropertyBinder;
import net.sourceforge.stripes.controller.ParameterName;
import net.sourceforge.stripes.controller.StripesRequestWrapper;
import net.sourceforge.stripes.exception.StripesServletException;
import net.sourceforge.stripes.util.Log;
import net.sourceforge.stripes.validation.ValidationErrors;
import net.sourceforge.stripes.validation.ValidationMetadata;

public class WizardPropertyBinder extends DefaultActionBeanPropertyBinder {
        private static final Log log =
Log.getInstance(DefaultActionBeanPropertyBinder.class);

        @Override
        protected void validateRequiredFields(Map<ParameterName, String[]>
parameters, ActionBean bean, ValidationErrors errors) {
                log.debug("Running required field validation on bean class ",
bean.getClass().getName());

                // Assemble a set of names that we know have indexed 
parameters, so
we won't check
                // for required-ness the regular way
                Set<String> indexedParams = new HashSet<String>();
                for (ParameterName name : parameters.keySet()) {
                        if (name.isIndexed()) {
                                indexedParams.add(name.getStrippedName());
                        }
                }

                Map<String, ValidationMetadata> validationInfos =
getConfiguration().getValidationMetadataProvider().getValidationMetadata(bean.getClass());
                ActionBeanContext context = bean.getContext();
                HttpServletRequest request = context.getRequest();
                StripesRequestWrapper stripesReq =
StripesRequestWrapper.findStripesWrapper(request);

                if (validationInfos != null) {
                        boolean wizard = 
bean.getClass().getAnnotation(Wizard.class) != null;
                        boolean lastWizardPage = 
isLastWizardPage(getConfiguration(), context, bean);
                        Collection<String> fieldsOnPage = 
getFieldsPresentInfo(bean);

                        for (Map.Entry<String, ValidationMetadata> entry :
validationInfos.entrySet()) {
                                String propertyName = entry.getKey();
                                ValidationMetadata validationInfo = 
entry.getValue();

                                // If the field is required, and we don't have 
index params that collapse
                                // to that property name, check that it was 
supplied
                                if 
(validationInfo.requiredOn(context.getEventName()) &&
!indexedParams.contains(propertyName)) {

                                        // Make the added check that if the 
form is a wizard, the required field is
                                        // in the set of fields that were on 
the page
                                        if (!wizard || lastWizardPage || 
fieldsOnPage.contains(propertyName)) {
                                                String[] values = 
trim(request.getParameterValues(propertyName),
validationInfo);
                                                log.debug("Checking required 
field: ", propertyName, ", with
values: ", values);
                                                
checkSingleRequiredField(propertyName, propertyName, values,
stripesReq, errors);
                                        }
                                }
                        }
                }

                // Now the easy work is done, figure out which rows of indexed 
props
had values submitted
                // and what to flag up as failing required field validation
                if (indexedParams.size() > 0) {
                        Map<String, Row> rows = new HashMap<String, Row>();

                        for (Map.Entry<ParameterName, String[]> entry : 
parameters.entrySet()) {
                                ParameterName name = entry.getKey();
                                String[] values = entry.getValue();

                                if (name.isIndexed()) {
                                        String rowKey = 
name.getName().substring(0,
name.getName().indexOf(']') + 1);
                                        if (!rows.containsKey(rowKey)) {
                                                rows.put(rowKey, new Row());
                                        }

                                        rows.get(rowKey).put(name, values);
                                }
                        }

                        for (Row row : rows.values()) {
                                if (row.hasNonEmptyValues()) {
                                        for (Map.Entry<ParameterName, String[]> 
entry : row.entrySet()) {
                                                ParameterName name = 
entry.getKey();
                                                String[] values = 
entry.getValue();
                                                ValidationMetadata 
validationInfo =
validationInfos.get(name.getStrippedName());

                                                if (validationInfo != null &&
validationInfo.requiredOn(context.getEventName())) {
                                                        
checkSingleRequiredField(name.getName(),
name.getStrippedName(), values, stripesReq, errors);
                                                }
                                        }
                                } else {
                                        // If the row is full of empty data, 
get rid of it all to
                                        // prevent problems in downstream 
validation
                                        for (ParameterName name : row.keySet()) 
{
                                                parameters.remove(name);
                                        }
                                }
                        }

                }
        }

        protected boolean isLastWizardPage(Configuration config,
ActionBeanContext context, ActionBean bean) {
                try {
                        ActionResolver resolver = config.getActionResolver();

                        // Then lookup the event name and handler method etc.
                        String eventName = context.getEventName();

                        final Method handler;
                        if (eventName != null) {
                                handler = resolver.getHandler(bean.getClass(), 
eventName);
                        } else {
                                handler = 
resolver.getDefaultHandler(bean.getClass());
                                if (handler != null) {
                                        
context.setEventName(resolver.getHandledEvent(handler));
                                }
                        }
                        return 
handler.isAnnotationPresent(LastWizardPage.class);
                } catch (StripesServletException e) {
                        throw new RuntimeException("This should never happen 
:-) - only a
quick fix");
                }

        }
        /**
         * An inner class that represents a "row" of form properties that all
have the same index
         * so that we can validate all those properties together.
         */
        protected static class Row extends HashMap<ParameterName, String[]> {
                private static final long serialVersionUID = 1L;

                private boolean hasNonEmptyValues = false;

                /**
                 * Adds the value to the map, along the way checking to see if 
there
are any non-null values for
                 * the row so far.
                 */
                @Override
                public String[] put(ParameterName key, String[] values) {
                        if (!hasNonEmptyValues) {
                                hasNonEmptyValues = (values != null) && 
(values.length > 0) &&
(values[0] != null) && (values[0].trim().length() > 0);

                        }
                        return super.put(key, values);
                }

                /** Returns true if the row had any non-empty values in it,
otherwise false. */
                public boolean hasNonEmptyValues() {
                        return this.hasNonEmptyValues;
                }
        }
}


On Mon, Feb 8, 2010 at 2:37 PM,  <[email protected]> wrote:
> Hey Stripes Users
>
> We have a lot of multi-page forms implemented with the @Wizard Annotation.
> This allows us to split required fields over multiple pages. We just noticed 
> that it
> Is possible to jump over given pages when changing the name="" Attribute of 
> the submit button.
>
> Since Stripes just validates the fields contained within "__fp" it is 
> possible to just enter the data
> On the first page and skip over the whole form process to the last page. The 
> last page then gets
> Displayed accordingly. Is this behavior intended?
>
> Is there a way to mark an EventHandler in a Wizard to validate all fields and 
> ignore the "__fp" parameter?
> We need to be sure that the user passed all steps in the process.
>
> Cheers,
> Yves Senn
>
>
> Sicherheitshinweis:
> Dieses E-Mail von PostFinance ist signiert. Weitere Informationen finden Sie 
> unter:
> https://www.postfinance.ch/e-signature.
> Geben Sie Ihre Sicherheitselemente niemals Dritten bekannt.
> ------------------------------------------------------------------------------
> The Planet: dedicated and managed hosting, cloud storage, colocation
> Stay online with enterprise data centers and the best network in the business
> Choose flexible plans and management services without long-term contracts
> Personal 24x7 support from experience hosting pros just a phone call away.
> http://p.sf.net/sfu/theplanet-com
> _______________________________________________
> Stripes-users mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/stripes-users
>
>



-- 
Richard Hauswald
Blog: http://tnfstacc.blogspot.com/
LinkedIn: http://www.linkedin.com/in/richardhauswald
Xing: http://www.xing.com/profile/Richard_Hauswald

------------------------------------------------------------------------------
The Planet: dedicated and managed hosting, cloud storage, colocation
Stay online with enterprise data centers and the best network in the business
Choose flexible plans and management services without long-term contracts
Personal 24x7 support from experience hosting pros just a phone call away.
http://p.sf.net/sfu/theplanet-com
_______________________________________________
Stripes-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-users

Sicherheitshinweis:
Dieses E-Mail von PostFinance ist signiert. Weitere Informationen finden Sie 
unter: 
https://www.postfinance.ch/e-signature.
Geben Sie Ihre Sicherheitselemente niemals Dritten bekannt.

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

------------------------------------------------------------------------------
SOLARIS 10 is the OS for Data Centers - provides features such as DTrace,
Predictive Self Healing and Award Winning ZFS. Get Solaris 10 NOW
http://p.sf.net/sfu/solaris-dev2dev
_______________________________________________
Stripes-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-users

Reply via email to