/**
* Copyright (C) 2009 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.HashMap;
import java.util.List;
import java.util.Map;
import org.bonitasoft.forms.client.i18n.FormsResourceBundle;
import org.bonitasoft.forms.client.model.ReducedApplicationConfig;
import org.bonitasoft.forms.client.model.ReducedHtmlTemplate;
import org.bonitasoft.forms.client.model.exception.ForbiddenApplicationAccessException;
import org.bonitasoft.forms.client.model.exception.MigrationProductVersionNotIdenticalException;
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.forms.client.view.widget.UserLogoutWidget;
import org.bonitasoft.web.rest.model.user.User;
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.http.client.URL;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
/**
* View controller of the form module
*
* @author Anthony Birembaut
*/
public class FormApplicationViewController {
protected static final String DEFAULT_USER_XP_URL = GWT.getModuleBaseURL() + "homepage";
/**
* The PICTURE_PLACEHOLDER
*/
protected static final String PICTURE_PLACEHOLDER = "themeResource?theme=portal&location=images/cleardot.gif";
/**
* Id of the element of the application template in witch the title has to be injected
*/
protected static final String APPLICATION_LABEL_ELEMENT_ID = "bonita_process_label";
/**
* Id of the element of the process template in witch the logout widget has to be injected
*/
protected static final String LOGOUT_WIDGET_ELEMENT_ID = "bonita_logout_box";
/**
* Id of the element of the process template in witch the logout link has to be injected
*/
protected static final String LOGOUT_BUTTON_ELEMENT_ID = "bonita_logout_button";
/**
* Id of the element of the process template in witch the username has to be injected
*/
protected static final String USERNAME_ELEMENT_ID = "bonita_username";
/**
* Id of the element of the process template in witch the refresh button has to be injected
*/
protected static final String REFRESH_BUTTON_ELEMENT_ID = "bonita_refresh_button";
/**
* Id of the element of the process template in witch the open to user experience button has to be injected
*/
protected static final String OPEN_USER_XP_ELEMENT_ID = "bonita_user_xp_link";
/**
* Logout Hidden constant
*/
public static final String LOGOUT_DISABLED = "logout.link.hidden";
/**
* Logout Visible constant
*/
public static final String LOGOUT_ENABLED = "logout.link.visible";
/**
* forms RPC service
*/
protected FormsServiceAsync formsServiceAsync;
/**
* Utility Class form DOM manipulation
*/
protected DOMUtils domUtils = DOMUtils.getInstance();
/**
* Utility Class form URL manipulation
*/
protected URLUtils urlUtils = URLUtilsFactory.getInstance();
/**
* Handler allowing to diplay the process forms config after the RPC call to retrieve its definition. this instance deals
* with the process template display
*/
protected ApplicationConfigHandler fullApplicationHandler = new ApplicationConfigHandler(true);
/**
* Handler allowing to diplay the process forms config after the RPC call to retrieve its definition this instance doesn't
* include template display
*/
protected ApplicationConfigHandler lightApplicationConfigHandler = new ApplicationConfigHandler(false);
/**
* mandatory form field symbol
*/
protected String mandatoryFieldSymbol;
/**
* mandatory form field label
*/
protected String mandatoryFieldLabel;
/**
* mandatory form field symbol classes
*/
protected String mandatoryFieldClasses;
/**
* The logged in user
*/
protected User user;
/**
* Id of the element in which to insert the page
*/
protected String elementId;
/**
* The form ID
*/
protected String formId;
/**
* The context of URL parameters
*/
protected Map<String, Object> urlContext;
/**
* Constructor.
*
* @param formId
* @param urlContext
* @param user
*/
public FormApplicationViewController(final String formId, final Map<String, Object> urlContext, final User user) {
this.formId = formId;
this.urlContext = urlContext;
this.user = user;
formsServiceAsync = RpcFormsServices.getFormsService();
}
/**
* create the view for the process template and the form page template
*
* @param formContainerId
* id of the element in which the form has to be placed (if null the form will be insterted at the
* end of the body)
*/
public void createInitialView(final String formContainerId) {
createView(formContainerId, true);
}
/**
* create the view for the form page at the end of the body
*/
public void createFormInitialView() {
createFormInitialView(null);
}
/**
* create the view for the form page in the specified node
*
* @param formContainerId
* id of the element in which the form has to be placed (if null the form will be insterted at the
* end of the body)
*/
public void createFormInitialView(final String formContainerId) {
createView(formContainerId, false);
}
/**
* create the view for the form page in the specified node
*
* @param formContainerId
* id of the element in which the form has to be placed (if null the form will be insterted at the
* end of the body)
* @param includeApplicationTemplate
* if true, include the process template otherwise just display the form
*/
protected void createView(final String formContainerId, final boolean includeApplicationTemplate) {
elementId = formContainerId;
ApplicationConfigHandler applicationConfigHandler = null;
if (includeApplicationTemplate) {
applicationConfigHandler = fullApplicationHandler;
} else {
applicationConfigHandler = lightApplicationConfigHandler;
}
formsServiceAsync.getApplicationConfig(formId, urlContext, includeApplicationTemplate, applicationConfigHandler);
}
/**
* Handler allowing to display the application template
*/
protected class ApplicationConfigHandler extends FormsAsyncCallback<ReducedApplicationConfig> {
protected boolean includeApplicationTemplate;
/**
* Constructor
*
* @param includeProcessTemplate
*/
public ApplicationConfigHandler(final boolean includeProcessTemplate) {
includeApplicationTemplate = includeProcessTemplate;
}
/**
* {@inheritDoc}
*/
@Override
public void onSuccess(final ReducedApplicationConfig applicationConfig) {
try {
if (includeApplicationTemplate) {
RequestBuilder theRequestBuilder;
final String theURL = urlUtils.buildLayoutURL(applicationConfig.getApplicationLayout().getBodyContentId(),
(String) urlContext.get(URLUtils.FORM_ID), (String) urlContext.get(URLUtils.TASK_ID_PARAM), false);
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 exception) {
final String errorMessage = FormsResourceBundle.getErrors().applicationConfigRetrievalError();
formsServiceAsync.getApplicationErrorTemplate(formId, urlContext, new ErrorPageHandler(null, formId, errorMessage, exception,
elementId));
}
@Override
public void onResponseReceived(final Request aRequest, final Response aResponse) {
HTMLPanel applicationHTMLPanel = null;
final ReducedHtmlTemplate applicationTemplate = applicationConfig.getApplicationLayout();
applicationTemplate.setBodyContent(aResponse.getText());
applicationHTMLPanel = new HTMLPanel(applicationTemplate.getBodyContent());
final String onloadAttributeValue = domUtils.insertApplicationTemplate(applicationTemplate.getHeadNodes(), applicationHTMLPanel,
applicationTemplate.getBodyAttributes());
if (applicationConfig.getApplicationLabel() != null && DOM.getElementById(APPLICATION_LABEL_ELEMENT_ID) != null) {
domUtils.insertInElement(applicationHTMLPanel, APPLICATION_LABEL_ELEMENT_ID, applicationConfig.getApplicationLabel());
}
if ((user.getFeatures() == null || user.getFeatures().contains(LOGOUT_ENABLED))
&& DOM.getElementById(LOGOUT_WIDGET_ELEMENT_ID) != null) {
final UserLogoutWidget logoutWidget = new UserLogoutWidget(user, urlContext);
applicationHTMLPanel.add(logoutWidget, LOGOUT_WIDGET_ELEMENT_ID);
}
if ((user.getFeatures() == null || user.getFeatures().contains(LOGOUT_ENABLED))
&& DOM.getElementById(LOGOUT_BUTTON_ELEMENT_ID) != null) {
Anchor logoutLink = null;
if (user.isAnonymous()) {
logoutLink = new Anchor(FormsResourceBundle.getMessages().loginButtonLabel());
} else {
logoutLink = new Anchor(FormsResourceBundle.getMessages().logoutButtonLabel());
}
logoutLink.setStyleName("bonita_logout_label");
final List<String> paramsToRemove = new ArrayList<String>();
paramsToRemove.add(URLUtils.LOCALE_PARAM);
final List<String> hashParamsToRemove = new ArrayList<String>();
if (user.isAutoLogin()) {
hashParamsToRemove.add(URLUtils.AUTO_LOGIN_PARAM);
} else {
hashParamsToRemove.add(URLUtils.USER_CREDENTIALS_PARAM);
}
final Map<String, String> hashParamsToAdd = new HashMap<String, String>();
hashParamsToAdd.put(URLUtils.TODOLIST_PARAM, "true");
hashParamsToAdd.put(URLUtils.VIEW_MODE_PARAM, "app");
final String tenant = Window.Location.getParameter(URLUtils.TENANT_PARAM);
final String theRedirectURL = urlUtils.rebuildUrl(paramsToRemove, null, hashParamsToRemove, hashParamsToAdd);
// relative path of the logout service regarding web.xml configuration
String theURL = GWT.getModuleBaseURL() + "../logoutservice?" + URLUtils.REDIRECT_URL_PARAM + "=";
try {
theURL += URL.encodeQueryString(theRedirectURL);
} catch (final Exception e) {
Window.alert("Unable to redirect to login page: Invalid URL");
theURL += GWT.getModuleBaseURL();
}
if (tenant != null) {
theURL += "&" + URLUtils.TENANT_PARAM + "=" + tenant;
}
logoutLink.setHref(theURL);
applicationHTMLPanel.add(logoutLink, LOGOUT_BUTTON_ELEMENT_ID);
}
if (DOM.getElementById(USERNAME_ELEMENT_ID) != null) {
Label usernameLabel = null;
if (user.isAnonymous()) {
usernameLabel = new Label("");
} else {
usernameLabel = new Label(user.getUsername());
}
usernameLabel.setStyleName("bonita_username_label");
applicationHTMLPanel.add(usernameLabel, USERNAME_ELEMENT_ID);
}
if (DOM.getElementById(REFRESH_BUTTON_ELEMENT_ID) != null) {
final Label refreshButton = new Label(FormsResourceBundle.getMessages().refreshButtonLabel());
refreshButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
Window.Location.reload();
}
});
applicationHTMLPanel.add(refreshButton, REFRESH_BUTTON_ELEMENT_ID);
}
if (DOM.getElementById(OPEN_USER_XP_ELEMENT_ID) != null && !user.isAutoLogin()) {
final Image userXPIcon = new Image(PICTURE_PLACEHOLDER);
userXPIcon.setStyleName("bonita_user_xp_icon");
final Label userXPLabel = new Label(FormsResourceBundle.getMessages().openUserXPButtonLabel());
userXPLabel.setStyleName("bonita_user_xp_label");
userXPLabel.addClickHandler(goToPortalHandler(applicationConfig));
userXPIcon.setTitle(FormsResourceBundle.getMessages().openUserXPButtonTitle());
userXPIcon.addClickHandler(goToPortalHandler(applicationConfig));
applicationHTMLPanel.add(userXPIcon, OPEN_USER_XP_ELEMENT_ID);
applicationHTMLPanel.add(userXPLabel, OPEN_USER_XP_ELEMENT_ID);
}
if (onloadAttributeValue != null) {
domUtils.javascriptEval(onloadAttributeValue);
}
buildPageFlow(applicationConfig, applicationHTMLPanel);
}
});
theRequestBuilder.send();
} else {
buildPageFlow(applicationConfig, null);
}
} catch (final RequestException e) {
GWT.log("Error while trying to query the form layout :" + e.getMessage());
}
}
@Override
public void onUnhandledFailure(final Throwable caught) {
try {
throw caught;
} catch (final ForbiddenApplicationAccessException e) {
formsServiceAsync.logout(new AsyncCallback<Void>() {
@Override
public void onFailure(final Throwable t) {
urlUtils.showLoginView();
}
@Override
public void onSuccess(final Void result) {
Window.alert(FormsResourceBundle.getMessages().forbiddenApplicationReadMessage());
urlUtils.showLoginView();
}
});
} catch (final MigrationProductVersionNotIdenticalException e) {
final String errorMessage = FormsResourceBundle.getMessages().migrationProductVersionMessage();
formsServiceAsync.getApplicationErrorTemplate(formId, urlContext, new ErrorPageHandler(null, formId, errorMessage, elementId));
} catch (final Throwable t) {
final String errorMessage = FormsResourceBundle.getErrors().applicationConfigRetrievalError();
formsServiceAsync.getApplicationErrorTemplate(formId, urlContext, new ErrorPageHandler(null, formId, errorMessage, t, elementId));
}
}
}
private ClickHandler goToPortalHandler(final ReducedApplicationConfig applicationConfig) {
return new ClickHandler() {
@Override
public void onClick(final ClickEvent event) {
String userXPURL = applicationConfig.getUserXPURL();
if (userXPURL == null) {
userXPURL = DEFAULT_USER_XP_URL;
}
urlUtils.windowAssign(userXPURL + "#?" + URLUtils.CONSOLE_LOCALE_PARAM + "=" + urlUtils.getLocale());
}
};
}
void buildPageFlow(final ReducedApplicationConfig applicationConfig, final HTMLPanel applicationHTMLPanel) {
mandatoryFieldSymbol = applicationConfig.getMandatorySymbol();
mandatoryFieldLabel = applicationConfig.getMandatoryLabel();
mandatoryFieldClasses = applicationConfig.getMandatoryStyle();
final PageflowViewController formPagesViewController = FormViewControllerFactory.getPageflowViewController(formId, urlContext, user, elementId,
applicationHTMLPanel);
formPagesViewController.setMandatoryFieldSymbol(mandatoryFieldSymbol);
formPagesViewController.setMandatoryFieldLabel(mandatoryFieldLabel);
formPagesViewController.setMandatoryFieldClasses(mandatoryFieldClasses);
if (formId != null) {
formPagesViewController.createForm();
} else {
final String errorMessage = FormsResourceBundle.getMessages().inboxEmptyMessage();
formsServiceAsync.getApplicationErrorTemplate(formId, urlContext, new ErrorPageHandler(applicationHTMLPanel, formId, errorMessage, elementId));
}
}
/**
* Handler allowing to get a temporary token to access the user XP
*/
protected class GenerateTemporaryTokenHandler implements AsyncCallback<String> {
protected String userXPURL;
/**
* Constructor
*
* @param userXPURL
*/
public GenerateTemporaryTokenHandler(final String userXPURL) {
super();
this.userXPURL = userXPURL;
}
/**
* {@inheritDoc}
*/
@Override
public void onSuccess(final String temporaryToken) {
urlUtils.windowAssign(userXPURL + "?" + URLUtils.USER_CREDENTIALS_PARAM + "=" + temporaryToken + "#?" + URLUtils.CONSOLE_LOCALE_PARAM + "="
+ urlUtils.getLocale());
}
/**
* {@inheritDoc}
*/
@Override
public void onFailure(final Throwable t) {
urlUtils.windowAssign(userXPURL + "#?" + URLUtils.CONSOLE_LOCALE_PARAM + "=" + urlUtils.getLocale());
}
}
}