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.portal;
35  
36  import fr.paris.lutece.portal.business.XmlContent;
37  import fr.paris.lutece.portal.business.page.Page;
38  import fr.paris.lutece.portal.business.page.PageHome;
39  import fr.paris.lutece.portal.business.portalcomponent.PortalComponentHome;
40  import fr.paris.lutece.portal.business.style.ModeHome;
41  import fr.paris.lutece.portal.business.stylesheet.StyleSheet;
42  import fr.paris.lutece.portal.service.cache.AbstractCacheableService;
43  import fr.paris.lutece.portal.service.html.XmlTransformerService;
44  import fr.paris.lutece.portal.service.page.PageEvent;
45  import fr.paris.lutece.portal.service.page.PageEventListener;
46  import fr.paris.lutece.portal.service.page.PageService;
47  import fr.paris.lutece.portal.service.security.LuteceUser;
48  import fr.paris.lutece.portal.service.security.SecurityService;
49  import fr.paris.lutece.util.xml.XmlUtil;
50  
51  import org.apache.commons.lang3.StringUtils;
52  
53  import java.util.Arrays;
54  import java.util.Collection;
55  import java.util.HashMap;
56  import java.util.Map;
57  import java.util.Properties;
58  
59  import javax.servlet.http.HttpServletRequest;
60  
61  /**
62   * This Service build the portal menu
63   */
64  public final class PortalMenuService extends AbstractCacheableService implements PageEventListener
65  {
66      public static final int MENU_INIT = 0;
67      public static final int MENU_MAIN = 1;
68      public static final int MODE_NORMAL = 0;
69      public static final int MODE_ADMIN = 1;
70      private static final int PORTAL_COMPONENT_MENU_INIT_ID = 3;
71      private static final int PORTAL_COMPONENT_MAIN_MENU_ID = 4;
72      private static final String SERVICE_NAME = "PortalMenuService";
73  
74      // Menus cache
75      private static PortalMenuService _singleton;
76  
77      /** Creates a new instance of PortalMenuService */
78      private PortalMenuService( )
79      {
80          initCache( getName( ) );
81          PageService.addPageEventListener( this );
82      }
83  
84      /**
85       * Get the unique instance of the service
86       * 
87       * @return The unique instance
88       */
89      public static synchronized PortalMenuService getInstance( )
90      {
91          if ( _singleton == null )
92          {
93              _singleton = new PortalMenuService( );
94          }
95  
96          return _singleton;
97      }
98  
99      /**
100      * Returns the service name
101      * 
102      * @return The service name
103      */
104     public String getName( )
105     {
106         return SERVICE_NAME;
107     }
108 
109     /**
110      * Returns the menu bar from the cache or builds it if it not stored in it
111      * 
112      * @param request
113      *            The HTTP request
114      * @param nMode
115      *            The selected mode
116      * @param nPart
117      *            The part of the menu to build
118      * @param nCurrentPageId
119      *            The current page ID
120      * @return The list of the menus layed out with the stylesheet correpsonding to the mode
121      */
122     public String getMenuContent( int nCurrentPageId, int nMode, int nPart, HttpServletRequest request )
123     {
124         String strKey = getKey( nMode, nPart, request );
125         String strMenu = (String) getFromCache( strKey );
126 
127         // Seek for the key in the cache
128         if ( strMenu == null )
129         {
130             // Builds the HTML document
131             strMenu = buildMenuContent( nCurrentPageId, nMode, nPart, request );
132 
133             // Add it in the cache
134             putInCache( strKey, strMenu );
135 
136             return strMenu;
137         }
138 
139         // The document exist in the cache
140         return strMenu;
141     }
142 
143     /**
144      * Builds the menu bar
145      *
146      * @param nCurrentPageId
147      *            The current page ID
148      * @param nMode
149      *            The selected mode
150      * @param nPart
151      *            The part of the menu to build
152      * @param request
153      *            The HttpServletRequest
154      * @return The list of the menus layed out with the stylesheet of the mode
155      */
156     private String buildMenuContent( int nCurrentPageId, int nMode, int nPart, HttpServletRequest request )
157     {
158         Collection<Page> listPagesMenu = PageHome.getChildPagesMinimalData( PortalService.getRootPageId( ) );
159 
160         StringBuffer strXml = new StringBuffer( );
161         strXml.append( XmlUtil.getXmlHeader( ) );
162         XmlUtil.beginElement( strXml, XmlContent.TAG_MENU_LIST );
163 
164         int nMenuIndex = 1;
165 
166         for ( Page menuPage : listPagesMenu )
167         {
168             if ( ( menuPage.isVisible( request ) ) || ( nMode == MODE_ADMIN ) )
169             {
170                 buildPageXml( menuPage, strXml, nMode, nMenuIndex, nCurrentPageId, request );
171 
172                 nMenuIndex++;
173             }
174         }
175 
176         XmlUtil.endElement( strXml, XmlContent.TAG_MENU_LIST );
177 
178         // Added in v1.3
179         StyleSheet xslSource = getMenuXslSource( nMode, nPart );
180 
181         Properties outputProperties = ModeHome.getOuputXslProperties( nMode );
182 
183         // Added in v1.3
184         // Add a path param for choose url to use in admin or normal mode
185         Map<String, String> mapParamRequest = new HashMap<>( );
186         PortalService.setXslPortalPath( mapParamRequest, nMode );
187 
188         XmlTransformerServicervice.html#XmlTransformerService">XmlTransformerService xmlTransformerService = new XmlTransformerService( );
189 
190         return xmlTransformerService.transformBySourceWithXslCache( strXml.toString( ), xslSource, mapParamRequest, outputProperties );
191     }
192 
193     private void buildPageXml( Page menuPage, StringBuffer strXml, int nMode, int nMenuIndex, int nCurrentPageId, HttpServletRequest request )
194     {
195         XmlUtil.beginElement( strXml, XmlContent.TAG_MENU );
196         XmlUtil.addElement( strXml, XmlContent.TAG_MENU_INDEX, nMenuIndex );
197         XmlUtil.addElement( strXml, XmlContent.TAG_PAGE_ID, menuPage.getId( ) );
198         XmlUtil.addElementHtml( strXml, XmlContent.TAG_PAGE_NAME, menuPage.getName( ) );
199         XmlUtil.addElementHtml( strXml, XmlContent.TAG_CURRENT_PAGE_ID, String.valueOf( nCurrentPageId ) );
200 
201         Collection<Page> listSubLevelMenuPages = PageHome.getChildPagesMinimalData( menuPage.getId( ) );
202 
203         // add element submenu-list only if list not empty
204         if ( !listSubLevelMenuPages.isEmpty( ) )
205         {
206             // Seek of the sub-menus
207             XmlUtil.beginElement( strXml, XmlContent.TAG_SUBLEVEL_MENU_LIST );
208 
209             int nSubLevelMenuIndex = 1;
210 
211             for ( Page subLevelMenuPage : listSubLevelMenuPages )
212             {
213                 if ( ( subLevelMenuPage.isVisible( request ) ) || ( nMode == MODE_ADMIN ) )
214                 {
215                     XmlUtil.beginElement( strXml, XmlContent.TAG_SUBLEVEL_MENU );
216                     XmlUtil.addElement( strXml, XmlContent.TAG_MENU_INDEX, nMenuIndex );
217                     XmlUtil.addElement( strXml, XmlContent.TAG_SUBLEVEL_INDEX, nSubLevelMenuIndex );
218                     XmlUtil.addElement( strXml, XmlContent.TAG_PAGE_ID, subLevelMenuPage.getId( ) );
219                     XmlUtil.addElementHtml( strXml, XmlContent.TAG_PAGE_NAME, subLevelMenuPage.getName( ) );
220                     XmlUtil.endElement( strXml, XmlContent.TAG_SUBLEVEL_MENU );
221                     XmlUtil.addElementHtml( strXml, XmlContent.TAG_CURRENT_PAGE_ID, String.valueOf( nCurrentPageId ) );
222                 }
223             }
224 
225             XmlUtil.endElement( strXml, XmlContent.TAG_SUBLEVEL_MENU_LIST );
226         }
227 
228         XmlUtil.endElement( strXml, XmlContent.TAG_MENU );
229     }
230 
231     private StyleSheet getMenuXslSource( int nMode, int nPart )
232     {
233         // Use the same stylesheet for normal or admin mode
234         StyleSheet xslSource;
235 
236         // Selection of the XSL stylesheet
237         switch( nMode )
238         {
239             case MODE_NORMAL:
240             case MODE_ADMIN:
241                 xslSource = PortalComponentHome.getXsl( PORTAL_COMPONENT_MAIN_MENU_ID, MODE_NORMAL );
242 
243                 break;
244 
245             default:
246                 xslSource = PortalComponentHome.getXsl( PORTAL_COMPONENT_MAIN_MENU_ID, nMode );
247 
248                 break;
249         }
250 
251         if ( nPart == MENU_INIT )
252         {
253             switch( nMode )
254             {
255                 case MODE_NORMAL:
256                 case MODE_ADMIN:
257                     xslSource = PortalComponentHome.getXsl( PORTAL_COMPONENT_MENU_INIT_ID, MODE_NORMAL );
258 
259                     break;
260 
261                 default:
262                     xslSource = PortalComponentHome.getXsl( PORTAL_COMPONENT_MENU_INIT_ID, nMode );
263 
264                     break;
265             }
266         }
267         return xslSource;
268     }
269 
270     /**
271      * Returns the key corresponding to the part according to the selected mode
272      *
273      * @param nMode
274      *            The mode
275      * @param nPart
276      *            the part
277      * @param request
278      *            The HTTP request
279      * @return The key as a String
280      */
281     private String getKey( int nMode, int nPart, HttpServletRequest request )
282     {
283         String strRoles = "-";
284 
285         if ( SecurityService.isAuthenticationEnable( ) && request != null )
286         {
287             LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
288 
289             if ( ( user != null ) && ( user.getRoles( ) != null ) )
290             {
291                 String [ ] roles = user.getRoles( );
292                 Arrays.sort( roles );
293                 strRoles = StringUtils.join( roles, ',' );
294             }
295         }
296 
297         StringBuilder sbKey = new StringBuilder( );
298         sbKey.append( "[menu:" ).append( nPart ).append( "][m:" ).append( nMode ).append( "][roles:" ).append( strRoles ).append( ']' );
299 
300         return sbKey.toString( );
301     }
302 
303     @Override
304     public void processPageEvent( PageEvent event )
305     {
306         // page was added, removed or updated; clear cache
307         resetCache( );
308     }
309 }