/** * Copyright (C) 2011 BonitaSoft S.A. * BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2.0 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.bonitasoft.forms.client.view.controller; import java.util.ArrayList; import java.util.Map; import org.bonitasoft.forms.client.i18n.FormsResourceBundle; import org.bonitasoft.forms.client.model.ReducedFormPage; import org.bonitasoft.forms.client.model.exception.AbortedFormException; import org.bonitasoft.forms.client.model.exception.CanceledFormException; import org.bonitasoft.forms.client.model.exception.ForbiddenFormAccessException; import org.bonitasoft.forms.client.model.exception.FormAlreadySubmittedException; import org.bonitasoft.forms.client.model.exception.FormInErrorException; import org.bonitasoft.forms.client.model.exception.IllegalActivityTypeException; import org.bonitasoft.forms.client.model.exception.MigrationProductVersionNotIdenticalException; import org.bonitasoft.forms.client.model.exception.SkippedFormException; import org.bonitasoft.forms.client.rpc.FormsServiceAsync; import org.bonitasoft.forms.client.view.FormsAsyncCallback; import org.bonitasoft.forms.client.view.common.DOMUtils; import org.bonitasoft.forms.client.view.common.RpcFormsServices; import org.bonitasoft.forms.client.view.common.URLUtils; import org.bonitasoft.forms.client.view.common.URLUtilsFactory; import org.bonitasoft.web.rest.model.user.User; import org.bonitasoft.web.toolkit.client.common.json.JSonSerializer; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.http.client.Request; import com.google.gwt.http.client.RequestBuilder; import com.google.gwt.http.client.RequestCallback; import com.google.gwt.http.client.RequestException; import com.google.gwt.http.client.Response; import com.google.gwt.json.client.JSONArray; import com.google.gwt.json.client.JSONParser; import com.google.gwt.json.client.JSONValue; import com.google.gwt.user.client.Cookies; import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.RootPanel; /** * Controller dealing with Task form controls before display * * @author Ruiheng Fan */ public class PageflowViewController { /** * forms RPC service */ protected FormsServiceAsync formsServiceAsync; /** * Pages view controller (handles the page flow) */ protected FormPagesViewController formPagesViewController; /** * Utility Class form DOM manipulation */ protected DOMUtils domUtils = DOMUtils.getInstance(); /** * Utility Class form URL manipulation */ protected URLUtils urlUtils = URLUtilsFactory.getInstance(); /** * mandatory form field symbol */ protected String mandatoryFieldSymbol; /** * mandatory form field label */ protected String mandatoryFieldLabel; /** * mandatory form field symbol classes */ protected String mandatoryFieldClasses; /** * The formID UUID retrieved from the request as a String */ protected String formID; /** * Application template panel (can be null in form only mode) */ protected HTMLPanel applicationHTMLPanel; /** * The logged in user */ protected User user; /** * Handler allowing to retrieve the first page */ protected FirstPageHandler firstPageHandler = new FirstPageHandler(); /** * The context of URL parameters */ protected Map<String, Object> urlContext; /** * Id of the element in which to insert the page */ protected String elementId; protected static String ALREADY_STARTED_ARRAY_COOKIE_KEY = "AlreadyStartedProcessId"; /** * Constructor * * @param formID * @param urlContext * @param user * @param applicationHTMLPanel */ public PageflowViewController(final String formID, final Map<String, Object> urlContext, final User user, final String elementId, final HTMLPanel applicationHTMLPanel) { this.formID = formID; this.urlContext = urlContext; this.user = user; this.elementId = elementId; this.applicationHTMLPanel = applicationHTMLPanel; formsServiceAsync = RpcFormsServices.getFormsService(); } /** * create the view for the form */ public void createForm() { formsServiceAsync.getFormFirstPage(formID, urlContext, firstPageHandler); } /** * Handler allowing to retrieve the page list */ protected class FirstPageHandler extends FormsAsyncCallback<ReducedFormPage> { /** * {@inheritDoc} */ @Override public void onSuccess(final ReducedFormPage firstPage) { if (firstPage != null) { try { RequestBuilder theRequestBuilder; final String theURL = urlUtils.buildLayoutURL(firstPage.getPageTemplate().getBodyContentId(), (String) urlContext.get(URLUtils.FORM_ID), (String) urlContext.get(URLUtils.TASK_ID_PARAM), true); GWT.log("Calling the Form Layout Download Servlet with query: " + theURL); theRequestBuilder = new RequestBuilder(RequestBuilder.GET, theURL); theRequestBuilder.setCallback(new RequestCallback() { @Override public void onError(final Request aRequest, final Throwable anException) { final String errorMessage = FormsResourceBundle.getErrors().applicationConfigRetrievalError(); formsServiceAsync.getApplicationErrorTemplate(firstPage.getPageId(), urlContext, new ErrorPageHandler(null, firstPage.getPageId(), errorMessage, anException, elementId)); } @Override public void onResponseReceived(final Request request, final Response response) { firstPage.getPageTemplate().setBodyContent(response.getText()); formPagesViewController = FormViewControllerFactory.getFormPagesViewController(formID, urlContext, firstPage, applicationHTMLPanel, user, elementId); formPagesViewController.setMandatoryFieldSymbol(mandatoryFieldSymbol); formPagesViewController.setMandatoryFieldLabel(mandatoryFieldLabel); formPagesViewController.setMandatoryFieldClasses(mandatoryFieldClasses); formPagesViewController.displayPage(0); } }); theRequestBuilder.send(); } catch (final RequestException e) { GWT.log("Error while trying to query the form layout :" + e.getMessage()); } } else { final String processUUIDStr = (String) urlContext.get(URLUtils.PROCESS_ID_PARAM); final String instanceUUIDStr = (String) urlContext.get(URLUtils.INSTANCE_ID_PARAM); if (processUUIDStr != null) { final String autoInstantiate = (String) urlContext.get(URLUtils.AUTO_INSTANTIATE); // retrieve the already started cookie value final String processIdArrayAsString = Cookies.getCookie(ALREADY_STARTED_ARRAY_COOKIE_KEY); final ArrayList<String> processIdArray = new ArrayList<String>(); boolean processIdIsInCookie = false; // build the new array to set in the cookie after consuming if (processIdArrayAsString != null) { final JSONValue jsonValue = JSONParser.parseStrict(processIdArrayAsString); final JSONArray jsonArray = jsonValue.isArray(); if (jsonArray != null) { for (int i = 0; i < jsonArray.size(); i++) { final String cookieValue = jsonArray.get(i).isString().stringValue(); if (!processUUIDStr.equals(cookieValue)) { processIdArray.add(cookieValue); } else { processIdIsInCookie = true; } } } } final ConfirmationPageHandler confirmationPageHandler = createConfirmationPageHandler(); final FormTerminationHandler formTerminationHandler = new FormTerminationHandler(confirmationPageHandler); // if the parameter autoInstanciate is set explicitly to false, the we skip the form if (!Boolean.FALSE.toString().equals(autoInstantiate) || processIdIsInCookie) { // write the cookie without the process id Cookies.setCookie(ALREADY_STARTED_ARRAY_COOKIE_KEY, JSonSerializer.serialize(processIdArray)); formsServiceAsync.skipForm(formID, urlContext, formTerminationHandler); } else { final FlowPanel buttonContainer = new FlowPanel(); confirmationPageHandler.setCurrentPageHTMLPanel(buttonContainer); buttonContainer.setStyleName("bonita_form_button_container"); buttonContainer.add(createStartProcessInstanceButton(formTerminationHandler)); addStartProcessInstanceContainer(buttonContainer); } } else if (instanceUUIDStr != null) { final String errorMessage = FormsResourceBundle.getErrors().nothingToDisplay(); formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } else { formsServiceAsync.skipForm(formID, urlContext, new FormTerminationHandler(createConfirmationPageHandler())); } } } @Override public void onUnhandledFailure(final Throwable caught) { try { throw caught; } catch (final ForbiddenFormAccessException e) { String errorMessage; if (urlContext.containsKey(URLUtils.PROCESS_ID_PARAM)) { errorMessage = FormsResourceBundle.getMessages().forbiddenProcessStartMessage(); } else { errorMessage = FormsResourceBundle.getMessages().forbiddenStepReadMessage(); } formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } catch (final MigrationProductVersionNotIdenticalException e) { final String errorMessage = FormsResourceBundle.getMessages().migrationProductVersionMessage(); formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } catch (final CanceledFormException e) { final String errorMessage = FormsResourceBundle.getMessages().cancelledTaskMessage(); formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); // } catch (SuspendedFormException e) { // final String errorMessage = FormsResourceBundle.getMessages().suspendedTaskMessage(); // formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, // elementId)); } catch (final AbortedFormException e) { final String errorMessage = FormsResourceBundle.getMessages().abortedFormMessage(); formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } catch (final FormInErrorException e) { final String errorMessage = FormsResourceBundle.getMessages().errorTaskMessage(); formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } catch (final SkippedFormException e) { final String errorMessage = FormsResourceBundle.getMessages().skippedFormMessage(); formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } catch (final FormAlreadySubmittedException e) { final String errorMessage = FormsResourceBundle.getErrors().formAlreadySubmittedError(); formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } catch (final Throwable t) { final String errorMessage = FormsResourceBundle.getErrors().pageListRetrievalError(); formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } } } protected Button createStartProcessInstanceButton(final FormTerminationHandler formTerminationHandler) { final Button startInstanceButton = new Button(FormsResourceBundle.getMessages().caseStartButtonLabel()); startInstanceButton.setStyleName("bonita_form_button"); startInstanceButton.addClickHandler(new ClickHandler() { @Override public void onClick(final ClickEvent event) { startInstanceButton.setEnabled(false); formsServiceAsync.skipForm(formID, urlContext, formTerminationHandler); } }); return startInstanceButton; } protected void addStartProcessInstanceContainer(final FlowPanel buttonContainer) { domUtils.hideLoading(); if (applicationHTMLPanel != null) { applicationHTMLPanel.add(buttonContainer, DOMUtils.DEFAULT_FORM_ELEMENT_ID); } else { RootPanel.get(DOMUtils.STATIC_CONTENT_ELEMENT_ID).add(buttonContainer); } } /** * Handler to deal with what happens after a form has been submitted or skipped */ protected class FormTerminationHandler extends FormsAsyncCallback<Map<String, Object>> { private ConfirmationPageHandler confirmationPageHandler; private final String actionForNotif; public FormTerminationHandler(final ConfirmationPageHandler confirmationPageHandler) { setConfirmationPageHandler(confirmationPageHandler); actionForNotif = domUtils.getActionForNotif(urlContext); } /** * {@inheritDoc} */ @Override public void onSuccess(final Map<String, Object> newContext) { if (domUtils.isPageInFrame()) { domUtils.notifyParentFrameSuccess(actionForNotif); } if (!"false".equals(urlUtils.getHashParameter(URLUtils.DISPLAY_CONFIRMATION))) { urlContext.putAll(newContext); redirectToConfirmationPage(getConfirmationPageHandler()); } } /** * @return the confirmationPageHandler */ public ConfirmationPageHandler getConfirmationPageHandler() { return confirmationPageHandler; } /** * @param confirmationPageHandler * the confirmationPageHandler to set */ public void setConfirmationPageHandler(final ConfirmationPageHandler confirmationPageHandler) { this.confirmationPageHandler = confirmationPageHandler; } @Override public void onUnhandledFailure(final Throwable caught) { try { throw caught; } catch (final IllegalActivityTypeException t) { final String errorMessage = FormsResourceBundle.getErrors().taskFormSkippedError(); if (domUtils.isPageInFrame()) { domUtils.notifyParentFrameError("taskFormSkippedError", actionForNotif); } if (!"false".equals(urlUtils.getHashParameter(URLUtils.DISPLAY_CONFIRMATION))) { formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } } catch (final FormAlreadySubmittedException t) { final String errorMessage = FormsResourceBundle.getErrors().formAlreadySubmittedOrCancelledError(); if (domUtils.isPageInFrame()) { domUtils.notifyParentFrameError("formAlreadySubmittedOrCancelledError", actionForNotif); } if (!"false".equals(urlUtils.getHashParameter(URLUtils.DISPLAY_CONFIRMATION))) { formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, elementId)); } } catch (final Throwable t) { final String errorMessage = FormsResourceBundle.getErrors().taskExecutionError(); if (domUtils.isPageInFrame()) { domUtils.notifyParentFrameError("taskExecutionError", actionForNotif); } if (!"false".equals(urlUtils.getHashParameter(URLUtils.DISPLAY_CONFIRMATION))) { formsServiceAsync.getApplicationErrorTemplate(formID, urlContext, new ErrorPageHandler(applicationHTMLPanel, formID, errorMessage, t, elementId)); } } } } public void setMandatoryFieldSymbol(final String mandatoryFieldSymbol) { this.mandatoryFieldSymbol = mandatoryFieldSymbol; } public void setMandatoryFieldClasses(final String mandatoryFieldClasses) { this.mandatoryFieldClasses = mandatoryFieldClasses; } public void setMandatoryFieldLabel(final String mandatoryFieldLabel) { this.mandatoryFieldLabel = mandatoryFieldLabel; } protected void redirectToConfirmationPage(final ConfirmationPageHandler confirmationPageHandler) { formsServiceAsync.getFormConfirmationTemplate(formID, urlContext, null, confirmationPageHandler); } protected ConfirmationPageHandler createConfirmationPageHandler() { return new ConfirmationPageHandler(applicationHTMLPanel, elementId, getDefaultConfirmationMessage(), formID, urlContext); } private String getDefaultConfirmationMessage() { String confirmationMessage = null; if (urlContext.containsKey(URLUtils.INSTANCE_ID_PARAM)) { confirmationMessage = FormsResourceBundle.getMessages().instanceSubmissionConfirmationMessage((String) urlContext.get(URLUtils.INSTANCE_ID_PARAM)); } else { confirmationMessage = FormsResourceBundle.getMessages().submissionConfirmationMessage(); } return confirmationMessage; } }