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.content;
35  
36  import fr.paris.lutece.portal.service.init.LuteceInitException;
37  import fr.paris.lutece.portal.service.message.SiteMessage;
38  import fr.paris.lutece.portal.service.message.SiteMessageException;
39  import fr.paris.lutece.portal.service.message.SiteMessageService;
40  import fr.paris.lutece.portal.service.portal.PortalService;
41  import fr.paris.lutece.portal.service.security.LuteceUser;
42  import fr.paris.lutece.portal.service.security.SecurityService;
43  import fr.paris.lutece.portal.service.security.UserNotSignedException;
44  import fr.paris.lutece.portal.service.spring.SpringContextService;
45  import fr.paris.lutece.portal.service.template.AppTemplateService;
46  import fr.paris.lutece.portal.service.util.AppException;
47  import fr.paris.lutece.portal.service.util.AppLogService;
48  import fr.paris.lutece.portal.web.xpages.XPage;
49  import fr.paris.lutece.portal.web.xpages.XPageApplication;
50  import fr.paris.lutece.portal.web.xpages.XPageApplicationEntry;
51  import fr.paris.lutece.util.html.HtmlTemplate;
52  import fr.paris.lutece.util.http.SecurityUtil;
53  
54  import org.apache.commons.collections.CollectionUtils;
55  import org.springframework.beans.factory.NoSuchBeanDefinitionException;
56  
57  import java.util.Collection;
58  import java.util.HashMap;
59  import java.util.List;
60  import java.util.Map;
61  
62  import javax.servlet.http.HttpServletRequest;
63  import javax.servlet.http.HttpSession;
64  
65  /**
66   * This class delivers Extra pages (xpages) to web components. An XPage is a page where the content is provided by a specific class, but should be integrated
67   * into the portal struture and design. XPageApps are identified by a key name. To display an XPage into the portal just call the following url :<br>
68   * <code>
69   * Portal.jsp?page=<i>keyname</i>&amp;param1=value1&amp; ...&amp;paramN=valueN </code>
70   *
71   * @see fr.paris.lutece.portal.web.xpages.XPage
72   */
73  public class XPageAppService extends ContentService
74  {
75      public static final String PARAM_XPAGE_APP = "page";
76      private static final String ERROR_INSTANTIATION = "Error instantiating XPageApplication : ";
77      private static final String CONTENT_SERVICE_NAME = "XPageAppService";
78      private static final String MESSAGE_ERROR_APP_BODY = "portal.util.message.errorXpageApp";
79      private static final String ATTRIBUTE_XPAGE = "LUTECE_XPAGE_";
80      private static Map<String, XPageApplicationEntry> _mapApplications = new HashMap<>( );
81  
82      /**
83       * Register an application by its entry defined in the plugin xml file
84       * 
85       * @param entry
86       *            The application entry
87       * @throws LuteceInitException
88       *             If an error occured
89       */
90      public static void registerXPageApplication( XPageApplicationEntry entry ) throws LuteceInitException
91      {
92          try
93          {
94              if ( entry.getClassName( ) == null )
95              {
96                  String applicationBeanName = entry.getPluginName( ) + ".xpage." + entry.getId( );
97  
98                  if ( !SpringContextService.getContext( ).containsBean( applicationBeanName ) )
99                  {
100                     throw new LuteceInitException( ERROR_INSTANTIATION + entry.getId( ) + " - Could not find bean named " + applicationBeanName,
101                             new NoSuchBeanDefinitionException( applicationBeanName ) );
102                 }
103             }
104             else
105             {
106                 // check that the class can be found
107                 Class.forName( entry.getClassName( ) ).newInstance( );
108             }
109 
110             _mapApplications.put( entry.getId( ), entry );
111             AppLogService.info( "New XPage application registered : {} {}", entry::getId, ( ) -> ( entry.isEnabled( ) ? "" : " (disabled)" ) );
112         }
113         catch( ClassNotFoundException | InstantiationException | IllegalAccessException e )
114         {
115             throw new LuteceInitException( ERROR_INSTANTIATION + entry.getId( ) + " - " + e.getCause( ), e );
116         }
117     }
118 
119     /**
120      * Returns the Content Service name
121      *
122      * @return The name as a String
123      */
124     @Override
125     public String getName( )
126     {
127         return CONTENT_SERVICE_NAME;
128     }
129 
130     /**
131      * Analyzes request parameters to see if the request should be handled by the current Content Service
132      *
133      * @param request
134      *            The HTTP request
135      * @return true if this ContentService should handle this request
136      */
137     @Override
138     public boolean isInvoked( HttpServletRequest request )
139     {
140         String strXPage = request.getParameter( PARAM_XPAGE_APP );
141 
142         return ( strXPage != null ) && ( strXPage.length( ) > 0 );
143     }
144 
145     /**
146      * Gets the current cache status.
147      *
148      * @return true if enable, otherwise false
149      */
150     @Override
151     public boolean isCacheEnable( )
152     {
153         return false;
154     }
155 
156     /**
157      * Reset the cache.
158      */
159     @Override
160     public void resetCache( )
161     {
162         // Do nothing
163     }
164 
165     /**
166      * Gets the number of item currently in the cache.
167      *
168      * @return the number of item currently in the cache.
169      */
170     @Override
171     public int getCacheSize( )
172     {
173         return 0;
174     }
175 
176     /**
177      * Build the XPage content.
178      *
179      * @param request
180      *            The HTTP request.
181      * @param nMode
182      *            The current mode.
183      * @return The HTML code of the page.
184      * @throws UserNotSignedException
185      *             The User Not Signed Exception
186      * @throws SiteMessageException
187      *             occurs when a site message need to be displayed
188      */
189     @Override
190     public String getPage( HttpServletRequest request, int nMode ) throws UserNotSignedException, SiteMessageException
191     {
192         // Gets XPage info from the lutece.properties
193         String strName = request.getParameter( PARAM_XPAGE_APP );
194 
195         XPageApplicationEntry entry = getApplicationEntry( strName );
196 
197         // TODO : Handle entry == null
198         if ( ( entry == null ) || ( !entry.isEnable( ) ) )
199         {
200             AppLogService.error( "The specified Xpage '{}' cannot be retrieved. Check installation of your Xpage application.",
201                     ( ) -> SecurityUtil.logForgingProtect( strName ) );
202             SiteMessageService.setMessage( request, MESSAGE_ERROR_APP_BODY, SiteMessage.TYPE_ERROR );
203 
204             return null; // unreachable because SiteMessageService.setMessage throws
205         }
206 
207         XPage page = null;
208         List<String> listRoles = entry.getRoles( );
209 
210         if ( SecurityService.isAuthenticationEnable( ) && CollectionUtils.isNotEmpty( listRoles ) )
211         {
212             LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
213 
214             if ( user == null )
215             {
216                 throw new UserNotSignedException( );
217             }
218 
219             boolean bAutorized = SecurityService.getInstance( ).isUserInAnyRole( request, listRoles );
220 
221             if ( bAutorized )
222             {
223                 XPageApplication application = getXPageSessionInstance( request, entry );
224                 page = application.getPage( request, nMode, entry.getPlugin( ) );
225             }
226             else
227             {
228                 // The user doesn't have the correct role
229                 String strAccessDeniedTemplate = SecurityService.getInstance( ).getAccessDeniedTemplate( );
230                 HtmlTemplate tAccessDenied = AppTemplateService.getTemplate( strAccessDeniedTemplate );
231                 page = new XPage( );
232                 page.setContent( tAccessDenied.getHtml( ) );
233             }
234         }
235         else
236         {
237             XPageApplication application = getXPageSessionInstance( request, entry );
238             page = application.getPage( request, nMode, entry.getPlugin( ) );
239         }
240 
241         if ( page.isStandalone( ) || page.isSendRedirect() )
242         {
243             return page.getContent( );
244         }
245 
246         PageData/service/content/PageData.html#PageData">PageData data = new PageData( );
247 
248         data.setContent( page.getContent( ) );
249         data.setName( page.getTitle( ) );
250 
251         // set the page path. Done by adding the extra-path information to the
252         // pathLabel.
253         String strXml = page.getXmlExtendedPathLabel( );
254 
255         if ( strXml == null )
256         {
257             data.setPagePath( PortalService.getXPagePathContent( page.getPathLabel( ), 0, request ) );
258         }
259         else
260         {
261             data.setPagePath( PortalService.getXPagePathContent( page.getPathLabel( ), 0, strXml, request ) );
262         }
263 
264         return PortalService.buildPageContent( data, nMode, request );
265     }
266 
267     /**
268      * Gets Application entry by name
269      * 
270      * @param strName
271      *            The application's name
272      * @return The entry
273      */
274     public static XPageApplicationEntry getApplicationEntry( String strName )
275     {
276         return _mapApplications.get( strName );
277     }
278 
279     /**
280      * Gets applications list
281      * 
282      * @return A collection of applications
283      */
284     public static Collection<XPageApplicationEntry> getXPageApplicationsList( )
285     {
286         return _mapApplications.values( );
287     }
288 
289     /**
290      * Return an instance of the XPage attached to the current Http Session
291      * 
292      * @param request
293      *            The HTTP request
294      * @param entry
295      *            The XPage entry
296      * @return The XPage instance
297      */
298     private static XPageApplication getXPageSessionInstance( HttpServletRequest request, XPageApplicationEntry entry )
299     {
300         HttpSession session = request.getSession( true );
301         String strAttribute = ATTRIBUTE_XPAGE + entry.getId( );
302         XPageApplication../fr/paris/lutece/portal/web/xpages/XPageApplication.html#XPageApplication">XPageApplication application = (XPageApplication) session.getAttribute( strAttribute );
303 
304         if ( application == null )
305         {
306             application = getApplicationInstance( entry );
307             session.setAttribute( strAttribute, application );
308             AppLogService.debug( "New XPage instance of {} created and attached to session {}", entry.getClassName( ), session );
309         }
310 
311         return application;
312     }
313 
314     /**
315      * Get an XPage instance
316      * 
317      * @param entry
318      *            The Xpage entry
319      * @return An instance of a given XPage
320      */
321     public static XPageApplication getApplicationInstance( XPageApplicationEntry entry )
322     {
323         XPageApplication application = null;
324 
325         try
326         {
327             if ( entry.getClassName( ) == null )
328             {
329                 application = SpringContextService.getBean( entry.getPluginName( ) + ".xpage." + entry.getId( ) );
330             }
331             else
332             {
333                 application = (XPageApplication) Class.forName( entry.getClassName( ) ).newInstance( );
334             }
335         }
336         catch( Exception e )
337         {
338             throw new AppException( ERROR_INSTANTIATION + entry.getId( ) + " - " + e.getCause( ), e );
339         }
340 
341         return application;
342     }
343 }