View Javadoc
1   /*
2    * Copyright (c) 2002-2022, 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.portal.util.mvc.admin;
35  
36  import fr.paris.lutece.portal.service.security.AccessLogService;
37  import java.io.IOException;
38  import java.io.OutputStream;
39  import java.io.PrintWriter;
40  import java.lang.reflect.InvocationTargetException;
41  import java.lang.reflect.Method;
42  import java.util.ArrayList;
43  import java.util.HashMap;
44  import java.util.List;
45  import java.util.Locale;
46  import java.util.Map;
47  import java.util.Map.Entry;
48  
49  import javax.servlet.http.HttpServletRequest;
50  import javax.servlet.http.HttpServletResponse;
51  
52  import org.apache.logging.log4j.Logger;
53  import org.springframework.util.ReflectionUtils;
54  
55  import fr.paris.lutece.portal.business.user.AdminUser;
56  import fr.paris.lutece.portal.service.admin.AccessDeniedException;
57  import fr.paris.lutece.portal.service.admin.AdminAuthenticationService;
58  import fr.paris.lutece.portal.service.i18n.I18nService;
59  import fr.paris.lutece.portal.service.security.AccessLoggerConstants;
60  import fr.paris.lutece.portal.service.template.AppTemplateService;
61  import fr.paris.lutece.portal.service.util.AppException;
62  import fr.paris.lutece.portal.service.util.AppLogService;
63  import fr.paris.lutece.portal.util.mvc.admin.annotations.Controller;
64  import fr.paris.lutece.portal.util.mvc.utils.MVCMessage;
65  import fr.paris.lutece.portal.util.mvc.utils.MVCUtils;
66  import fr.paris.lutece.portal.web.admin.PluginAdminPageJspBean;
67  import fr.paris.lutece.util.ErrorMessage;
68  import fr.paris.lutece.util.beanvalidation.ValidationError;
69  import fr.paris.lutece.util.html.HtmlTemplate;
70  import fr.paris.lutece.util.url.UrlItem;
71  
72  /**
73   * MVC Admin JspBean implementation let use MVC model to develop admin feature.
74   */
75  public abstract class MVCAdminJspBean extends PluginAdminPageJspBean
76  {
77      private static final long serialVersionUID = 278165302545398831L;
78  
79      // markers
80      private static final String MARK_ERRORS = "errors";
81      private static final String MARK_INFOS = "infos";
82      private static final String MARK_WARNINGS = "warnings";
83  
84      // properties
85      private static final String PROPERTY_SITE_CODE = "lutece.code";
86  
87      // instance vars
88      private static Logger _logger = MVCUtils.getLogger( );
89      private List<ErrorMessage> _listErrors = new ArrayList<>( );
90      private List<ErrorMessage> _listInfos = new ArrayList<>( );
91      private List<ErrorMessage> _listWarnings = new ArrayList<>( );
92      private Controller _controller = getClass( ).getAnnotation( Controller.class );
93      private HttpServletResponse _response;
94  
95      /**
96       * Process request as a controller
97       * 
98       * @param request
99       *            The HTTP request
100      * @param response
101      *            The HTTP response
102      * @return The page content
103      * @throws AccessDeniedException
104      *             If the user's has no right
105      */
106     public String processController( HttpServletRequest request, HttpServletResponse response ) throws AccessDeniedException
107     {
108         _response = response;
109         init( request, _controller.right( ) );
110 
111         Method [ ] methods = ReflectionUtils.getAllDeclaredMethods( getClass( ) );
112 
113         try
114         {
115             // Process views
116             Method m = MVCUtils.findViewAnnotedMethod( request, methods );
117 
118             AdminUser adminUser = AdminAuthenticationService.getInstance( ).getRegisteredUser( request );
119 
120             if ( m != null )
121             {
122                 AccessLogService.getInstance( ).trace( AccessLoggerConstants.EVENT_TYPE_VIEW, m.getName( ), adminUser,
123                         request.getRequestURL( ) + "?" + request.getQueryString( ), AccessLogService.ACCESS_LOG_BO );
124                 return (String) m.invoke( this, request );
125             }
126 
127             // Process actions
128             m = MVCUtils.findActionAnnotedMethod( request, methods );
129 
130             if ( m != null )
131             {
132                 AccessLogService.getInstance( ).debug( AccessLoggerConstants.EVENT_TYPE_ACTION, m.getName( ), adminUser,
133                         request.getRequestURL( ) + "?" + request.getQueryString( ), AccessLogService.ACCESS_LOG_BO );
134                 return (String) m.invoke( this, request );
135             }
136 
137             // No view or action found so display the default view
138             m = MVCUtils.findDefaultViewMethod( methods );
139 
140             AccessLogService.getInstance( ).trace( AccessLoggerConstants.EVENT_TYPE_VIEW, m.getName( ), adminUser,
141                     request.getRequestURL( ) + "?" + request.getQueryString( ), AccessLogService.ACCESS_LOG_BO );
142             return (String) m.invoke( this, request );
143         }
144         catch( InvocationTargetException e )
145         {
146             if ( e.getTargetException( ) instanceof AccessDeniedException )
147             {
148                 throw (AccessDeniedException) e.getTargetException( );
149             }
150 
151             throw new AppException( "MVC Error dispaching view and action ", e );
152         }
153         catch( IllegalAccessException e )
154         {
155             throw new AppException( "MVC Error dispaching view and action ", e );
156         }
157     }
158 
159     // //////////////////////////////////////////////////////////////////////////
160     // Page utils
161 
162     /**
163      * Add an error message
164      * 
165      * @param strMessage
166      *            The message
167      */
168     protected void addError( String strMessage )
169     {
170         _listErrors.add( new MVCMessage( strMessage ) );
171     }
172 
173     /**
174      * Add an error message
175      * 
176      * @param strMessageKey
177      *            The message
178      * @param locale
179      *            The locale
180      */
181     protected void addError( String strMessageKey, Locale locale )
182     {
183         _listErrors.add( new MVCMessage( I18nService.getLocalizedString( strMessageKey, locale ) ) );
184     }
185 
186     /**
187      * Add a warning message
188      * 
189      * @param strMessage
190      *            The message
191      */
192     protected void addWarning( String strMessage )
193     {
194         _listWarnings.add( new MVCMessage( strMessage ) );
195     }
196 
197     /**
198      * Add an warning message
199      * 
200      * @param strMessageKey
201      *            The message
202      * @param locale
203      *            The locale
204      */
205     protected void addWarning( String strMessageKey, Locale locale )
206     {
207         _listWarnings.add( new MVCMessage( I18nService.getLocalizedString( strMessageKey, locale ) ) );
208     }
209 
210     /**
211      * Add an info message
212      * 
213      * @param strMessage
214      *            The message
215      */
216     protected void addInfo( String strMessage )
217     {
218         _listInfos.add( new MVCMessage( strMessage ) );
219     }
220 
221     /**
222      * Add an info message
223      * 
224      * @param strMessageKey
225      *            The message key
226      * @param locale
227      *            The locale
228      */
229     protected void addInfo( String strMessageKey, Locale locale )
230     {
231         _listInfos.add( new MVCMessage( I18nService.getLocalizedString( strMessageKey, locale ) ) );
232     }
233 
234     /**
235      * Fill the model with commons objects used in templates
236      * 
237      * @param model
238      *            The model
239      */
240     protected void fillCommons( Map<String, Object> model )
241     {
242         List<ErrorMessage> listErrors = new ArrayList<>( _listErrors );
243         List<ErrorMessage> listInfos = new ArrayList<>( _listInfos );
244         List<ErrorMessage> listWarnings = new ArrayList<>( _listWarnings );
245         model.put( MARK_ERRORS, listErrors );
246         model.put( MARK_INFOS, listInfos );
247         model.put( MARK_WARNINGS, listWarnings );
248         _listErrors.clear( );
249         _listInfos.clear( );
250         _listWarnings.clear( );
251     }
252 
253     /**
254      * Get a model Object filled with default values
255      * 
256      * @return The model
257      */
258     protected Map<String, Object> getModel( )
259     {
260         Map<String, Object> model = new HashMap<>( );
261         fillCommons( model );
262 
263         return model;
264     }
265 
266     /**
267      * Return the page content
268      * 
269      * @param strTemplate
270      *            The template
271      * @return The page
272      */
273     protected String getPage( String strTemplate )
274     {
275         String strPageTitleProperty = _controller.pageTitleProperty( );
276 
277         return getPage( strPageTitleProperty, strTemplate, getModel( ) );
278     }
279 
280     /**
281      * Return the page content
282      * 
283      * @param strPageTitleProperty
284      *            The page title property
285      * @param strTemplate
286      *            The template
287      * @return The page
288      */
289     protected String getPage( String strPageTitleProperty, String strTemplate )
290     {
291         return getPage( strPageTitleProperty, strTemplate, getModel( ) );
292     }
293 
294     /**
295      * Return the page content
296      * 
297      * @param strPageTitleProperty
298      *            The page title property
299      * @param strTemplate
300      *            The template
301      * @param model
302      *            The model
303      * @return The page
304      */
305     protected String getPage( String strPageTitleProperty, String strTemplate, Map<String, Object> model )
306     {
307         setPageTitleProperty( strPageTitleProperty );
308 
309         HtmlTemplate template = AppTemplateService.getTemplate( strTemplate, getLocale( ), model );
310 
311         return getAdminPage( template.getHtml( ) );
312     }
313 
314     /**
315      * Validate a bean
316      * 
317      * @param <T>
318      *            The bean class
319      * @param bean
320      *            The bean
321      * @param strPrefix
322      *            The prefix
323      * @return true if validated otherwise false
324      */
325     protected <T> boolean validateBean( T bean, String strPrefix )
326     {
327         List<ValidationError> errors = validate( bean, strPrefix );
328 
329         if ( errors.isEmpty( ) )
330         {
331             return true;
332         }
333 
334         for ( ValidationError errorValidation : errors )
335         {
336             MVCMessage/util/mvc/utils/MVCMessage.html#MVCMessage">MVCMessage error = new MVCMessage( );
337             error.setMessage( errorValidation.getMessage( ) );
338             error.setFieldName( errorValidation.getFieldName( ) );
339             _listErrors.add( error );
340         }
341 
342         return false;
343     }
344 
345     // //////////////////////////////////////////////////////////////////////////
346     // Redirect utils
347 
348     /**
349      * Return the JSP name used as controller
350      * 
351      * @return The JSP name
352      */
353     protected String getControllerJsp( )
354     {
355         return _controller.controllerJsp( );
356     }
357 
358     /**
359      * Return the path of the JSP used as controller
360      * 
361      * @return The controller path
362      */
363     protected String getControllerPath( )
364     {
365         return _controller.controllerPath( );
366     }
367 
368     /**
369      * Redirect to requested page
370      *
371      * @param request
372      *            the http request
373      * @param strTarget
374      *            the targeted page
375      * @return null. The page should be redirected
376      */
377     protected String redirect( HttpServletRequest request, String strTarget )
378     {
379         try
380         {
381             _logger.debug( "Redirect : {}", strTarget );
382             _response.sendRedirect( strTarget );
383         }
384         catch( IOException e )
385         {
386             _logger.error( "Unable to redirect : {} : {}", strTarget, e.getMessage( ), e );
387         }
388 
389         return null;
390     }
391 
392     /**
393      * Redirect to an url defined by given parameters
394      * 
395      * @param request
396      *            The HTTP request
397      * @param strView
398      *            The View name
399      * @param strParameter
400      *            The additional parameter
401      * @param nValue
402      *            The additional parameter's value
403      * @return The redirection result
404      */
405     protected String redirect( HttpServletRequest request, String strView, String strParameter, int nValue )
406     {
407         UrlItemtil/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( getViewUrl( strView ) );
408         url.addParameter( strParameter, nValue );
409 
410         return redirect( request, url.getUrl( ) );
411     }
412 
413     /**
414      * Redirect to an url defined by given parameters
415      * 
416      * @param request
417      *            The HTTP request
418      * @param strView
419      *            The View name
420      * @param strParameter1
421      *            The first additional parameter
422      * @param nValue1
423      *            The first additional parameter's value
424      * @param strParameter2
425      *            The second additionnal parameter
426      * @param nValue2
427      *            The second additionnal parameter's value
428      * @return The redirection result
429      */
430     protected String redirect( HttpServletRequest request, String strView, String strParameter1, int nValue1, String strParameter2, int nValue2 )
431     {
432         UrlItemtil/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( getViewUrl( strView ) );
433         url.addParameter( strParameter1, nValue1 );
434         url.addParameter( strParameter2, nValue2 );
435 
436         return redirect( request, url.getUrl( ) );
437     }
438 
439     /**
440      * Redirect to an url defined by given parameters
441      * 
442      * @param request
443      *            The HTTP request
444      * @param strView
445      *            The View name
446      * @param additionalParameters
447      *            A map containing parameters to add to the URL. Keys of the map are parameters name, and values are parameters values
448      * @return The XPage redirected
449      */
450     protected String redirect( HttpServletRequest request, String strView, Map<String, String> additionalParameters )
451     {
452         UrlItemtil/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( getViewUrl( strView ) );
453 
454         if ( additionalParameters != null )
455         {
456             for ( Entry<String, String> entry : additionalParameters.entrySet( ) )
457             {
458                 url.addParameter( entry.getKey( ), entry.getValue( ) );
459             }
460         }
461 
462         return redirect( request, url.getUrl( ) );
463     }
464 
465     /**
466      * Redirect to requested view
467      *
468      * @param request
469      *            the http request
470      * @param strView
471      *            the targeted view
472      * @return The redirection result
473      */
474     protected String redirectView( HttpServletRequest request, String strView )
475     {
476         return redirect( request, getViewUrl( strView ) );
477     }
478 
479     /**
480      * Get a View URL
481      * 
482      * @param strView
483      *            The view name
484      * @return The URL
485      */
486     protected String getViewUrl( String strView )
487     {
488         UrlItemtil/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( getControllerJsp( ) );
489         url.addParameter( MVCUtils.PARAMETER_VIEW, strView );
490 
491         return url.getUrl( );
492     }
493 
494     /**
495      * Gets the view URL with the JSP path
496      * 
497      * @param strView
498      *            The view
499      * @return The URL
500      */
501     protected String getViewFullUrl( String strView )
502     {
503         return getControllerPath( ) + getViewUrl( strView );
504     }
505 
506     /**
507      * Get Action URL
508      * 
509      * @param strAction
510      *            The view name
511      * @return The URL
512      */
513     protected String getActionUrl( String strAction )
514     {
515         UrlItemtil/url/UrlItem.html#UrlItem">UrlItem url = new UrlItem( getControllerPath( ) + getControllerJsp( ) );
516         url.addParameter( MVCUtils.PARAMETER_ACTION, strAction );
517 
518         return url.getUrl( );
519     }
520 
521     /**
522      * Initiates a file download
523      * 
524      * @param strData
525      *            Data of the file to download
526      * @param strFilename
527      *            Name of file
528      * @param strContentType
529      *            content type to set to the response
530      */
531     protected void download( String strData, String strFilename, String strContentType )
532     {
533         HttpServletResponse response = _response;
534         PrintWriter out = null;
535         response.setHeader( "Content-Disposition", "attachment; filename=\"" + strFilename + "\";" );
536         MVCUtils.addDownloadHeaderToResponse( response, strFilename, strContentType );
537 
538         try
539         {
540             out = response.getWriter( );
541             out.print( strData );
542         }
543         catch( IOException e )
544         {
545             AppLogService.error( e.getStackTrace( ), e );
546         }
547         finally
548         {
549             if ( out != null )
550             {
551                 out.close( );
552             }
553         }
554     }
555 
556     /**
557      * Initiates a download of a byte array
558      * 
559      * @param data
560      *            Data to download
561      * @param strFilename
562      *            Name of the downloaded file
563      * @param strContentType
564      *            Content type to set to the response
565      */
566     protected void download( byte [ ] data, String strFilename, String strContentType )
567     {
568         HttpServletResponse response = _response;
569         OutputStream os;
570         MVCUtils.addDownloadHeaderToResponse( response, strFilename, strContentType );
571 
572         try
573         {
574             os = response.getOutputStream( );
575             os.write( data );
576             os.close( );
577         }
578         catch( IOException e )
579         {
580             AppLogService.error( e.getStackTrace( ), e );
581         }
582     }
583 }