View Javadoc
1   /*
2    * Copyright (c) 2002-2025, City of Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.plugins.formresponsxpage.web;
35  
36  import java.io.IOException;
37  import java.util.ArrayList;
38  import java.util.Collection;
39  import java.util.HashMap;
40  import java.util.LinkedHashMap;
41  import java.util.Locale;
42  import java.util.Map;
43  import java.util.List;
44  import java.util.Objects;
45  import java.util.Optional;
46  import java.util.Map.Entry;
47  import java.util.stream.Collectors;
48  import javax.servlet.http.HttpServletRequest;
49  
50  import fr.paris.lutece.plugins.forms.business.FormQuestionResponse;
51  import fr.paris.lutece.plugins.forms.business.FormResponseStep;
52  import fr.paris.lutece.plugins.forms.service.EntryServiceManager;
53  import fr.paris.lutece.plugins.forms.web.entrytype.DisplayType;
54  import fr.paris.lutece.plugins.forms.web.entrytype.EntryTypeFileDisplayService;
55  import fr.paris.lutece.plugins.forms.web.entrytype.IEntryDisplayService;
56  import fr.paris.lutece.plugins.genericattributes.business.Response;
57  import fr.paris.lutece.plugins.leaflet.business.GeolocItem;
58  import fr.paris.lutece.plugins.leaflet.service.IconService;
59  import fr.paris.lutece.plugins.search.solr.business.SolrSearchEngine;
60  import fr.paris.lutece.plugins.search.solr.business.SolrSearchResult;
61  import fr.paris.lutece.plugins.search.solr.indexer.SolrItem;
62  import fr.paris.lutece.portal.business.file.File;
63  import org.apache.commons.lang3.math.NumberUtils;
64  
65  import fr.paris.lutece.api.user.User;
66  import fr.paris.lutece.plugins.carto.business.DataLayer;
67  import fr.paris.lutece.plugins.carto.business.DataLayerMapTemplate;
68  import fr.paris.lutece.plugins.carto.business.DataLayerMapTemplateHome;
69  import fr.paris.lutece.plugins.carto.business.MapTemplate;
70  import fr.paris.lutece.plugins.carto.business.MapTemplateHome;
71  import fr.paris.lutece.plugins.cartography.modules.solr.service.CartographyService;
72  import fr.paris.lutece.plugins.formresponsxpage.business.Formsreponseedito;
73  import fr.paris.lutece.plugins.formresponsxpage.business.FormsreponseeditoHome;
74  import fr.paris.lutece.plugins.forms.business.Form;
75  import fr.paris.lutece.plugins.forms.business.FormHome;
76  import fr.paris.lutece.plugins.forms.business.FormResponse;
77  import fr.paris.lutece.plugins.forms.business.FormResponseHome;
78  import fr.paris.lutece.plugins.forms.service.upload.FormsAsynchronousUploadHandler;
79  import fr.paris.lutece.plugins.forms.util.FormsConstants;
80  import fr.paris.lutece.plugins.forms.util.FormsResponseUtils;
81  import fr.paris.lutece.plugins.workflowcore.business.action.Action;
82  import fr.paris.lutece.portal.service.admin.AccessDeniedException;
83  import fr.paris.lutece.portal.service.admin.AdminUserService;
84  import fr.paris.lutece.portal.service.i18n.I18nService;
85  import fr.paris.lutece.portal.service.message.SiteMessage;
86  import fr.paris.lutece.portal.service.message.SiteMessageException;
87  import fr.paris.lutece.portal.service.message.SiteMessageService;
88  import fr.paris.lutece.portal.service.search.SearchResult;
89  import fr.paris.lutece.portal.service.security.LuteceUser;
90  import fr.paris.lutece.portal.service.security.SecurityService;
91  import fr.paris.lutece.portal.service.security.SecurityTokenService;
92  import fr.paris.lutece.portal.service.util.AppException;
93  import fr.paris.lutece.portal.service.util.AppLogService;
94  import fr.paris.lutece.portal.service.util.AppPropertiesService;
95  import fr.paris.lutece.portal.service.workflow.WorkflowService;
96  import fr.paris.lutece.portal.util.mvc.commons.annotations.View;
97  import fr.paris.lutece.portal.util.mvc.xpage.MVCApplication;
98  import fr.paris.lutece.portal.util.mvc.xpage.annotations.Controller;
99  import fr.paris.lutece.portal.web.util.LocalizedPaginator;
100 import fr.paris.lutece.portal.web.xpages.XPage;
101 import fr.paris.lutece.util.html.AbstractPaginator;
102 import fr.paris.lutece.util.html.DelegatePaginator;
103 import fr.paris.lutece.util.html.IPaginator;
104 import fr.paris.lutece.util.html.Paginator;
105 import fr.paris.lutece.util.url.UrlItem;
106 
107 /**
108  *
109  * Controller for formResponse display
110  *
111  */
112 @Controller( xpageName = FormsResponseEditoXPage.XPAGE_NAME, pageTitleI18nKey = FormsResponseEditoXPage.MESSAGE_PAGE_TITLE, pagePathI18nKey = FormsResponseEditoXPage.MESSAGE_PATH )
113 public class FormsResponseEditoXPage extends MVCApplication
114 {
115     public static final String XPAGE_NAME = "formsResponseEdito";
116 
117     /**
118      * Generated serial id
119      */
120     private static final long serialVersionUID = 8146530527615651620L;
121 
122     // Messages
123     protected static final String MESSAGE_PAGE_TITLE = "forms.response.xpage.form.view.pageTitle";
124     protected static final String MESSAGE_PATH = "forms.response.xpage.form.view.pagePathLabel";
125     protected static final String MESSAGE_ERROR_NOT_PUBLISHED_FORM_RESPONSE = "forms.xpage.response.error.inactive";
126     protected static final String MESSAGE_ERROR_NOT_FOUND_FORM_RESPONSE = "forms.xpage.response.error.notfound";
127     protected static final String MESSAGE_FORM_RESPONSE_PAGETITLE = "forms.xpage.response.pagetitle";
128     protected static final String MESSAGE_FORM_RESPONSE_PATHLABEL = "forms.xpage.response.pathlabel";
129     private static final String MESSAGE_ACTION_ERROR = "forms.xpage.response.action.error";
130     private static final String MESSAGE_ERROR_TOKEN = "Invalid security token";
131     private static final String MESSAGE_ACTION_SUCCESS = "forms.xpage.response.action.success";
132 
133     public static final String PARAMETER_SOLR_GEOJSON = "DataLayer_text";
134     
135     private static final String PROPERTY_RESULTS_PER_PAGE = "search.nb.docs.per.page";
136     private static final int DEFAULT_RESULTS_PER_PAGE = 10;
137     private static final String PARAMETER_NB_ITEMS_PER_PAGE = "items_per_page";
138     private static final String DEFAULT_PAGE_INDEX = "1";
139     private static final String PARAMETER_PAGE_INDEX = "page_index";
140     
141     // Views
142     public static final String VIEW_FORM_RESPONSE = "formResponseView";
143     public static final String VIEW_FORM_FILE = "formFileView";
144     public static final String VIEW_FORM_LIST_RESPONSES = "formListResponsesView";
145 
146     // Actions
147     private static final String ACTION_PROCESS_ACTION = "doProcessAction";
148     private static final String ACTION_SAVE_TASK_FORM = "doSaveTaskForm";
149 
150     // Templates
151     private static final String TEMPLATE_VIEW_FORM_RESPONSE = "/skin/plugins/forms/view_form_response.html";
152     private static final String TEMPLATE_VIEW_FORM_FILE = "/skin/plugins/forms/view_form_file.html";
153     private static final String TEMPLATE_TASK_FORM_RESPONSE = "/skin/plugins/forms/task_form_workflow.html";
154     private static final String TEMPLATE_VIEW_LIST_FORM_RESPONSES = "/skin/formresponsesedito/view_list_form_responses.html";
155 
156     // Marks
157     private static final String MARK_WORKFLOW_ACTION_LIST = "workflow_action_list";
158     private static final String MARK_ID_FORM_RESPONSE = "id_form_response";
159     private static final String MARK_ID_ACTION = "id_action";
160     private static final String MARK_TASK_FORM = "tasks_form";
161     private static final String MARK_LIST_FILE = "listFiles";
162     private static final String MARK_LIST_FORMRESPONSE = "listFormResponses";
163     private static final String MARK_FORMRESPONSES_LIST = "listFormResponsePaginator";
164     private static final String MARK_FORMSREPONSEEDITO = "formsreponseedito";
165     private static final String MARK_URL_PAGINATOR = "urlPaginator";
166     private static final String MARK_NB_ITEMS_PER_PAGE = "nb_items_per_page";
167     
168     private static final String FULL_URL = "fullUrl";
169     
170     private static final String MARK_POINTS = "points";
171     private static final String MARK_POINTS_GEOJSON = "geojson";
172     private static final String MARK_POINTS_ID = "id";
173     private static final String MARK_POINTS_FIELDCODE = "code";
174     private static final String MARK_POINTS_TYPE = "type";
175     private static final String MARK_PAGINATOR = "paginator";
176     
177     private int _nItemsPerPage;
178     
179  // Errors
180     private static final String ERROR_RESOURCE_NOT_FOUND = "Resource not found";
181 
182     // Parameters
183     private static final String PARAMETER_ID_ACTION = "id_action";
184 
185     @View( value = VIEW_FORM_RESPONSE, defaultView = true )
186     public XPage getFormResponseView( HttpServletRequest request ) throws SiteMessageException
187     {
188         Locale locale = getLocale( request );
189         FormResponse formResponse = findFormResponseFrom( request );
190 
191         Collection<Action> actionsList = getActionsForUser( request, formResponse );
192         if("true".equals(request.getParameter(FormsConstants.PARAMETER_ACTION_SUCCESS)))
193         {
194             addInfo( MESSAGE_ACTION_SUCCESS,getLocale(request) );
195         }
196         Map<String, Object> model = getModel( );
197         model.put( FormsConstants.MARK_FORM_RESPONSE, formResponse );
198         model.put( MARK_WORKFLOW_ACTION_LIST, actionsList );
199         model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, ACTION_PROCESS_ACTION ) );
200 
201         XPage xPage = getXPage( TEMPLATE_VIEW_FORM_RESPONSE, getLocale( request ), model );
202         xPage.setTitle( I18nService.getLocalizedString( MESSAGE_FORM_RESPONSE_PAGETITLE, locale ) );
203         xPage.setPathLabel( I18nService.getLocalizedString( MESSAGE_FORM_RESPONSE_PATHLABEL, locale ) );
204 
205         return xPage;
206     }
207     
208     //NLG
209     @View( value = VIEW_FORM_LIST_RESPONSES, defaultView = false )
210     public XPage getFormListeResponsesView( HttpServletRequest request ) throws SiteMessageException
211     {
212         Locale locale = getLocale( request );
213         List<FormResponse> lstFormResponse = FormResponseHome.selectAllFormResponses();
214 
215         Map<String, Object> model = getModel( );
216         
217         SolrSearchEngine engine = SolrSearchEngine.getInstance( );
218         
219         List<HashMap<String, Object>> points = new ArrayList<HashMap<String, Object>>( );
220         
221         Optional<MapTemplate> xpageFrontOfficeMapTemplate = MapTemplateHome.findXpageFrontOffice();
222         List<DataLayer> lstDatalayers = DataLayerMapTemplateHome.getDataLayerListByMapTemplateId( xpageFrontOfficeMapTemplate.get().getId( ) );
223         
224         for ( DataLayer datalayer : lstDatalayers )
225         {
226         	List<SolrSearchResult> listResultsGeoloc = null;
227         	listResultsGeoloc = engine.getGeolocSearchResults( PARAMETER_SOLR_GEOJSON + ":" + datalayer.getSolrTag( ), null, 100 );
228             Optional<DataLayerMapTemplate> dataLayerMapTemplate = DataLayerMapTemplateHome.findByIdMapKeyIdDataLayerKey( xpageFrontOfficeMapTemplate.get().getId( ), datalayer.getId( ) );
229         	points.addAll( CartographyService.getGeolocModel( listResultsGeoloc, datalayer, dataLayerMapTemplate.get( ) ) );
230         }
231                 
232         // paginator & session related elements
233         String strCurrentPageIndex = request.getParameter( PARAMETER_PAGE_INDEX );
234         int nDefaultItemsPerPage = AppPropertiesService.getPropertyInt( PROPERTY_RESULTS_PER_PAGE, DEFAULT_RESULTS_PER_PAGE );
235         String strCurrentItemsPerPage = request.getParameter( PARAMETER_NB_ITEMS_PER_PAGE );
236         int nCurrentItemsPerPage = strCurrentItemsPerPage != null ? Integer.parseInt( strCurrentItemsPerPage ) : 0;
237 
238         strCurrentPageIndex = ( strCurrentPageIndex != null ) ? strCurrentPageIndex : DEFAULT_PAGE_INDEX;
239         
240         UrlItem url = new UrlItem( "jsp/site/Portal.jsp?page=formsResponseEdito&view=formListResponsesView" );
241         String strUrl = url.getUrl( );
242         
243         _nItemsPerPage = AbstractPaginator.getItemsPerPage( request, AbstractPaginator.PARAMETER_ITEMS_PER_PAGE, _nItemsPerPage, nDefaultItemsPerPage );
244         
245         Paginator<FormResponse> paginatorList = new LocalizedPaginator<>( lstFormResponse, _nItemsPerPage, strUrl, AbstractPaginator.PARAMETER_PAGE_INDEX,
246        		strCurrentPageIndex, getLocale(request ) );
247         
248         int nId = 1;
249         Formsreponseedito formsreponseedito;
250         Optional<Formsreponseedito> optFormsreponseedito = FormsreponseeditoHome.findByPrimaryKey( nId );
251         formsreponseedito = optFormsreponseedito.orElseThrow( ( ) -> new AppException(ERROR_RESOURCE_NOT_FOUND ) );
252 
253         
254         // Récupère la page courante depuis la requête
255         Paginator.getPageIndex(request, strUrl, strCurrentPageIndex);
256 
257         model.put( MARK_FORMSREPONSEEDITO, formsreponseedito );
258         
259         model.put( MARK_POINTS, points );
260         model.put( MARK_PAGINATOR, paginatorList );
261         model.put( MARK_FORMRESPONSES_LIST, paginatorList.getPageItems( ) );
262         model.put( MARK_LIST_FORMRESPONSE, lstFormResponse);
263         model.put( MARK_URL_PAGINATOR, strUrl );
264         model.put( MARK_NB_ITEMS_PER_PAGE, "" + _nItemsPerPage );
265         model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, ACTION_PROCESS_ACTION ) );
266         
267         String strRequestUrl = request.getRequestURL( ).toString( );
268         model.put( FULL_URL, strRequestUrl );
269 
270         XPage xPage = getXPage( TEMPLATE_VIEW_LIST_FORM_RESPONSES, getLocale( request ), model );
271         xPage.setTitle( I18nService.getLocalizedString( MESSAGE_FORM_RESPONSE_PAGETITLE, locale ) );
272         xPage.setPathLabel( I18nService.getLocalizedString( MESSAGE_FORM_RESPONSE_PATHLABEL, locale ) );
273 
274         return xPage;
275     }
276     
277 
278     @View( value = VIEW_FORM_FILE, defaultView = false )
279     public XPage getFormFileView( HttpServletRequest request ) throws SiteMessageException
280     {
281         Locale locale = getLocale( request );
282         FormResponse formResponse = findFormResponseFrom( request );
283         List<Response> listResponse = new ArrayList<Response>();
284         if("true".equals(request.getParameter(FormsConstants.PARAMETER_ACTION_SUCCESS)))
285         {
286             addInfo( MESSAGE_ACTION_SUCCESS,getLocale(request) );
287         }
288         Map<String, Object> model = getModel( );
289         model.put( FormsConstants.MARK_FORM_RESPONSE, formResponse );
290         model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, ACTION_PROCESS_ACTION ) );
291         for (FormResponseStep step: formResponse.getSteps() ){
292             listResponse.addAll(findResponses(step.getQuestions()));
293         }
294         model.put( FormsConstants.MARK_QUESTION_LIST_RESPONSES, listResponse );
295 
296         formResponse.getSteps().stream()
297                 .flatMap(step -> step.getQuestions().stream())
298                 .forEach(fqr -> {
299                     IEntryDisplayService displayService = EntryServiceManager.getInstance()
300                             .getEntryDisplayService(fqr.getQuestion().getEntry().getEntryType());
301                     if (displayService instanceof EntryTypeFileDisplayService) {
302                         displayService.getEntryTemplateDisplay(request, fqr.getQuestion().getEntry(), locale, model, DisplayType.READONLY_FRONTOFFICE);
303                     }
304                 });
305 
306         List<File> listFiles = formResponse.getSteps().stream()
307                 .flatMap(step -> step.getQuestions().stream())
308                 .filter(fqr -> fqr.getQuestion().getEntry().isOnlyDisplayInBack())
309                 .flatMap(fqr -> fqr.getEntryResponse().stream())
310                 .map(Response::getFile)
311                 .filter(Objects::nonNull)
312                 .collect(Collectors.toList());
313 
314         model.put( MARK_LIST_FILE, listFiles );
315         XPage xPage = getXPage( TEMPLATE_VIEW_FORM_FILE, getLocale( request ), model );
316         xPage.setTitle( I18nService.getLocalizedString( MESSAGE_FORM_RESPONSE_PAGETITLE, locale ) );
317         xPage.setPathLabel( I18nService.getLocalizedString( MESSAGE_FORM_RESPONSE_PATHLABEL, locale ) );
318 
319         return xPage;
320     }
321 
322     @fr.paris.lutece.portal.util.mvc.commons.annotations.Action( value = ACTION_PROCESS_ACTION )
323     public XPage doProcessAction( HttpServletRequest request ) throws AccessDeniedException
324     {
325         // CSRF Token control
326         if ( !SecurityTokenService.getInstance( ).validate( request, ACTION_PROCESS_ACTION ) )
327         {
328             throw new AccessDeniedException( MESSAGE_ERROR_TOKEN );
329         }
330         // Get parameters from request
331         int nIdFormResponse = NumberUtils.toInt( request.getParameter( FormsConstants.PARAMETER_ID_RESPONSE ), NumberUtils.INTEGER_MINUS_ONE );
332         int nIdAction = NumberUtils.toInt( request.getParameter( PARAMETER_ID_ACTION ), NumberUtils.INTEGER_MINUS_ONE );
333 
334         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
335         FormResponse formResponse = FormResponseHome.findByPrimaryKey( nIdFormResponse );
336 
337         if (  formResponse == null || !FormsResponseUtils.isAuthorized(formResponse, user) )
338         {
339             return redirect( request, VIEW_FORM_RESPONSE, FormsConstants.PARAMETER_ID_RESPONSE, nIdFormResponse );
340         }
341 
342         Locale locale = getLocale( request );
343         WorkflowService workflowService = WorkflowService.getInstance( );
344         if ( workflowService.isDisplayTasksForm( nIdAction, locale ) )
345         {
346             FormsAsynchronousUploadHandler.getHandler( ).removeSessionFiles( request.getSession( ) );
347 
348             String strHtmlTasksForm = WorkflowService.getInstance( ).getDisplayTasksForm( nIdFormResponse, FormResponse.RESOURCE_TYPE, nIdAction, request,
349                     locale, null );
350 
351             Map<String, Object> model = new LinkedHashMap<>( );
352             model.put( MARK_ID_FORM_RESPONSE, String.valueOf( nIdFormResponse ) );
353             model.put( MARK_ID_ACTION, String.valueOf( nIdAction ) );
354             model.put( MARK_TASK_FORM, strHtmlTasksForm );
355             model.put( SecurityTokenService.MARK_TOKEN, SecurityTokenService.getInstance( ).getToken( request, ACTION_SAVE_TASK_FORM ) );
356 
357             XPage xPage = getXPage( TEMPLATE_TASK_FORM_RESPONSE, locale, model );
358             xPage.setTitle( I18nService.getLocalizedString( MESSAGE_FORM_RESPONSE_PAGETITLE, locale ) );
359             xPage.setPathLabel( I18nService.getLocalizedString( MESSAGE_FORM_RESPONSE_PATHLABEL, locale ) );
360 
361             return xPage;
362         }
363 
364         try
365         {
366             workflowService.doProcessAction( nIdFormResponse, FormResponse.RESOURCE_TYPE, nIdAction, formResponse.getFormId( ), request, locale, false, user );
367         }
368         catch( AppException e )
369         {
370             AppLogService.error( "Error processing action for id response '" + nIdFormResponse + "' - cause : " + e.getMessage( ), e );
371         }
372         // Redirect to the correct view
373         return redirect( request, VIEW_FORM_RESPONSE, FormsConstants.PARAMETER_ID_RESPONSE, nIdFormResponse );
374     }
375 
376     /**
377      * Process workflow action
378      *
379      * @param request
380      *            The Http request
381      * @return The Jsp URL of the process result
382      * @throws AccessDeniedException
383      */
384     @fr.paris.lutece.portal.util.mvc.commons.annotations.Action( value = ACTION_SAVE_TASK_FORM )
385     public XPage doSaveTaskForm( HttpServletRequest request ) throws AccessDeniedException
386     {
387         int nIdFormResponse = NumberUtils.toInt( request.getParameter( FormsConstants.PARAMETER_ID_RESPONSE ), NumberUtils.INTEGER_MINUS_ONE );
388         int nIdAction = NumberUtils.toInt( request.getParameter( PARAMETER_ID_ACTION ), NumberUtils.INTEGER_MINUS_ONE );
389 
390         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
391         FormResponse formResponse = FormResponseHome.findByPrimaryKey( nIdFormResponse );
392 
393         if ( formResponse == null || !FormsResponseUtils.isAuthorized(formResponse, user) )
394         {
395             return redirect( request, VIEW_FORM_RESPONSE, FormsConstants.PARAMETER_ID_RESPONSE, nIdFormResponse );
396         }
397 
398         // CSRF Token control
399         if ( !SecurityTokenService.getInstance( ).validate( request, ACTION_SAVE_TASK_FORM ) )
400         {
401             throw new AccessDeniedException( MESSAGE_ERROR_TOKEN );
402         }
403 
404         int nIdForm = formResponse.getFormId( );
405         Locale locale = getLocale( request );
406         WorkflowService workflowService = WorkflowService.getInstance( );
407 
408         if ( workflowService.canProcessAction( nIdFormResponse, FormResponse.RESOURCE_TYPE, nIdAction, nIdForm, request, false, user ) )
409         {
410             try
411             {
412                 String strError = workflowService.doSaveTasksForm( nIdFormResponse, FormResponse.RESOURCE_TYPE, nIdAction, nIdForm, request, locale, user );
413                 if ( strError != null )
414                 {
415                     return redirect( request, strError );
416                 }
417             }
418             catch( AppException e )
419             {
420                 AppLogService.error( "Error processing action for record " + nIdFormResponse, e );
421             }
422         }
423         else
424         {
425             addError( MESSAGE_ACTION_ERROR, locale );
426         }
427         return redirect( request, VIEW_FORM_RESPONSE, FormsConstants.PARAMETER_ID_RESPONSE, nIdFormResponse );
428     }
429 
430     private Collection<Action> getActionsForUser( HttpServletRequest request, FormResponse formResponse )
431     {
432         LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
433         if (formResponse != null)
434         {
435             Form form = FormHome.findByPrimaryKey( formResponse.getFormId( ) );
436             if (FormsResponseUtils.isAuthorized(formResponse, SecurityService.getInstance( ).getRegisteredUser( request ), form ))
437             {
438                 WorkflowService workflowService = WorkflowService.getInstance( );
439                 boolean workflowEnabled = workflowService.isAvailable( ) && ( form.getIdWorkflow( ) != FormsConstants.DEFAULT_ID_VALUE );
440 
441                 if ( workflowEnabled )
442                 {
443                     return workflowService.getActions( formResponse.getId( ), FormResponse.RESOURCE_TYPE, form.getIdWorkflow( ), (User) user );
444                 }
445             }
446         }
447         return new ArrayList<>( );
448     }
449 
450     /**
451      * Finds the formResponse from the specified request
452      *
453      * @param request
454      *            the request
455      * @return the found formResponse, or {@code null} if not found
456      * @throws FormResponseNotFoundException
457      *             if the form is not found
458      * @throws SiteMessageException
459      *             if the formResponse is not accessible
460      */
461     private FormResponse findFormResponseFrom( HttpServletRequest request ) throws SiteMessageException
462     {
463         FormResponse formResponse = null;
464         int nIdFormResponse = NumberUtils.toInt( request.getParameter( FormsConstants.PARAMETER_ID_RESPONSE ), FormsConstants.DEFAULT_ID_VALUE );
465 
466         if ( nIdFormResponse != FormsConstants.DEFAULT_ID_VALUE )
467         {
468             formResponse = FormResponseHome.findByPrimaryKey( nIdFormResponse );
469         }
470         else
471         {
472             SiteMessageService.setMessage( request, MESSAGE_ERROR_NOT_FOUND_FORM_RESPONSE, SiteMessage.TYPE_ERROR );
473         }
474 
475         if ( formResponse == null )
476         {
477             SiteMessageService.setMessage( request, MESSAGE_ERROR_NOT_FOUND_FORM_RESPONSE, SiteMessage.TYPE_ERROR );
478         }
479         else if ( !formResponse.isPublished( ) && !FormsResponseUtils.isAuthorized(formResponse, SecurityService.getInstance( ).getRegisteredUser( request ) ) )
480         {
481             SiteMessageService.setMessage( request, MESSAGE_ERROR_NOT_PUBLISHED_FORM_RESPONSE, SiteMessage.TYPE_ERROR );
482         }
483 
484         return formResponse;
485     }
486 
487     private List<Response> findResponses(List<FormQuestionResponse> listFormQuestionResponse )
488     {
489         List<Response> listResponse = new ArrayList<>( );
490 
491         if ( listFormQuestionResponse != null )
492         {
493             for ( FormQuestionResponse formQuestionResponse : listFormQuestionResponse )
494             {
495                 listResponse.addAll(formQuestionResponse.getEntryResponse( ));
496             }
497         }
498 
499         return listResponse;
500     }
501 }