diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/AbstractFormResponseService.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/AbstractFormResponseService.java index 164f7fc..89b0b57 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/AbstractFormResponseService.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/AbstractFormResponseService.java @@ -137,4 +137,16 @@ protected boolean isRecordStateValid( ITask task, TaskConfig config, int idHisto } return bIsValid; } + + /** + * Check whether the values from the given List of FormQuestionResponse are valid + * + * @param listFormQuestionResponse + * the List of FormQuestionResponse to check + * @return true if all the Responses have valid values, returns false otherwise + */ + protected boolean areFormResponsesValid( List listFormQuestionResponse ) + { + return _formsTaskService.areFormQuestionResponsesValid( listFormQuestionResponse ); + } } diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/CompleteFormResponseService.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/CompleteFormResponseService.java index 070b72f..e5eff2a 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/CompleteFormResponseService.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/CompleteFormResponseService.java @@ -35,6 +35,7 @@ import java.sql.Date; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; @@ -43,6 +44,7 @@ import javax.inject.Named; import javax.servlet.http.HttpServletRequest; +import fr.paris.lutece.plugins.forms.business.FormQuestionResponse; import fr.paris.lutece.plugins.forms.business.FormResponse; import fr.paris.lutece.plugins.forms.business.Question; import fr.paris.lutece.plugins.forms.business.QuestionHome; @@ -92,6 +94,9 @@ public class CompleteFormResponseService extends AbstractFormResponseService imp @Inject private ICompleteFormResponseTaskHistoryService _completeFormResponseTaskHistoryService; + // List of FormQuestionResponse to store the user's new Responses + private List _submittedFormResponses; + @Override public List findListQuestionUsedCorrectForm( FormResponse formResponse ) { @@ -226,7 +231,15 @@ public boolean doEditResponseData( HttpServletRequest request, CompleteFormRespo return false; } List listQuestions = getListQuestionToEdit( response, completeFormResponse.getListCompleteReponseValues( ) ); - + // Get the values of the newly submitted Responses + _submittedFormResponses = _formsTaskService.getSubmittedFormQuestionResponses( request, response, listQuestions ); + // Check if the new Responses are valid + if ( !areFormResponsesValid( _submittedFormResponses ) ) + { + return false; + } + // Reset the content of the List + _submittedFormResponses = Collections.emptyList( ); doEditResponseData( request, response, listQuestions, idTask, idHistory ); return true; } @@ -262,4 +275,10 @@ protected void createTaskHistory( EditableResponse editableResponse, int idTask, _completeFormResponseTaskHistoryService.create( history ); } + + @Override + public List getSubmittedFormResponseList( ) + { + return _submittedFormResponses; + } } diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/ICompleteFormResponseService.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/ICompleteFormResponseService.java index e38f284..e6e65f8 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/ICompleteFormResponseService.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/ICompleteFormResponseService.java @@ -38,6 +38,7 @@ import javax.servlet.http.HttpServletRequest; +import fr.paris.lutece.plugins.forms.business.FormQuestionResponse; import fr.paris.lutece.plugins.forms.business.FormResponse; import fr.paris.lutece.plugins.forms.business.Question; import fr.paris.lutece.plugins.genericattributes.business.Entry; @@ -162,4 +163,11 @@ public interface ICompleteFormResponseService * the Response */ void doCompleteResponse( CompleteFormResponse completeFormResponse ); + + /** + * Get the List of FormQuestionResponse containing the new Responses that the user is trying to submit + * + * @return A List of FormQuestionResponse + */ + List getSubmittedFormResponseList( ); } diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/IResubmitFormResponseService.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/IResubmitFormResponseService.java index 54cdcb2..4f46424 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/IResubmitFormResponseService.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/IResubmitFormResponseService.java @@ -38,6 +38,7 @@ import javax.servlet.http.HttpServletRequest; +import fr.paris.lutece.plugins.forms.business.FormQuestionResponse; import fr.paris.lutece.plugins.forms.business.FormResponse; import fr.paris.lutece.plugins.forms.business.Question; import fr.paris.lutece.plugins.genericattributes.business.Entry; @@ -176,4 +177,11 @@ public interface IResubmitFormResponseService * the Response */ void doCompleteResponse( ResubmitFormResponse resubmitFormResponse ); + + /** + * Get the List of FormQuestionResponse containing the new Responses that the user is trying to submit + * + * @return A List of FormQuestionResponse + */ + List getSubmittedFormResponseList( ); } diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/ResubmitFormResponseService.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/ResubmitFormResponseService.java index add4f4d..e146ce1 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/ResubmitFormResponseService.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/ResubmitFormResponseService.java @@ -35,6 +35,7 @@ import java.sql.Date; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.stream.Collectors; @@ -105,6 +106,9 @@ public class ResubmitFormResponseService extends AbstractFormResponseService imp @Inject private IResubmitFormResponseTaskHistoryService _resubmitFormResponseTaskHistoryService; + // List of FormQuestionResponse to store the user's new Responses + private List _submittedFormQuestionResponses; + @Override public ResubmitFormResponse find( int nIdHistory, int nIdTask ) { @@ -270,7 +274,15 @@ public boolean doEditResponseData( HttpServletRequest request, ResubmitFormRespo return false; } List listQuestions = getListQuestionToEdit( response, resubmitFormResponse.getListResubmitReponseValues( ) ); - + // Get the values of the newly submitted Responses + _submittedFormQuestionResponses = _formsTaskService.getSubmittedFormQuestionResponses( request, response, listQuestions ); + // Check if the new Responses are valid + if ( !areFormResponsesValid( _submittedFormQuestionResponses ) ) + { + return false; + } + // Reset the content of the List + _submittedFormQuestionResponses = Collections.emptyList( ); doEditResponseData( request, response, listQuestions, idTask, idHistory ); return true; } @@ -307,4 +319,10 @@ public void doCompleteResponse( ResubmitFormResponse resubmitFormResponse ) resubmitFormResponse.setDateCompleted( new Date( System.currentTimeMillis( ) ) ); update( resubmitFormResponse ); } + + @Override + public List getSubmittedFormResponseList( ) + { + return _submittedFormQuestionResponses; + } } diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/task/FormsTaskService.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/task/FormsTaskService.java index ea6b633..8a5ea42 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/task/FormsTaskService.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/task/FormsTaskService.java @@ -43,6 +43,9 @@ import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; +import fr.paris.lutece.plugins.forms.business.Control; +import fr.paris.lutece.plugins.forms.business.ControlHome; +import fr.paris.lutece.plugins.forms.business.ControlType; import fr.paris.lutece.plugins.forms.business.FormQuestionResponse; import fr.paris.lutece.plugins.forms.business.FormQuestionResponseHome; import fr.paris.lutece.plugins.forms.business.FormResponse; @@ -52,9 +55,11 @@ import fr.paris.lutece.plugins.forms.business.Step; import fr.paris.lutece.plugins.forms.service.EntryServiceManager; import fr.paris.lutece.plugins.forms.service.entrytype.EntryTypeDate; +import fr.paris.lutece.plugins.forms.validation.IValidator; import fr.paris.lutece.plugins.forms.web.StepDisplayTree; import fr.paris.lutece.plugins.forms.web.entrytype.DisplayType; import fr.paris.lutece.plugins.forms.web.entrytype.IEntryDataService; +import fr.paris.lutece.plugins.genericattributes.business.GenericAttributeError; import fr.paris.lutece.plugins.genericattributes.business.Response; import fr.paris.lutece.plugins.genericattributes.service.entrytype.AbstractEntryTypeUpload; import fr.paris.lutece.plugins.genericattributes.service.entrytype.EntryTypeServiceManager; @@ -178,6 +183,28 @@ public List buildFormStepDisplayTreeList( HttpServletRequest request, Li return listFormDisplayTrees; } + @Override + public List buildFormStepDisplayTree( HttpServletRequest request, List listStep, List listQuestionToDisplay, + List listFormQuestionResponse, FormResponse formResponse, DisplayType displayType ) + { + List listFormDisplayTrees = new ArrayList<>( ); + + List listQuestionToDisplayId = listFormQuestionResponse.stream( ).map( FormQuestionResponse::getQuestion ).map( Question::getId ) + .collect( Collectors.toList( ) ); + + if ( !CollectionUtils.isEmpty( listStep ) ) + { + for ( Step step : listStep ) + { + int nIdStep = step.getId( ); + + StepDisplayTree stepDisplayTree = new StepDisplayTree( nIdStep, formResponse, listQuestionToDisplayId ); + listFormDisplayTrees.add( stepDisplayTree.getCompositeHtml( request, listFormQuestionResponse, request.getLocale( ), displayType ) ); + } + } + return listFormDisplayTrees; + } + @Override public List findChangedResponses( List listEditableResponse ) { @@ -207,6 +234,7 @@ public List createEditableResponses( FormResponse formResponse IEntryDataService entryDataService = EntryServiceManager.getInstance( ).getEntryDataService( question.getEntry( ).getEntryType( ) ); FormQuestionResponse responseFromForm = entryDataService.createResponseFromRequest( question, request, false ); responseFromForm.setIdFormResponse( formResponse.getId( ) ); + FormQuestionResponse responseSaved = findSavedResponse( formResponse, question ); if ( responseSaved == null ) @@ -364,4 +392,67 @@ public String createPreviousNewValue( FormQuestionResponse responseForm ) } return value; } + + @Override + public List getSubmittedFormQuestionResponses( HttpServletRequest request, FormResponse formResponse, List listQuestions ) + { + List submittedFormResponses = new ArrayList<>( ); + for ( Question question : listQuestions ) + { + IEntryDataService entryDataService = EntryServiceManager.getInstance( ).getEntryDataService( question.getEntry( ).getEntryType( ) ); + FormQuestionResponse responseFromForm = entryDataService.createResponseFromRequest( question, request, false ); + responseFromForm.setIdFormResponse( formResponse.getId( ) ); + submittedFormResponses.add( responseFromForm ); + } + return submittedFormResponses; + } + + @Override + public boolean areFormQuestionResponsesValid( List listFormQuestionResponse ) + { + boolean areAllResponsesValid = Boolean.TRUE; + + for ( FormQuestionResponse formQuestionResponse : listFormQuestionResponse ) + { + if ( !isResponseValid( formQuestionResponse ) ) + { + areAllResponsesValid = Boolean.FALSE; + } + } + return areAllResponsesValid; + } + + /** + * Check whether the given FormQuestionResponse satisfies the Validator associated with it + * + * @param formQuestionResponse + * the FormQuestionResponse to check + * @return true if the Response is valid, returns false otherwise + */ + private boolean isResponseValid( FormQuestionResponse formQuestionResponse ) + { + // Get the list of controls created for this question's validation process + List listControl = ControlHome.getControlByQuestionAndType( formQuestionResponse.getQuestion( ).getId( ), + ControlType.VALIDATION.getLabel( ) ); + + // Check that the current response is valid with each associated control + for ( Control control : listControl ) + { + IValidator validator = EntryServiceManager.getInstance( ).getValidator( control.getValidatorName( ) ); + + // If the given response is not valid with a control + if ( !validator.validate( formQuestionResponse, control ) ) + { + // Create an error to be displayed + GenericAttributeError error = new GenericAttributeError( ); + error.setIsDisplayableError( true ); + error.setErrorMessage( control.getErrorMessage( ) ); + // Set the error on the response's Entry field + formQuestionResponse.setError( error ); + + return false; + } + } + return true; + } } diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/task/IFormsTaskService.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/task/IFormsTaskService.java index 5269581..f591ef1 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/task/IFormsTaskService.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/service/task/IFormsTaskService.java @@ -33,6 +33,7 @@ */ package fr.paris.lutece.plugins.workflow.modules.forms.service.task; +import java.util.Collection; import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -99,6 +100,26 @@ default FormResponse findFormResponseFrom( ResourceHistory resourceHistory ) List buildFormStepDisplayTreeList( HttpServletRequest request, List listStep, List listQuestionToDisplay, FormResponse formResponse, DisplayType displayType ); + /** + * Get a List of Strings containing the models and values displayed to the user, for each existing Step + * + * @param request + * the HTTP request + * @param listStep + * the List of Steps being processed + * @param listQuestionToDisplay + * the List of Question that should be resubmitted by the user + * @param listFormQuestionResponse + * the List of new FormQuestionResponse being submitted by the user + * @param formResponse + * the FormResponse being processed + * @param displayType + * the DisplayType used in the template + * @return a List of Strings containing the complete template + */ + List buildFormStepDisplayTree( HttpServletRequest request, List listStep, List listQuestionToDisplay, + List listFormQuestionResponse, FormResponse formResponse, DisplayType displayType ); + /** * Finds the responses that have changed * @@ -174,4 +195,26 @@ List buildFormStepDisplayTreeList( HttpServletRequest request, List getSubmittedFormQuestionResponses( HttpServletRequest request, FormResponse formResponse, List listQuestions ); + + /** + * Check whether the values from the given FormQuestionResponse satisfy the Validators (regEx, file type, etc.) associated with them + * + * @param formQuestionResponses + * the List of FormQuestionResponse to check + * @return true if all the Responses are valid, returns false otherwise + */ + boolean areFormQuestionResponsesValid( List formQuestionResponses ); } diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/web/CompleteFormResponseApp.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/web/CompleteFormResponseApp.java index 498d224..7b1c750 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/web/CompleteFormResponseApp.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/web/CompleteFormResponseApp.java @@ -40,6 +40,7 @@ import javax.servlet.http.HttpServletRequest; +import fr.paris.lutece.plugins.forms.business.FormQuestionResponse; import fr.paris.lutece.plugins.forms.business.FormResponse; import fr.paris.lutece.plugins.forms.business.Question; import fr.paris.lutece.plugins.forms.business.Step; @@ -57,6 +58,7 @@ import fr.paris.lutece.portal.web.xpages.XPage; import fr.paris.lutece.util.html.HtmlTemplate; import fr.paris.lutece.util.signrequest.AbstractPrivateKeyAuthenticator; +import io.jsonwebtoken.lang.Collections; public class CompleteFormResponseApp extends AbstractFormResponseApp { @@ -88,6 +90,7 @@ public class CompleteFormResponseApp extends AbstractFormResponseApp listStepDisplayTree; FormResponse formResponse = _formsTaskService.getFormResponseFromIdHistory( completeFormResponse.getIdHistory( ) ); List listQuestions = _completeFormResponseService.getListQuestionToEdit( formResponse, completeFormResponse.getListCompleteReponseValues( ) ); @@ -95,8 +98,21 @@ protected XPage getFormResponseXPage( HttpServletRequest request, CompleteFormRe List listStep = listQuestions.stream( ).map( Question::getStep ).map( Step::getId ).distinct( ).map( StepHome::findByPrimaryKey ) .collect( Collectors.toList( ) ); - List listStepDisplayTree = _formsTaskService.buildFormStepDisplayTreeList( request, listStep, listQuestions, formResponse, - DisplayType.COMPLETE_FRONTOFFICE ); + // Get the List of Responses the user previously tried to submit + List formQuestionResponseList = _completeFormResponseService.getSubmittedFormResponseList( ); + // If the List is empty, then it is likely the first submission attempt + if ( Collections.isEmpty( formQuestionResponseList ) ) + { + // If the List has elements, then the user already tried to submit some Responses. + // We make sure to retrieve their values, as well as the potential errors associated with them + listStepDisplayTree = _formsTaskService.buildFormStepDisplayTreeList( request, listStep, listQuestions, formResponse, + DisplayType.COMPLETE_FRONTOFFICE ); + } + else + { + listStepDisplayTree = _formsTaskService.buildFormStepDisplayTree( request, listStep, listQuestions, formQuestionResponseList, formResponse, + DisplayType.COMPLETE_FRONTOFFICE ); + } Map model = initModelFormPage( request, formResponse, listStepDisplayTree ); model.put( MARK_COMPLETE_FORM, completeFormResponse ); diff --git a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/web/ResubmitFormResponseApp.java b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/web/ResubmitFormResponseApp.java index 85c622f..4d2db1a 100644 --- a/src/java/fr/paris/lutece/plugins/workflow/modules/forms/web/ResubmitFormResponseApp.java +++ b/src/java/fr/paris/lutece/plugins/workflow/modules/forms/web/ResubmitFormResponseApp.java @@ -43,6 +43,7 @@ import org.apache.commons.lang3.StringUtils; +import fr.paris.lutece.plugins.forms.business.FormQuestionResponse; import fr.paris.lutece.plugins.forms.business.FormResponse; import fr.paris.lutece.plugins.forms.business.Question; import fr.paris.lutece.plugins.forms.business.Step; @@ -62,6 +63,7 @@ import fr.paris.lutece.portal.web.xpages.XPage; import fr.paris.lutece.util.html.HtmlTemplate; import fr.paris.lutece.util.signrequest.AbstractPrivateKeyAuthenticator; +import io.jsonwebtoken.lang.Collections; public class ResubmitFormResponseApp extends AbstractFormResponseApp { @@ -145,6 +147,7 @@ public XPage getPage( HttpServletRequest request, int nMode, Plugin plugin ) thr protected XPage getFormResponseXPage( HttpServletRequest request, ResubmitFormResponse resubmitFormResponse ) { XPage page = new XPage( ); + List listStepDisplayTree; FormResponse formResponse = _formsTaskService.getFormResponseFromIdHistory( resubmitFormResponse.getIdHistory( ) ); List listQuestions = _resubmitFormResponseService.getListQuestionToEdit( formResponse, resubmitFormResponse.getListResubmitReponseValues( ) ); @@ -152,8 +155,21 @@ protected XPage getFormResponseXPage( HttpServletRequest request, ResubmitFormRe List listStep = listQuestions.stream( ).map( Question::getStep ).map( Step::getId ).distinct( ).map( StepHome::findByPrimaryKey ) .collect( Collectors.toList( ) ); - List listStepDisplayTree = _formsTaskService.buildFormStepDisplayTreeList( request, listStep, listQuestions, formResponse, - DisplayType.RESUBMIT_FRONTOFFICE ); + // Get the List of Responses the user previously tried to submit + List formQuestionResponseList = _resubmitFormResponseService.getSubmittedFormResponseList( ); + // If the List is empty, then it is likely the first submission attempt + if ( Collections.isEmpty( formQuestionResponseList ) ) + { + listStepDisplayTree = _formsTaskService.buildFormStepDisplayTreeList( request, listStep, listQuestions, formResponse, + DisplayType.RESUBMIT_FRONTOFFICE ); + } + else + { + // If the List has elements, then the user already tried to submit some Responses. + // We make sure to retrieve their values, as well as the potential errors associated with them + listStepDisplayTree = _formsTaskService.buildFormStepDisplayTree( request, listStep, listQuestions, formQuestionResponseList, formResponse, + DisplayType.RESUBMIT_FRONTOFFICE ); + } Map model = initModelFormPage( request, formResponse, listStepDisplayTree ); model.put( MARK_RESUBMIT_FORM, resubmitFormResponse );