FormXPage.java
/*
* Copyright (c) 2002-2022, City of Paris
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright notice
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice
* and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* License 1.0
*/
package fr.paris.lutece.plugins.forms.web;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import fr.paris.lutece.plugins.forms.business.*;
import fr.paris.lutece.plugins.genericattributes.business.ResponseHome;
import fr.paris.lutece.portal.service.util.AppLogService;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import fr.paris.lutece.plugins.forms.exception.FormNotFoundException;
import fr.paris.lutece.plugins.forms.exception.MaxFormResponseException;
import fr.paris.lutece.plugins.forms.exception.QuestionValidationException;
import fr.paris.lutece.plugins.forms.service.FormService;
import fr.paris.lutece.plugins.forms.service.entrytype.EntryTypeAutomaticFileReading;
import fr.paris.lutece.plugins.forms.service.upload.FormsAsynchronousUploadHandler;
import fr.paris.lutece.plugins.forms.util.FormsConstants;
import fr.paris.lutece.plugins.forms.util.FormsResponseUtils;
import fr.paris.lutece.plugins.forms.util.FormsUtils;
import fr.paris.lutece.plugins.forms.web.breadcrumb.IBreadcrumb;
import fr.paris.lutece.plugins.forms.web.entrytype.DisplayType;
import fr.paris.lutece.plugins.forms.web.http.SynchronousHttpServletRequestWrapper;
import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError;
import fr.paris.lutece.plugins.genericattributes.business.Response;
import fr.paris.lutece.plugins.genericattributes.service.entrytype.IEntryTypeService;
import fr.paris.lutece.portal.business.file.FileHome;
import fr.paris.lutece.portal.business.physicalfile.PhysicalFileHome;
import fr.paris.lutece.portal.service.accesscontrol.AccessControlService;
import fr.paris.lutece.portal.service.admin.AccessDeniedException;
import fr.paris.lutece.portal.service.captcha.CaptchaSecurityService;
import fr.paris.lutece.portal.service.captcha.ICaptchaSecurityService;
import fr.paris.lutece.portal.service.i18n.I18nService;
import fr.paris.lutece.portal.service.message.SiteMessage;
import fr.paris.lutece.portal.service.message.SiteMessageException;
import fr.paris.lutece.portal.service.message.SiteMessageService;
import fr.paris.lutece.portal.service.security.LuteceUser;
import fr.paris.lutece.portal.service.security.SecurityService;
import fr.paris.lutece.portal.service.security.SecurityTokenService;
import fr.paris.lutece.portal.service.security.UserNotSignedException;
import fr.paris.lutece.portal.service.spring.SpringContextService;
import fr.paris.lutece.portal.service.util.AppPathService;
import fr.paris.lutece.portal.service.util.AppPropertiesService;
import fr.paris.lutece.portal.util.mvc.commons.annotations.Action;
import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
import fr.paris.lutece.portal.util.mvc.utils.MVCUtils;
import fr.paris.lutece.portal.util.mvc.xpage.MVCApplication;
import fr.paris.lutece.portal.util.mvc.xpage.annotations.Controller;
import fr.paris.lutece.portal.web.upload.MultipartHttpServletRequest;
import fr.paris.lutece.portal.web.xpages.XPage;
import fr.paris.lutece.util.url.UrlItem;
import org.apache.james.mime4j.dom.datetime.DateTime;
/**
*
* Controller for form display
*
*/
@Controller( xpageName = FormXPage.XPAGE_NAME, pageTitleI18nKey = FormXPage.MESSAGE_PAGE_TITLE, pagePathI18nKey = FormXPage.MESSAGE_PATH )
public class FormXPage extends MVCApplication
{
protected static final String XPAGE_NAME = "forms";
private static final long serialVersionUID = -8380962697376893817L;
// Messages
protected static final String MESSAGE_PAGE_TITLE = "forms.xpage.form.view.pageTitle";
protected static final String MESSAGE_PATH = "forms.xpage.form.view.pagePathLabel";
protected static final String MESSAGE_ERROR_NOT_RESPONSE_AGAIN_FORM = "forms.xpage.form.error.limitNumberResponse";
protected static final String MESSAGE_ERROR_CONTROL = "forms.xpage.form.error.control";
protected static final String MESSAGE_LIST_FORMS_PAGETITLE = "forms.xpage.listForms.pagetitle";
protected static final String MESSAGE_LIST_FORMS_PATHLABEL = "forms.xpage.listForms.pathlabel";
private static final String MESSAGE_WARNING_LOST_SESSION = "forms.warning.lost.session";
private static final String MESSAGE_WARNING_CAPTCHA = "portal.admin.message.wrongCaptcha";
private static final String MESSAGE_WARNING_INACTIVE_STATE_BYPASSED = "forms.warning.inactive.state.bypassed";
private static final String MESSAGE_ERROR_TOKEN = "Invalid security token";
// Views
private static final String VIEW_LIST_FORM = "listForm";
private static final String VIEW_STEP = "stepView";
// Actions
private static final String ACTION_SAVE_FORM_RESPONSE = "doSaveResponse";
private static final String ACTION_SAVE_STEP = "doSaveStep";
private static final String ACTION_RESET_BACKUP = "doResetBackup";
private static final String ACTION_SAVE_FOR_BACKUP = "doSaveForBackup";
private static final String ACTION_PREVIOUS_STEP = "doReturnStep";
private static final String ACTION_GO_TO_STEP = "doGoToStep";
private static final String ACTION_FORM_RESPONSE_SUMMARY = "formResponseSummary";
private static final String ACTION_SAVE_FORM_RESPONSE_SUMMARY = "doSaveResponseSummary";
// Actions
private static final String ACTION_UPLOAD = "doSynchronousUploadDocument";
private static final String ACTION_ADD_ITERATION = "addIteration";
private static final String ACTION_REMOVE_ITERATION = "removeIteration";
// Templates
private static final String TEMPLATE_VIEW_STEP = "/skin/plugins/forms/step_view.html";
private static final String TEMPLATE_FORM_SUBMITTED = "/skin/plugins/forms/form_submitted_view.html";
private static final String TEMPLATE_VIEW_FORM_RESPONSE_SUMMARY = "/skin/plugins/forms/form_response_summary.html";
private static final String TEMPLATE_LIST_FORMS = "skin/plugins/forms/list_forms.html";
// Markers
private static final String STEP_HTML_MARKER = "stepContent";
private static final String MARK_LIST_SUMMARY_STEP_DISPLAY = "list_summary_step_display";
private static final String MARK_FORM_LIST = "form_list";
private static final String MARK_DISPLAY_CAPTCHA = "display_captcha";
private static final String MARK_CAPTCHA = "captcha";
// Other
private static FormService _formService = SpringContextService.getBean( FormService.BEAN_NAME );
private ICaptchaSecurityService _captchaSecurityService = new CaptchaSecurityService( );
// Attributes
private FormResponseManager _formResponseManager;
private Step _currentStep;
private StepDisplayTree _stepDisplayTree;
private IBreadcrumb _breadcrumb;
private boolean _bInactiveStateBypassed;
private boolean IsRequestComingFromAction = false;
/**
* Return the default XPage with the list of all available Form
*
* @param request
* The HttpServletRequest
* @return the list of all available forms
*/
@View( value = VIEW_LIST_FORM, defaultView = true )
public XPage getListFormView( HttpServletRequest request )
{
Locale locale = getLocale( request );
List<Form> listFormsAll = FormHome.getFormList( );
for ( Form form : listFormsAll )
{
if ( form.isCountResponses( ) )
{
form.setCurrentNumberResponse( FormHome.getNumberOfResponseForms( form.getId( ) ) );
}
if ( form.getLogo( ) != null )
{
form.setLogo( FileHome.findByPrimaryKey( form.getLogo( ).getIdFile( ) ) );
form.getLogo( ).setPhysicalFile( PhysicalFileHome.findByPrimaryKey( form.getLogo( ).getPhysicalFile( ).getIdPhysicalFile( ) ) );
}
}
Map<String, Object> model = getModel( );
model.put( MARK_FORM_LIST, listFormsAll );
XPage xPage = getXPage( TEMPLATE_LIST_FORMS, locale, model );
xPage.setTitle( I18nService.getLocalizedString( MESSAGE_LIST_FORMS_PAGETITLE, locale ) );
xPage.setPathLabel( I18nService.getLocalizedString( MESSAGE_LIST_FORMS_PATHLABEL, locale ) );
return xPage;
}
/**
*
* @param form
* the given Form
* @param request
* The Http request
* @throws UserNotSignedException
* Exception
*/
private void checkAuthentication( Form form, HttpServletRequest request ) throws UserNotSignedException
{
try
{
checkMyLuteceAuthentification( form, request );
}
catch( UserNotSignedException e )
{
init( form.getId( ) );
throw new UserNotSignedException( );
}
}
/**
* check if authentification
*
* @param form
* Form
* @param request
* HttpServletRequest
* @throws UserNotSignedException
* exception if the form requires an authentification and the user is not logged
*/
private void checkMyLuteceAuthentification( Form form, HttpServletRequest request ) throws UserNotSignedException
{
// Try to register the user in case of external authentication
if ( !SecurityService.isAuthenticationEnable( ) )
{
return;
}
if ( SecurityService.getInstance( ).isExternalAuthentication( ) )
{
// The authentication is external
// Should register the user if it's not already done
if ( SecurityService.getInstance( ).getRegisteredUser( request ) == null && SecurityService.getInstance( ).getRemoteUser( request ) == null
&& form.isAuthentificationNeeded( ) )
{
// Authentication is required to access to the portal
throw new UserNotSignedException( );
}
}
else
{
// If portal authentication is enabled and user is null and the requested URL
// is not the login URL, user cannot access to Portal
if ( ( form.isAuthentificationNeeded( ) ) && ( SecurityService.getInstance( ).getRegisteredUser( request ) == null )
&& !SecurityService.getInstance( ).isLoginUrl( request ) )
{
// Authentication is required to access to the portal
throw new UserNotSignedException( );
}
}
}
/**
*
* @param request
* The Http request
* @return the XPage
*
* @throws SiteMessageException
* Exception
* @throws UserNotSignedException
* Exception
*/
@View( value = VIEW_STEP )
public synchronized XPage getStepView( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException
{
String paramInit = request.getParameter( FormsConstants.PARAMETER_INIT );
if ( Boolean.parseBoolean( paramInit ) )
{
init( request );
}
int nIdForm = NumberUtils.toInt( request.getParameter( FormsConstants.PARAMETER_ID_FORM ), FormsConstants.DEFAULT_ID_VALUE );
if ( nIdForm != FormsConstants.DEFAULT_ID_VALUE && ( _currentStep == null || nIdForm != _currentStep.getIdForm( ) ) )
{
init( nIdForm );
}
if ( _currentStep == null )
{
// Throws Exception
SiteMessageService.setMessage( request, FormsConstants.MESSAGE_ERROR_NO_STEP, SiteMessage.TYPE_ERROR );
return null;
}
Form form = FormHome.findByPrimaryKey( _currentStep.getIdForm( ) );
checkAuthentication( form, request );
LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
if ( user != null && !(FormsResponseUtils.checkIfUserResponseForm( form, user.getName( ) ) ) )
{
SiteMessageService.setMessage( request, MESSAGE_ERROR_NOT_RESPONSE_AGAIN_FORM, SiteMessage.TYPE_ERROR );
}
if ( !(FormsResponseUtils.checkNumberMaxResponseForm( form ) ) )
{
SiteMessageService.setMessage( request, FormsConstants.MESSAGE_ERROR_NUMBER_MAX_RESPONSE_FORM, SiteMessage.TYPE_ERROR );
}
String strTitleForm = I18nService.getLocalizedString( FormsConstants.MESSAGE_STEP_TITLE, new String [ ] {
form.getTitle( ), _currentStep.getTitle( )
}, getLocale( request ) );
String strPathForm = form.getTitle( );
Map<String, Object> model = getModel( );
if ( form.isActive( ) || bypassInactiveState( form, request ) )
{
if ( _breadcrumb == null )
{
_breadcrumb = SpringContextService.getBean( form.getBreadcrumbName( ) );
}
if(_formResponseManager != null && !_formResponseManager.getIsBackupResponseAlreadyInitiated() && _formResponseManager.getIsResponseLoadedFromBackup()) {
_formResponseManager.setBackupResponseAlreadyInitiated(true);
}
initFormResponseManager( request, form );
if ( _formResponseManager.getFormResponse( ).isFromSave( ) )
{
String strActionNextStep = request.getParameter( "action_" + ACTION_SAVE_STEP );
if (strActionNextStep == null && _formResponseManager.getCurrentStep() != null) {
_currentStep = _formResponseManager.getCurrentStep( );
_stepDisplayTree = new StepDisplayTree( _currentStep.getId( ), _formResponseManager.getFormResponse( ) );
}
Object [ ] args = {
_formResponseManager.getFormResponseUpdateDate(),
};
model.put( FormsConstants.MARK_INFO, I18nService.getLocalizedString( FormsConstants.MESSAGE_LOAD_BACKUP, args, getLocale( request ) ) );
}
if ( _stepDisplayTree == null || _currentStep.getId( ) != _stepDisplayTree.getStep( ).getId( ) )
{
_stepDisplayTree = new StepDisplayTree( _currentStep.getId( ), _formResponseManager.getFormResponse( ) );
if(!_formResponseManager.getValidatedSteps().contains(_currentStep.getId())) {
_formResponseManager.add(_currentStep);
}
}
if ( !_formResponseManager.getFormResponse( ).isFromSave( ) && !bypassInactiveState( form, request ) )
{
XPage accessControlPage = AccessControlService.getInstance( ).doExecuteAccessControl( request, form.getId( ), Form.RESOURCE_TYPE,
_formResponseManager );
if ( accessControlPage != null )
{
return accessControlPage;
}
}
getFormStepModel( form, request, model );
}
else
{
if ( StringUtils.isNotEmpty( form.getUnavailableMessage( ) ) )
{
SiteMessageService.setCustomMessage( request, form.getUnavailableMessage( ), SiteMessage.TYPE_ERROR );
}
else
{
SiteMessageService.setMessage( request, FormsConstants.MESSAGE_ERROR_INACTIVE_FORM, SiteMessage.TYPE_ERROR );
}
}
IsRequestComingFromAction = true;
XPage xPage = getXPage( TEMPLATE_VIEW_STEP, getLocale( request ), model );
xPage.setTitle( strTitleForm );
xPage.setPathLabel( strPathForm );
return xPage;
}
/**
* Does the request contain parameters to bypass the inactive state of the form
*
* @param form
* the forme
* @param request
* thre request
* @return <code>true</code> if the request contains valid bypass parameters, <code>false</code> otherwise
*/
private boolean bypassInactiveState( Form form, HttpServletRequest request )
{
if ( _bInactiveStateBypassed )
{
return true;
}
String strTimestamp = request.getParameter( FormsConstants.PARAMETER_TIMESTAMP );
String strToken = request.getParameter( FormsConstants.PARAMETER_TOKEN_BYPASS );
if ( StringUtils.isBlank( strToken ) || !StringUtils.isNumeric( strTimestamp ) )
{
return false;
}
String refToken = FormsUtils.getInactiveBypassToken( form, strTimestamp );
if ( !refToken.equals( strToken ) )
{
return false;
}
long now = new Date( ).getTime( );
long timestampAge = now - Long.parseLong( strTimestamp );
if ( timestampAge < 0 )
{
return false;
}
long lBypassDuration = AppPropertiesService.getPropertyLong( FormsConstants.PROPERTY_INACTIVE_BYPASS_DURATION_MILLISECONDS, 1000L * 60 * 30 ); // Half
// hour
// in
// milliseconds
if ( timestampAge <= lBypassDuration )
{
_bInactiveStateBypassed = true;
return true;
}
return false;
}
/**
* @param form
* The form to display
* @param request
* The Http request
* @param model
* The model for XPage
*/
private void getFormStepModel( Form form, HttpServletRequest request, Map<String, Object> model )
{
Map<String, Object> modelForStep = _breadcrumb.getModelForCurrentStep( request, _formResponseManager );
modelForStep.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, ACTION_SAVE_FORM_RESPONSE ) );
_stepDisplayTree.addModel( modelForStep );
FormsResponseUtils.populateFormWithLogoAndNumberResponse(form);
model.put( FormsConstants.MARK_FORM, form );
model.put( STEP_HTML_MARKER,
_stepDisplayTree.getCompositeHtml( request, _formResponseManager.findAllResponses( ), getLocale( request ), DisplayType.EDITION_FRONTOFFICE ) );
model.put( FormsConstants.MARK_FORM_TOP_BREADCRUMB, _breadcrumb.getTopHtml( request, _formResponseManager ) );
model.put( FormsConstants.MARK_FORM_BOTTOM_BREADCRUMB, _breadcrumb.getBottomHtml( request, _formResponseManager ) );
if ( bypassInactiveState( form, request ) )
{
addWarning( MESSAGE_WARNING_INACTIVE_STATE_BYPASSED, getLocale( request ) );
}
fillCommons( model );
}
/**
*
* @param request
* The Http request
* @return the XPage
*
* @throws SiteMessageException
* Exception
* @throws UserNotSignedException
*/
@Action( value = ACTION_PREVIOUS_STEP )
public synchronized XPage doReturnStep( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException
{
IsRequestComingFromAction = true;
boolean bSessionLost = isSessionLost( );
try
{
findFormFrom( request );
}
catch( FormNotFoundException e )
{
return getStepView( request );
}
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
_currentStep = _formResponseManager.getCurrentStep( );
return getStepView( request );
}
try
{
// The condition below : We don't want to fill the FormResponseManager when just logged in with response made when user wasn't logged in
//for exemple in case you are at step 2, you log in and you have to go back to step 1 that you already saved in backup
if(_formResponseManager.getIsBackupResponseAlreadyInitiated() && _formResponseManager.getIsResponseLoadedFromBackup()
|| !_formResponseManager.getIsResponseLoadedFromBackup() && _formResponseManager.getFormResponse().getCreation() != null) {
FormsResponseUtils.fillResponseManagerWithResponses(request, false, _formResponseManager, _stepDisplayTree.getQuestions(), false);
}
}
catch( QuestionValidationException exception )
{
return getStepView( request );
}
_formResponseManager.popStep();
_currentStep = _formResponseManager.getCurrentStep();
try {
_stepDisplayTree = new StepDisplayTree(_currentStep.getId(), _formResponseManager.getFormResponse());
} catch (Exception e) {
return getStepView( request );
}
return getStepView( request );
}
/**
*
* @param request
* The Http request
* @return the XPage
*
* @throws SiteMessageException
* Exception
* @throws UserNotSignedException
*/
@Action( value = ACTION_GO_TO_STEP )
public synchronized XPage doGoToStep( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException
{
IsRequestComingFromAction = true;
boolean bSessionLost = isSessionLost( );
try
{
findFormFrom( request );
}
catch( FormNotFoundException e )
{
return getStepView( request );
}
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
try
{
FormsResponseUtils.fillResponseManagerWithResponses( request, false, _formResponseManager, _stepDisplayTree.getQuestions( ), false );
}
catch( QuestionValidationException e )
{
// this cannot happen because the validation is not performed
}
int nIndexStep = NumberUtils.toInt( request.getParameter( FormsConstants.PARAMETER_ACTION_GO_TO_STEP ), FormsConstants.INCORRECT_ID );
_currentStep = _formResponseManager.goTo( nIndexStep );
_stepDisplayTree = new StepDisplayTree( _currentStep.getId( ), _formResponseManager.getFormResponse( ) );
return getStepView( request );
}
/**
* Gives the summary page
*
* @param request
* The request
* @return the summary page
* @throws SiteMessageException
* if there is an error during the page generation
* @throws UserNotSignedException
*/
@Action( value = ACTION_FORM_RESPONSE_SUMMARY )
public synchronized XPage doFormResponseSummary( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException
{
IsRequestComingFromAction = true;
Form form = null;
try
{
boolean bSessionLost = isSessionLost( );
form = findFormFrom( request );
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
FormsResponseUtils.fillResponseManagerWithResponses( request, true, _formResponseManager, _stepDisplayTree.getQuestions( ), false );
boolean needValidation = form.isCaptchaStepFinal( );
if ( isCaptchaKO( request, needValidation ) )
{
addWarning( MESSAGE_WARNING_CAPTCHA, getLocale( request ) );
return getStepView( request );
}
}
catch( FormNotFoundException | QuestionValidationException exception )
{
return getStepView( request );
}
return getFormResponseSummaryPage( request, form );
}
private XPage getFormResponseSummaryPage( HttpServletRequest request, Form form )
{
if ( form.isCountResponses( ) )
{
form.setCurrentNumberResponse( FormHome.getNumberOfResponseForms( form.getId( ) ) );
}
Map<String, Object> model = buildModelForSummary( form, request );
model.put( FormsConstants.MARK_ID_FORM, form.getId( ) );
model.put( FormsConstants.MARK_FORM, form );
boolean displayCaptcha = _captchaSecurityService.isAvailable( ) && form.isCaptchaRecap( );
model.put( MARK_DISPLAY_CAPTCHA, displayCaptcha );
if ( displayCaptcha )
{
model.put( MARK_CAPTCHA, _captchaSecurityService.getHtmlCode( ) );
}
model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, ACTION_SAVE_FORM_RESPONSE ) );
String strTitleForm = I18nService.getLocalizedString( FormsConstants.MESSAGE_SUMMARY_TITLE, new String [ ] {
form.getTitle( )
}, getLocale( request ) );
XPage xPage = getXPage( TEMPLATE_VIEW_FORM_RESPONSE_SUMMARY, getLocale( request ), model );
xPage.setTitle( strTitleForm );
xPage.setPathLabel( form.getTitle( ) );
return xPage;
}
/**
* Builds the model for the summary page
*
* @param request
* the request
* @return the model
*/
private Map<String, Object> buildModelForSummary( Form form, HttpServletRequest request )
{
Map<String, Object> mapFormResponseSummaryModel = getModel( );
List<Step> listValidatedStep = _formResponseManager.getValidatedSteps( );
List<String> listStepHtml = FormsResponseUtils.buildStepsHtml( request, listValidatedStep, _formResponseManager, true );
mapFormResponseSummaryModel.put( MARK_LIST_SUMMARY_STEP_DISPLAY, listStepHtml );
if ( bypassInactiveState( form, request ) )
{
addWarning( MESSAGE_WARNING_INACTIVE_STATE_BYPASSED, getLocale( request ) );
}
fillCommons( mapFormResponseSummaryModel );
return mapFormResponseSummaryModel;
}
/**
*
* @param request
* The Http request
* @return the XPage
*
* @throws SiteMessageException
* Exception
* @throws UserNotSignedException
* Exception
* @throws AccessDeniedException
*/
@Action( value = ACTION_SAVE_FORM_RESPONSE )
public synchronized XPage doSaveFormResponse( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException, AccessDeniedException
{
IsRequestComingFromAction = true;
// CSRF Token control
if ( !SecurityTokenService.getInstance( ).validate( request, ACTION_SAVE_FORM_RESPONSE ) )
{
throw new AccessDeniedException( MESSAGE_ERROR_TOKEN );
}
Form form = null;
try
{
boolean bSessionLost = isSessionLost( );
form = findFormFrom( request );
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
FormsResponseUtils.fillResponseManagerWithResponses( request, true, _formResponseManager, _stepDisplayTree.getQuestions( ), false );
boolean needValidation = form.isCaptchaStepFinal( );
if ( isCaptchaKO( request, needValidation ) )
{
addWarning( MESSAGE_WARNING_CAPTCHA, getLocale( request ) );
return getStepView( request );
}
}
catch( FormNotFoundException | QuestionValidationException exception )
{
return redirectView( request, VIEW_STEP );
}
return doSaveResponse( request, form );
}
/**
*
* @param request
* The Http request
* @return the XPage
*
* @throws SiteMessageException
* Exception
* @throws UserNotSignedException
* Exception
* @throws AccessDeniedException
*/
@Action( value = ACTION_SAVE_FORM_RESPONSE_SUMMARY )
public synchronized XPage doSaveFormResponseSummary( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException, AccessDeniedException
{
IsRequestComingFromAction = true;
// CSRF Token control
if ( !SecurityTokenService.getInstance( ).validate( request, ACTION_SAVE_FORM_RESPONSE ) )
{
throw new AccessDeniedException( MESSAGE_ERROR_TOKEN );
}
Form form = null;
try
{
boolean bSessionLost = isSessionLost( );
form = findFormFrom( request );
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
boolean needValidation = form.isCaptchaRecap( );
if ( isCaptchaKO( request, needValidation ) )
{
addWarning( MESSAGE_WARNING_CAPTCHA, getLocale( request ) );
return getFormResponseSummaryPage( request, form );
}
}
catch( FormNotFoundException exception )
{
return getStepView( request );
}
return doSaveResponse( request, form );
}
private XPage doSaveResponse( HttpServletRequest request, Form form ) throws SiteMessageException, UserNotSignedException
{
_currentStep = _formResponseManager.getCurrentStep( );
if ( !_formResponseManager.validateFormResponses( ) )
{
_stepDisplayTree = new StepDisplayTree( _currentStep.getId( ), _formResponseManager.getFormResponse( ) );
return getStepView( request );
}
if ( !_currentStep.isFinal( ) )
{
_stepDisplayTree = new StepDisplayTree( _currentStep.getId( ), _formResponseManager.getFormResponse( ) );
addError( FormsConstants.MESSAGE_ERROR_STEP_NOT_FINAL, getLocale( request ) );
return getStepView( request );
}
saveFormResponse( form, request );
Map<String, Object> model = getModel( );
model.put( FormsConstants.PARAMETER_ID_FORM, form.getId( ) );
if ( bypassInactiveState( form, request ) )
{
addWarning( MESSAGE_WARNING_INACTIVE_STATE_BYPASSED, getLocale( request ) );
}
FormMessage formMessage = FormMessageHome.findByForm( form.getId( ) );
boolean bIsEndMessageDisplayed = formMessage.getEndMessageDisplay( );
String strBackUrl = getBackUrl( form, bIsEndMessageDisplayed, _formResponseManager.getFormResponse( ).getId( ) );
init( request );
if ( formMessage.getEndMessageDisplay( ) )
{
model.put( FormsConstants.MARK_INFO, formMessage.getEndMessage( ) );
}
else
{
return redirect( request, strBackUrl );
}
model.put( FormsConstants.PARAMETER_BACK_URL, strBackUrl );
fillCommons( model );
XPage xPage = getXPage( TEMPLATE_FORM_SUBMITTED, getLocale( request ), model );
xPage.setTitle( form.getTitle( ) );
xPage.setPathLabel( form.getTitle( ) );
return xPage;
}
/**
* Finds the form from the specified request
*
* @param request
* the request
* @return the found form, or {@code null} if not found
* @throws FormNotFoundException
* if the form is not found
* @throws SiteMessageException
* if the form is not accessible
*/
private Form findFormFrom( HttpServletRequest request ) throws FormNotFoundException, SiteMessageException
{
Form form = null;
if ( _currentStep == null )
{
int nIdForm = NumberUtils.toInt( request.getParameter( FormsConstants.PARAMETER_ID_FORM ), FormsConstants.INCORRECT_ID );
if ( nIdForm != FormsConstants.INCORRECT_ID )
{
form = FormHome.findByPrimaryKey( nIdForm );
if ( form == null )
{
throw new FormNotFoundException( );
}
init( nIdForm );
}
else
{
throw new FormNotFoundException( );
}
}
else
{
form = FormHome.findByPrimaryKey( _currentStep.getIdForm( ) );
}
if ( !form.isActive( ) && !bypassInactiveState( form, request ) )
{
if ( StringUtils.isNotEmpty( form.getUnavailableMessage( ) ) )
{
SiteMessageService.setCustomMessage( request, form.getUnavailableMessage( ), SiteMessage.TYPE_ERROR );
}
else
{
SiteMessageService.setMessage( request, FormsConstants.MESSAGE_ERROR_INACTIVE_FORM, SiteMessage.TYPE_ERROR );
}
}
return form;
}
/**
*
* @param form
* The Form
* @param bIsEndMessageDisplayed
* {@code true} if the end message is displayed, {@code false} otherwise
* @return the back URL
*/
private String getBackUrl( Form form, boolean bIsEndMessageDisplayed, int nIdFormResponse )
{
if ( StringUtils.isNotEmpty( form.getReturnUrl( ) ) )
{
return form.getReturnUrl( );
}
else
{
UrlItem url = null;
if ( form.isAuthentificationNeeded( ) )
{
url= bIsEndMessageDisplayed? new UrlItem( AppPathService.getPortalUrl( ) ):new UrlItem( "" );
url.addParameter( MVCUtils.PARAMETER_PAGE, FormResponseXPage.XPAGE_NAME );
url.addParameter( MVCUtils.PARAMETER_VIEW, FormResponseXPage.VIEW_FORM_RESPONSE );
url.addParameter( FormsConstants.PARAMETER_ID_RESPONSE, nIdFormResponse );
url.addParameter( FormsConstants.PARAMETER_ACTION_SUCCESS, "true" );
}
else
if ( bIsEndMessageDisplayed )
{
url = new UrlItem( getViewFullUrl( VIEW_STEP ) );
}
else
{
url = new UrlItem( getViewUrl( VIEW_STEP ) );
}
url.addParameter( FormsConstants.PARAMETER_ID_FORM, form.getId( ) );
return url.getUrl( );
}
}
/**
*
* @param request
* The Http request
* @return the XPage
*
* @throws SiteMessageException
* Exception
* @throws UserNotSignedException
* Exception
* @throws AccessDeniedException
*/
@Action( value = ACTION_SAVE_STEP )
public synchronized XPage doSaveStep( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException, AccessDeniedException
{
// CSRF Token control
if ( !SecurityTokenService.getInstance( ).validate( request, ACTION_SAVE_FORM_RESPONSE ) )
{
// if you go to step 2, then you log in (as you didn't save any backup), the token is invalided
// why are we here as we didn't try to save any backup ? So instead of throwing the error, we redirect.
AppLogService.error("FormXPage l 897 : " + MESSAGE_ERROR_TOKEN );
_currentStep = StepHome.findByPrimaryKey(Integer.parseInt(request.getParameter(FormsConstants.PARAMETER_ID_STEP)));
List<String> errorList = new ArrayList<>( );
_currentStep = FormsResponseUtils.getNextStep( _currentStep.getId( ), errorList, _formResponseManager );
return getStepView( request );
}
try
{
boolean bSessionLost = isSessionLost( );
Form form = findFormFrom( request );
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
if(_formResponseManager.getCurrentStep() == null) {
_formResponseManager.add(StepHome.getInitialStep(form.getId()));
}
FormsResponseUtils.fillResponseManagerWithResponses( request, true, _formResponseManager, _stepDisplayTree.getQuestions( ), false );
boolean needValidation = _currentStep.isInitial( ) && form.isCaptchaStepInitial( );
if ( isCaptchaKO( request, needValidation ) )
{
addWarning( MESSAGE_WARNING_CAPTCHA, getLocale( request ) );
return getStepView( request );
}
}
catch( FormNotFoundException | QuestionValidationException exception )
{
return getStepView( request );
}
List<String> errorList = new ArrayList<>( );
Step currentStep = FormsResponseUtils.getNextStep( _currentStep.getId( ), errorList, _formResponseManager );
_currentStep = currentStep != null ? currentStep : _currentStep;
if ( currentStep == null )
{
SiteMessageService.setMessage( request, MESSAGE_ERROR_CONTROL, new Object [ ] {
errorList.stream( ).collect( Collectors.joining( ) )
}, null, null, null, SiteMessage.TYPE_ERROR, null, getViewFullUrl( VIEW_STEP ) );
}
IsRequestComingFromAction = true;
return getStepView( request );
}
private boolean isCaptchaKO( HttpServletRequest request, boolean needValidation )
{
if ( !needValidation )
{
return false;
}
return !_captchaSecurityService.validate( request );
}
/**
*
* @param request
* The Http request
* @return the XPage
*
* @throws SiteMessageException
* Exception
* @throws UserNotSignedException
* Exception
* @throws AccessDeniedException
*/
@Action( value = ACTION_SAVE_FOR_BACKUP )
public synchronized XPage doSaveForBackup( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException, AccessDeniedException
{
IsRequestComingFromAction = true;
// CSRF Token control
if ( !SecurityTokenService.getInstance( ).validate( request, ACTION_SAVE_FORM_RESPONSE ) )
{
throw new AccessDeniedException( MESSAGE_ERROR_TOKEN );
}
Form form = null;
try
{
boolean bSessionLost = isSessionLost( );
form = findFormFrom( request );
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
}
catch( FormNotFoundException exception )
{
return getStepView( request );
}
checkAuthentication( form, request );
if(form.isBackupEnabled())
{
if(_formResponseManager.getCurrentStep() == null) {
_formResponseManager.add(StepHome.getInitialStep(form.getId()));
}
try
{
FormsResponseUtils.fillResponseManagerWithResponses( request, false, _formResponseManager, _stepDisplayTree.getQuestions( ), false );
}
catch( QuestionValidationException e )
{
return getStepView( request );
}
LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
FormResponse formResponse = _formResponseManager.getFormResponse( );
formResponse.setGuid( user.getName( ) );
formResponse.setUpdateStatus(Timestamp.valueOf(LocalDateTime.now()));
_formService.saveFormForBackup( formResponse );
_formResponseManager.setFormResponseUpdateDate( formResponse.getUpdateStatus( ) );
_formResponseManager.setIsResponseLoadedFromBackup(false);
_formService.saveFormForBackup(formResponse);
}
return getStepView( request );
}
/**
* Removes Backup
*
* @param request
* The Http request
* @return the XPage
*
* @throws SiteMessageException
* Exception
* @throws UserNotSignedException
* Exception
* @throws AccessDeniedException
*/
@Action( value = ACTION_RESET_BACKUP )
public synchronized XPage doResetBackup( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException, AccessDeniedException
{
IsRequestComingFromAction = true;
// CSRF Token control
if ( !SecurityTokenService.getInstance( ).validate( request, ACTION_SAVE_FORM_RESPONSE ) )
{
AppLogService.error( MESSAGE_ERROR_TOKEN );
return getStepView( request );
}
Form form = null;
try
{
form = findFormFrom( request );
}
catch( FormNotFoundException exception )
{
return getStepView( request );
}
checkAuthentication( form, request );
FormResponse formResponse = _formResponseManager.getFormResponse( );
_formService.removeFormBackup( formResponse );
_formResponseManager = null;
init( form.getId( ) );
return getStepView( request );
}
/**
* Adds an iteration
*
* @param request
* the request
* @return the XPage
* @throws SiteMessageException
* if there is an error during the iteration
* @throws UserNotSignedException
* if the user is not signed in
*/
@Action( value = ACTION_ADD_ITERATION )
public synchronized XPage doAddIteration( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException
{
IsRequestComingFromAction = true;
try
{
boolean bSessionLost = isSessionLost( );
findFormFrom( request );
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
FormsResponseUtils.fillResponseManagerWithResponses( request, false, _formResponseManager, _stepDisplayTree.getQuestions( ), false );
}
catch( FormNotFoundException | QuestionValidationException exception )
{
return getStepView( request );
}
int nIdGroup = NumberUtils.toInt( request.getParameter( FormsConstants.PARAMETER_ACTION_PREFIX + ACTION_ADD_ITERATION ),
FormsConstants.DEFAULT_ID_VALUE );
if ( nIdGroup != FormsConstants.DEFAULT_ID_VALUE )
{
_stepDisplayTree.iterate( nIdGroup );
}
return getStepView( request );
}
/**
* Remove an iteration
*
* @param request
* the request
* @return the XPage
* @throws SiteMessageException
* if there is an error during the iteration
* @throws UserNotSignedException
* if the user is not signed in
*/
@Action( value = ACTION_REMOVE_ITERATION )
public synchronized XPage doRemoveIteration( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException
{
IsRequestComingFromAction = true;
try
{
boolean bSessionLost = isSessionLost( );
findFormFrom( request );
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
FormsResponseUtils.fillResponseManagerWithResponses( request, false, _formResponseManager, _stepDisplayTree.getQuestions( ), false );
}
catch( FormNotFoundException | QuestionValidationException exception )
{
return getStepView( request );
}
String strIterationInfo = request.getParameter( FormsConstants.PARAMETER_ACTION_PREFIX + ACTION_REMOVE_ITERATION );
String [ ] arrayIterationInfo = strIterationInfo.split( FormsConstants.SEPARATOR_UNDERSCORE );
int nIdGroupParent = Integer.parseInt( arrayIterationInfo [0] );
int nIndexIteration = Integer.parseInt( arrayIterationInfo [1] );
_stepDisplayTree.removeIteration( request, nIdGroupParent, nIndexIteration );
return getStepView( request );
}
/**
* Synchronous upload process to fill the form.
*
* @param request
* the request
* @return the XPage
* @throws SiteMessageException
* if there is an error during the iteration
* @throws UserNotSignedException
* if the user is not signed in
*/
@Action( value = ACTION_UPLOAD )
public synchronized XPage doSynchronousUploadDocument( HttpServletRequest request ) throws SiteMessageException, UserNotSignedException
{
IsRequestComingFromAction = true;
boolean bSessionLost = isSessionLost( );
if ( bSessionLost )
{
addWarning( MESSAGE_WARNING_LOST_SESSION, getLocale( request ) );
return getStepView( request );
}
String error = null;
String strFlagAsynchrounous = request.getParameter( "action_" + ACTION_UPLOAD );
boolean isUpload = strFlagAsynchrounous.startsWith( FormsAsynchronousUploadHandler.getUploadFormsSubmitPrefix( ) );
Map<String, String [ ]> extraParams = new TreeMap<>( );
extraParams.put( strFlagAsynchrounous, new String [ ] {
strFlagAsynchrounous
} );
HttpServletRequest wrappedRequest = new SynchronousHttpServletRequestWrapper( (MultipartHttpServletRequest) request, extraParams );
FormsAsynchronousUploadHandler handler = FormsAsynchronousUploadHandler.getHandler( );
String strAttributeName = handler.getUploadAction( wrappedRequest ).substring( handler.getUploadSubmitPrefix( ).length( ) );
if ( isUpload )
{
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
List<FileItem> fileUploaded = multipartRequest.getFileList( strAttributeName );
error = handler.canUploadFiles( wrappedRequest, strAttributeName, fileUploaded, getLocale( request ) );
}
try
{
FormsResponseUtils.fillResponseManagerWithResponses( wrappedRequest, false, _formResponseManager, _stepDisplayTree.getQuestions( ), false );
}
catch( QuestionValidationException exception )
{
return getStepView( request );
}
if ( isUpload )
{
List<Question> listQuestionStep = _stepDisplayTree.getQuestions( );
List<FormQuestionResponse> listFormQuestionResponse = _formResponseManager.findResponsesFor( _currentStep );
String strIdEntry = strAttributeName.split( IEntryTypeService.PREFIX_ATTRIBUTE ) [1].trim( );
if ( error != null )
{
for ( FormQuestionResponse formResponse : listFormQuestionResponse )
{
if ( formResponse.getQuestion( ).getIdEntry( ) == Integer.parseInt( strIdEntry ) )
{
List<Response> listResponse = formResponse.getEntryResponse( );
Response response = new Response( );
response.setEntry( formResponse.getQuestion( ).getEntry( ) );
GenericAttributeError genAttError = new GenericAttributeError( );
genAttError.setErrorMessage( error );
listResponse.add( response );
formResponse.setError( genAttError );
formResponse.setEntryResponse( listResponse );
break;
}
}
_formResponseManager.addResponses( listFormQuestionResponse );
}
// if the entry type is Automatic file Reading, we fill the form responses question with ocr values readed
else
if ( EntryTypeAutomaticFileReading.fill( listQuestionStep, listFormQuestionResponse, wrappedRequest ) )
{
_formResponseManager.addResponses( listFormQuestionResponse );
}
}
return getStepView( request );
}
private void initFormResponseManager( HttpServletRequest request, Form form )
{
LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
if ( _formResponseManager == null || !_formResponseManager.getIsResponseLoadedFromBackup() && !IsRequestComingFromAction)
{
if ( user != null && form.isBackupEnabled() )
{
_formResponseManager = _formService.createFormResponseManagerFromBackUp( form, user.getName( ) );
}
else
{
_formResponseManager = new FormResponseManager( form );
}
}
}
/**
* save the response of form
*
* @param form
* the form
* @param request
* The Http request request
* @throws SiteMessageException
* the exception
*/
private void saveFormResponse( Form form, HttpServletRequest request ) throws SiteMessageException
{
if ( _bInactiveStateBypassed )
{
return; // form was in testing mode; do not save response
}
FormResponse formResponse = _formResponseManager.getFormResponse( );
if ( form.isAuthentificationNeeded( ) )
{
LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
formResponse.setGuid( user.getName( ) );
}
try
{
_formService.saveForm( form, formResponse );
}
catch( MaxFormResponseException e)
{
SiteMessageService.setMessage( request, FormsConstants.MESSAGE_ERROR_NUMBER_MAX_RESPONSE_FORM, SiteMessage.TYPE_ERROR );
}
AccessControlService.getInstance( ).cleanSessionData( request, form.getId( ), Form.RESOURCE_TYPE );
_formService.processFormAction( form, formResponse );
}
/**
* initialize the object.
*/
private void init( HttpServletRequest request )
{
_formResponseManager = null;
_currentStep = null;
_stepDisplayTree = null;
_breadcrumb = null;
_bInactiveStateBypassed = false;
IsRequestComingFromAction = false;
FormsAsynchronousUploadHandler.getHandler( ).removeSessionFiles( request.getSession( ) );
}
/**
* initialize the object
*
* @param nIdForm
* id form
*/
private void init( int nIdForm )
{
_currentStep = StepHome.getInitialStep( nIdForm );
_formResponseManager = null;
_stepDisplayTree = null;
_breadcrumb = null;
IsRequestComingFromAction = false;
}
/**
* ckeck if the session has expired
*
*
*/
private boolean isSessionLost( )
{
return ( _currentStep == null && _formResponseManager == null && _stepDisplayTree == null && _breadcrumb == null );
}
}