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.service.dashboard;
35  
36  import fr.paris.lutece.portal.business.dashboard.DashboardFactory;
37  import fr.paris.lutece.portal.business.dashboard.DashboardFilter;
38  import fr.paris.lutece.portal.business.dashboard.DashboardHome;
39  import fr.paris.lutece.portal.business.user.AdminUser;
40  import fr.paris.lutece.portal.service.plugin.Plugin;
41  import fr.paris.lutece.portal.service.spring.SpringContextService;
42  import fr.paris.lutece.portal.service.util.AppLogService;
43  import fr.paris.lutece.portal.service.util.AppPropertiesService;
44  import fr.paris.lutece.util.ReferenceList;
45  import fr.paris.lutece.util.sort.AttributeComparator;
46  
47  import java.util.ArrayList;
48  import java.util.Collections;
49  import java.util.HashMap;
50  import java.util.List;
51  import java.util.Map;
52  
53  import javax.servlet.http.HttpServletRequest;
54  
55  import org.apache.commons.collections.CollectionUtils;
56  
57  /**
58   * Dashboard Service
59   */
60  public final class DashboardService
61  {
62      // Properties
63      private static final String PROPERTY_COLUMN_COUNT = "dashboard.columnCount";
64  
65      // Constants
66      private static final String ALL = "ALL";
67      private static final String EMPTY_STRING = "";
68      private static final String ORDER = "order";
69      private static final int CONSTANTE_FIRST_ORDER = 1;
70      private static final int CONSTANTE_DEFAULT_COLUMN_COUNT = 3;
71      private static DashboardServiceoard/DashboardService.html#DashboardService">DashboardService _singleton = new DashboardService( );
72  
73      /**
74       * Private Constructor
75       */
76      private DashboardService( )
77      {
78      }
79  
80      /**
81       * Return the unique instance
82       * 
83       * @return The instance
84       */
85      public static DashboardService getInstance( )
86      {
87          return _singleton;
88      }
89  
90      /**
91       * Returns the column count, with {@link DashboardService#PROPERTY_COLUMN_COUNT}. Default is {@link DashboardService#CONSTANTE_DEFAULT_COLUMN_COUNT}
92       * 
93       * @return the column count
94       */
95      public int getColumnCount( )
96      {
97          return AppPropertiesService.getPropertyInt( PROPERTY_COLUMN_COUNT, CONSTANTE_DEFAULT_COLUMN_COUNT );
98      }
99  
100     /**
101      * All known dashboards as declared in SpringContext
102      * 
103      * @return dashboards list
104      */
105     public List<IDashboardComponent> getAllDashboardComponents( )
106     {
107         return DashboardFactory.getAllDashboardComponents( );
108     }
109 
110     /**
111      *
112      * @param nColumn
113      *            the column id
114      * @return all dashboards for this column
115      */
116     public List<IDashboardComponent> getDashboardComponents( int nColumn )
117     {
118         DashboardFilter/dashboard/DashboardFilter.html#DashboardFilter">DashboardFilter filter = new DashboardFilter( );
119         filter.setFilterColumn( nColumn );
120 
121         return DashboardHome.findByFilter( filter );
122     }
123 
124     /**
125      * Register a Dashboard Component
126      * 
127      * @param entry
128      *            The DashboardComponent entry defined in the plugin's XML file
129      * @param plugin
130      *            The plugin
131      */
132     public void registerDashboardComponent( DashboardComponentEntry entry, Plugin plugin )
133     {
134         try
135         {
136             DashboardComponent/../../../fr/paris/lutece/portal/service/dashboard/DashboardComponent.html#DashboardComponent">DashboardComponent dc = (DashboardComponent) Class.forName( entry.getComponentClass( ) ).newInstance( );
137 
138             dc.setName( entry.getName( ) );
139             dc.setRight( entry.getRight( ) );
140             dc.setPlugin( plugin );
141 
142             boolean bRegistered = DashboardFactory.registerDashboardComponent( dc );
143 
144             if ( bRegistered )
145             {
146                 AppLogService.info( "New Dashboard Component registered : " + entry.getName( ) );
147             }
148             else
149             {
150                 AppLogService.error( " Dashboard Component not registered : " + entry.getName( ) + " : " + entry.getComponentClass( ) );
151             }
152         }
153         catch( ClassNotFoundException | IllegalAccessException | InstantiationException e )
154         {
155             AppLogService.error( "Error registering a DashboardComponent : " + e.getMessage( ), e );
156         }
157     }
158 
159     /**
160      * Moves the dashboard.
161      * 
162      * @param dashboard
163      *            to move, with new values
164      * @param nOldColumn
165      *            previous column id
166      * @param nOldOrder
167      *            previous order
168      * @param bCreate
169      *            <code>true</code> if this is a new dashboard, <code>false</code> otherwise.
170      */
171     public void doMoveDashboard( IDashboardComponent dashboard, int nOldColumn, int nOldOrder, boolean bCreate )
172     {
173         int nColumn = dashboard.getZone( );
174         int nOrder = dashboard.getOrder( );
175 
176         // find the dashboard already with this order and column
177         DashboardFilter/dashboard/DashboardFilter.html#DashboardFilter">DashboardFilter filter = new DashboardFilter( );
178         filter.setFilterColumn( nColumn );
179 
180         List<IDashboardComponent> listColumnDashboards = DashboardHome.findByFilter( filter );
181 
182         if ( CollectionUtils.isNotEmpty( listColumnDashboards ) )
183         {
184             if ( AppLogService.isDebugEnabled( ) )
185             {
186                 AppLogService.debug( "Reordering  dashboard column " + dashboard.getZone( ) );
187             }
188 
189             // sort by order
190             Collections.sort( listColumnDashboards );
191 
192             int nMaxOrder = listColumnDashboards.get( listColumnDashboards.size( ) - 1 ).getOrder( );
193 
194             if ( ( nOldColumn == 0 ) || ( nOldColumn != nColumn ) )
195             {
196                 // was not in this column before, put to the end
197                 dashboard.setOrder( nMaxOrder + 1 );
198             }
199             else
200             {
201                 updateDashboardComponents( dashboard, listColumnDashboards, nOldOrder );
202 
203                 // dashboard are singletons, values are modified by getting it from database
204                 dashboard.setOrder( nOrder );
205                 dashboard.setZone( nColumn );
206             }
207         }
208         else
209         {
210             dashboard.setOrder( 1 );
211         }
212 
213         if ( bCreate )
214         {
215             // create dashboard
216             DashboardHome.create( dashboard );
217         }
218         else
219         {
220             // update dashboard
221             DashboardHome.update( dashboard );
222         }
223     }
224 
225     private void updateDashboardComponents( IDashboardComponent dashboard, List<IDashboardComponent> listColumnDashboards, int nOldOrder )
226     {
227         int nOrder = dashboard.getOrder( );
228         for ( IDashboardComponent dc : listColumnDashboards )
229         {
230             if ( dc.equals( dashboard ) )
231             {
232                 continue;
233             }
234 
235             if ( nOrder < nOldOrder )
236             {
237                 int nCurrentOrder = dc.getOrder( );
238 
239                 if ( ( nCurrentOrder >= nOrder ) && ( nCurrentOrder < nOldOrder ) )
240                 {
241                     dc.setOrder( nCurrentOrder + 1 );
242                     DashboardHome.update( dc );
243                 }
244             }
245             else
246                 if ( nOrder > nOldOrder )
247                 {
248                     int nCurrentOrder = dc.getOrder( );
249 
250                     if ( ( nCurrentOrder <= nOrder ) && ( nCurrentOrder > nOldOrder ) )
251                     {
252                         dc.setOrder( nCurrentOrder - 1 );
253                         DashboardHome.update( dc );
254                     }
255                 }
256         }
257     }
258 
259     /**
260      * Returns all dashboards with no column/order set
261      * 
262      * @return all dashboards with no column/order set
263      */
264     public List<IDashboardComponent> getNotSetDashboards( )
265     {
266         List<IDashboardComponent> listDashboards = DashboardHome.findAll( );
267         List<IDashboardComponent> listSpringDashboards = getAllDashboardComponents( );
268 
269         List<IDashboardComponent> listUnsetDashboards = new ArrayList<>( );
270 
271         for ( IDashboardComponent dashboard : listSpringDashboards )
272         {
273             if ( !listDashboards.contains( dashboard ) )
274             {
275                 listUnsetDashboards.add( dashboard );
276             }
277         }
278 
279         return listUnsetDashboards;
280     }
281 
282     /**
283      * Finds all dashboard with column and order set.
284      * 
285      * @param user
286      *            the current user
287      * @return a map where key is the column id, and value is the column's dashboard list.
288      */
289     public Map<String, List<IDashboardComponent>> getAllSetDashboards( AdminUser user )
290     {
291         Map<String, List<IDashboardComponent>> mapDashboardComponents = new HashMap<>( );
292 
293         List<IDashboardComponent> listDashboards = DashboardHome.findAll( );
294 
295         for ( IDashboardComponent dashboard : listDashboards )
296         {
297             int nColumn = dashboard.getZone( );
298             boolean bRight = user.checkRight( dashboard.getRight( ) ) || dashboard.getRight( ).equalsIgnoreCase( ALL );
299 
300             if ( !bRight )
301             {
302                 continue;
303             }
304 
305             String strColumn = Integer.toString( nColumn );
306 
307             // find this column list
308             List<IDashboardComponent> listDashboardsColumn = mapDashboardComponents.computeIfAbsent( strColumn, s -> new ArrayList<>( ) );
309 
310             // add dashboard to the list
311             listDashboardsColumn.add( dashboard );
312         }
313 
314         return mapDashboardComponents;
315     }
316 
317     /**
318      * Gets Data from all components of the zone
319      * 
320      * @param user
321      *            The user
322      * @param nZone
323      *            The dasboard zone
324      * @param request
325      *            HttpServletRequest
326      * @return Data of all components of the zone
327      */
328     public String getDashboardData( AdminUser user, int nZone, HttpServletRequest request )
329     {
330         StringBuilder sbDashboardData = new StringBuilder( );
331 
332         for ( IDashboardComponent dc : getDashboardComponents( nZone ) )
333         {
334             boolean bRight = user.checkRight( dc.getRight( ) ) || dc.getRight( ).equalsIgnoreCase( ALL );
335 
336             if ( ( dc.getZone( ) == nZone ) && dc.isEnabled( ) && bRight )
337             {
338                 sbDashboardData.append( dc.getDashboardData( user, request ) );
339             }
340         }
341 
342         return sbDashboardData.toString( );
343     }
344 
345     /**
346      * Get the list of dashboard from plugins
347      * 
348      * @param user
349      *            the current user
350      * @param request
351      *            HttpServletRequest
352      * @return the list of dashboards
353      */
354     public List<IDashboardComponent> getDashboards( AdminUser user, HttpServletRequest request )
355     {
356         List<IDashboardComponent> listDashboards = new ArrayList<>( );
357 
358         // Attributes associated to the plugins
359         for ( DashboardListenerService dashboardListenerService : SpringContextService.getBeansOfType( DashboardListenerService.class ) )
360         {
361             dashboardListenerService.getDashboardComponents( listDashboards, user, request );
362         }
363 
364         return listDashboards;
365     }
366 
367     /**
368      * Gets Data from all components of the zone
369      * 
370      * @param listDashboards
371      *            the list of dashboards
372      * @param user
373      *            The user
374      * @param nZone
375      *            The dasboard zone
376      * @param request
377      *            HttpServletRequest
378      * @return Data of all components of the zone
379      */
380     public String getDashboardData( List<IDashboardComponent> listDashboards, AdminUser user, int nZone, HttpServletRequest request )
381     {
382         List<IDashboardComponent> listDashboardComponents = new ArrayList<>( );
383 
384         for ( IDashboardComponent dc : listDashboards )
385         {
386             if ( dc.getZone( ) == nZone )
387             {
388                 listDashboardComponents.add( dc );
389             }
390         }
391 
392         Collections.sort( listDashboardComponents, new AttributeComparator( ORDER, true ) );
393 
394         StringBuilder sbDashboardData = new StringBuilder( );
395 
396         for ( IDashboardComponent dc : listDashboardComponents )
397         {
398             boolean bRight = user.checkRight( dc.getRight( ) ) || dc.getRight( ).equalsIgnoreCase( ALL );
399 
400             if ( ( dc.getZone( ) == nZone ) && dc.isEnabled( ) && bRight )
401             {
402                 sbDashboardData.append( dc.getDashboardData( user, request ) );
403             }
404         }
405 
406         return sbDashboardData.toString( );
407     }
408 
409     /**
410      * Reorders column's dashboard
411      * 
412      * @param nColumn
413      *            the column to reorder
414      */
415     public void doReorderColumn( int nColumn )
416     {
417         int nOrder = CONSTANTE_FIRST_ORDER;
418 
419         for ( IDashboardComponent dc : getDashboardComponents( nColumn ) )
420         {
421             dc.setOrder( nOrder++ );
422             DashboardHome.update( dc );
423         }
424     }
425 
426     /**
427      * Builds the map to with column id as key, and <code>true</code> as value if column is well ordered, <code>false</code> otherwise.
428      * 
429      * @return the map
430      */
431     public Map<String, Boolean> getOrderedColumnsStatus( )
432     {
433         Map<String, Boolean> mapOrderedStatus = new HashMap<>( );
434         List<Integer> listColumns = DashboardHome.findColumns( );
435 
436         for ( Integer nIdColumn : listColumns )
437         {
438             mapOrderedStatus.put( nIdColumn.toString( ), isWellOrdered( nIdColumn ) );
439         }
440 
441         return mapOrderedStatus;
442     }
443 
444     /**
445      * Determines if the column is well ordered
446      * 
447      * @param nColumn
448      *            the column id
449      * @return true if well ordered, <code>false</code> otherwise.
450      */
451     private boolean isWellOrdered( int nColumn )
452     {
453         int nOrder = CONSTANTE_FIRST_ORDER;
454 
455         for ( IDashboardComponent dc : getDashboardComponents( nColumn ) )
456         {
457             if ( nOrder != dc.getOrder( ) )
458             {
459                 return false;
460             }
461 
462             nOrder++;
463         }
464 
465         return true;
466     }
467 
468     /**
469      * Returns list with available column
470      * 
471      * @return all available columns
472      */
473     public ReferenceList getListAvailableColumns( )
474     {
475         ReferenceListList.html#ReferenceList">ReferenceList refList = new ReferenceList( );
476 
477         // add empty item
478         refList.addItem( EMPTY_STRING, EMPTY_STRING );
479 
480         for ( int nColumnIndex = 1; nColumnIndex <= getColumnCount( ); nColumnIndex++ )
481         {
482             refList.addItem( nColumnIndex, Integer.toString( nColumnIndex ) );
483         }
484 
485         return refList;
486     }
487 
488     /**
489      * Builds all refList order for all columns
490      * 
491      * @return the map with column id as key
492      */
493     public Map<String, ReferenceList> getMapAvailableOrders( )
494     {
495         Map<String, ReferenceList> mapAvailableOrders = new HashMap<>( );
496 
497         // get columns
498         for ( Integer nColumn : DashboardHome.findColumns( ) )
499         {
500             // get orders
501             mapAvailableOrders.put( nColumn.toString( ), getListAvailableOrders( nColumn ) );
502         }
503 
504         return mapAvailableOrders;
505     }
506 
507     /**
508      * Orders reference list for the given column
509      * 
510      * @param nColumn
511      *            column
512      * @return the refList
513      */
514     public ReferenceList getListAvailableOrders( int nColumn )
515     {
516         ReferenceListList.html#ReferenceList">ReferenceList refList = new ReferenceList( );
517 
518         // add empty item
519         refList.addItem( EMPTY_STRING, EMPTY_STRING );
520 
521         int nMaxOrder = DashboardHome.findMaxOrder( nColumn );
522 
523         for ( int nOrder = 1; nOrder <= nMaxOrder; nOrder++ )
524         {
525             refList.addItem( nOrder, Integer.toString( nOrder ) );
526         }
527 
528         return refList;
529     }
530 }