package extended.forms; import javax.jcr.Node; import javax.jcr.RepositoryException; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import info.magnolia.context.MgnlContext; import info.magnolia.jcr.util.NodeUtil; import info.magnolia.jcr.wrapper.I18nNodeWrapper; import info.magnolia.module.form.engine.EndView; import info.magnolia.module.form.engine.FormStateTokenMissingException; import info.magnolia.module.form.engine.FormStepState; import info.magnolia.module.form.engine.NoSuchFormStateException; import info.magnolia.module.form.engine.RedirectView; import info.magnolia.module.form.engine.RedirectWithTokenAndParametersView; import info.magnolia.module.form.engine.RedirectWithTokenView; import info.magnolia.module.form.engine.View; import info.magnolia.module.form.processors.FormProcessorFailedException; import info.magnolia.module.form.templates.components.FormParagraph; import info.magnolia.module.form.templates.components.multistep.NavigationUtils; import info.magnolia.module.form.templates.components.multistep.SubStepFormEngine; import info.magnolia.rendering.context.RenderingContext; /** * Custom form engine which saves data in the session when the user clicks 'back' instead of * discarding it. * Note: Besides the custom FormDataBinder {@link ExtendedFormDataBinder} that is used by * this enting, only two lines of code had to be added for this behavior, these start on line 107. */ public class ExtendedSubStepFormEngine extends SubStepFormEngine { private final Logger logger = LoggerFactory.getLogger(ExtendedSubStepFormEngine.class); /** * Constructor. * @param configurationNode the config node * @param configurationParagraph the config paragraph * @param startPage the start page * @param context the rendering context */ public ExtendedSubStepFormEngine(Node configurationNode, FormParagraph configurationParagraph, Node startPage, RenderingContext context) { super(configurationNode, configurationParagraph, startPage, context); } @Override public View handleRequest(Node content) throws RepositoryException { if (!isFormSubmission()) { String formStateToken; try { formStateToken = getFormStateToken(); } catch (FormStateTokenMissingException e) { return handleTokenMissing(); } try { setFormState(getFormState(formStateToken)); } catch (NoSuchFormStateException e) { return handleNoSuchFormState(e.getToken()); } View view = getFormState().getView(); getFormState().setView(null); if (view == null) { return getFormView(getFormState().getStep(NodeUtil.getNodeIdentifierIfPossible(content))); } if (getFormState().isEnded()) { destroyFormState(); } return view; } String formStateToken; final Node mainContent = new I18nNodeWrapper(context.getMainContent()); try { formStateToken = getFormStateToken(); } catch (FormStateTokenMissingException e) { // Cant post without a token... should never happen // Redirect the user to this page return new RedirectView(mainContent); } try { setFormState(getFormState(formStateToken)); } catch (NoSuchFormStateException e) { return handleNoSuchFormStateOnSubmit(e.getToken()); } View view = null; if (isBackButton()) { logger.debug("User pressed back button. Currently executing step number is {}", getFormState().getCurrentlyExecutingStep()); // Only the following two lines have been added to the default SubStepFormEngine: FormStepState step = getFormDataBinder().bind(content); getFormState().addStep(step); String pageUuid = getPreviousPage(); if (pageUuid == null) { return getFormView(getFormState().getStep(NodeUtil.getNodeIdentifierIfPossible(content))); } // update current step count getFormState().setCurrentlyExecutingStep(getFormState().getCurrentlyExecutingStep() - 1); logger.debug("Updated currently executing step. Now is {}", getFormState().getCurrentlyExecutingStep()); Node parent = new I18nNodeWrapper( NavigationUtils.findParagraphParentPage(NodeUtil.getNodeByIdentifier(content.getSession() .getWorkspace() .getName(), pageUuid))); if (isRedirectWithParams()) { return new RedirectWithTokenAndParametersView(parent, formStateToken); } return new RedirectWithTokenView(parent, formStateToken); } view = processSubmission(content); getFormState().setView(null); if (view instanceof EndView) { destroyFormState(); return view; } if (view instanceof RedirectWithTokenView) { return view; } if (view instanceof RedirectWithTokenAndParametersView) { return view; } getFormState().setView(view); if (isRedirectWithParams()) { return new RedirectWithTokenAndParametersView(mainContent, getFormState().getToken()); } return new RedirectWithTokenView(mainContent, getFormState().getToken()); } @Override protected ExtendedFormDataBinder getFormDataBinder() { ExtendedFormDataBinder formDataBinder = new ExtendedFormDataBinder(); formDataBinder.setI18nBasename(getConfigurationParagraph().getI18nBasename()); return formDataBinder; } /** * Performs the processing of submitted values. If this method returns a RedirectView this is treated like an exit * and the formState is removed from session. * * @param content */ private View processSubmission(Node content) throws RepositoryException { // Validate the input parameters and collect FormField instances FormStepState step = getFormDataBinder().bindAndValidate(content); // Add the submitted fields to formState getFormState().addStep(step); // If validation failed proceed and render page if (!step.isValid()) { return getValidationFailedView(step); } // Validation succeeded View validationSuccessfulView = getValidationSuccessfulView(getFormState()); if (validationSuccessfulView != null) { //on success we move to the next step, increase count by one getFormState().setCurrentlyExecutingStep(getFormState().getCurrentlyExecutingStep() + 1); logger.debug("Updated currently executing step. Now is {}", getFormState().getCurrentlyExecutingStep()); return validationSuccessfulView; } if (StringUtils.isNotEmpty(MgnlContext.getParameter("field"))) { return getValidationFailedView(step); } getFormState().setEnded(true); // Execute processors try { executeProcessors(getFormState().getValues()); } catch (FormProcessorFailedException e) { return getProcessorFailedView(e.getMessage()); } catch (Exception e) { logger.error("FormProcessor threw unexpected exception", e); return getProcessorFailedView(null); } // Render page with success message return getSuccessView(); } }