View Javadoc
1   /*
2    * Copyright (c) 2002-2021, 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.extend.service.content;
35  
36  import fr.paris.lutece.plugins.extend.business.extender.ResourceExtenderDTO;
37  import fr.paris.lutece.plugins.extend.service.converter.IStringMapper;
38  import fr.paris.lutece.plugins.extend.service.extender.IResourceExtenderService;
39  import fr.paris.lutece.portal.business.page.Page;
40  import fr.paris.lutece.portal.service.content.ContentPostProcessor;
41  import fr.paris.lutece.portal.service.template.AppTemplateService;
42  import fr.paris.lutece.portal.service.util.AppLogService;
43  import fr.paris.lutece.portal.service.util.AppPathService;
44  import fr.paris.lutece.portal.service.util.AppPropertiesService;
45  import fr.paris.lutece.util.html.HtmlTemplate;
46  
47  import org.apache.commons.lang3.StringUtils;
48  
49  import org.springframework.beans.factory.InitializingBean;
50  
51  import org.springframework.util.Assert;
52  
53  import java.util.HashMap;
54  import java.util.Map;
55  import java.util.regex.Matcher;
56  import java.util.regex.Pattern;
57  
58  import javax.inject.Inject;
59  
60  import javax.servlet.http.HttpServletRequest;
61  
62  /**
63   *
64   * This content post processor replace all macro of type @Extender[idResource,resourceType,extenderType,parameters]@ to the associated extender content.
65   *
66   */
67  public class ExtendableContentPostProcessor implements ContentPostProcessor, InitializingBean
68  {
69      /**
70       * Name of this bean
71       */
72      public static final String BEAN_NAME = "extend.extendableContentPostProcessor";
73      private static final String NAME = "Extend content processor";
74      private static final String END_BODY = "</body>";
75      private static final String EXTEND_PARAMETERED_ID = "ExtendParameteredId";
76  
77      // PROPERTIES
78      private static final String PROPERTY_CLIENT_SIDE = "extend.contentPostProcessor.clientSide";
79  
80      // MARKS
81      private static final String MARK_REGEX_PATTERN = "extendRegexPattern";
82      private static final String MARK_BASE_URL = "baseUrl";
83      private static final String PARAM_PAGE = "page";
84      private static final String PARAM_PORTLET_ID = "portlet_id";
85  
86      // TEMPLATES
87      private static final String TEMPLATE_CONTENT_POST_PROCESSOR = "skin/plugins/extend/extendable_content_post_processor.html";
88      @Inject
89      private IResourceExtenderService _extenderService;
90      @Inject
91      private IStringMapper<ResourceExtenderDTO> _mapper;
92      private String _strRegexPattern;
93      private Pattern _regexPattern;
94      private String _strExtenderParameterRegexPattern;
95      private Pattern _extendedParameterRegexPattern;
96  
97      /**
98       * Sets the regex pattern.
99       *
100      * @param strRegexPattern
101      *            the new regex pattern
102      */
103     public void setRegexPattern( String strRegexPattern )
104     {
105         _strRegexPattern = strRegexPattern;
106         if ( _regexPattern == null || !_regexPattern.pattern( ).equals( strRegexPattern ) )
107         {
108             Pattern pattern = Pattern.compile( strRegexPattern );
109             _regexPattern = pattern;
110         }
111     }
112 
113     /**
114      * Sets the regex pattern.
115      *
116      * @param strExtenderParameterRegexPattern
117      *            the new regex pattern
118      */
119     public void setExtenderParameterRegexPattern( String strExtenderParameterRegexPattern )
120     {
121         _strExtenderParameterRegexPattern = strExtenderParameterRegexPattern;
122         if ( _extendedParameterRegexPattern == null || !_extendedParameterRegexPattern.pattern( ).equals( strExtenderParameterRegexPattern ) )
123         {
124             Pattern pattern = Pattern.compile( strExtenderParameterRegexPattern );
125             _extendedParameterRegexPattern = pattern;
126         }
127     }
128 
129     /**
130      * {@inheritDoc}
131      */
132     @Override
133     public String getName( )
134     {
135         return NAME;
136     }
137 
138     /**
139      * {@inheritDoc}
140      */
141     @Override
142     public String process( HttpServletRequest request, String strContent )
143     {
144         String strHtmlContent = strContent;
145 
146         // Check if the process is carried out in client or server side
147         boolean bClientSide = Boolean.valueOf( AppPropertiesService.getProperty( PROPERTY_CLIENT_SIDE, "false" ) );
148 
149         if ( bClientSide )
150         {
151             // CLIENT SIDE
152             int nPos = strHtmlContent.indexOf( END_BODY );
153 
154             if ( nPos < 0 )
155             {
156                 AppLogService.error( "ExtendableContentPostProcessor Service : no BODY end tag found" );
157 
158                 return strHtmlContent;
159             }
160 
161             Map<String, Object> model = new HashMap<String, Object>( );
162             model.put( MARK_BASE_URL, AppPathService.getBaseUrl( request ) );
163             model.put( MARK_REGEX_PATTERN, _strRegexPattern );
164 
165             HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_CONTENT_POST_PROCESSOR, request.getLocale( ), model );
166 
167             StringBuilder sb = new StringBuilder( );
168             sb.append( strHtmlContent.substring( 0, nPos ) );
169             sb.append( template.getHtml( ) );
170             sb.append( strHtmlContent.substring( nPos ) );
171             strHtmlContent = sb.toString( );
172         }
173         else
174         {
175             // SERVER SIDE
176 
177             /**
178              * Replace all makers @Extender[<idResource>,<resourceType>,<extenderType>,<params>]@ to the correct HTML content of the extender. 1) First parse
179              * the content of the markers 2) Get all information (idResource, resourceType, extenderType, params) 3) Get the html content from the given
180              * information 4) Replace the markers by the html content
181              */
182 
183             // 1) First parse the content of the markers
184             Matcher match = _regexPattern.matcher( strHtmlContent );
185             Matcher parameterMatch = null;
186             StringBuffer strResultHTML = new StringBuffer( strHtmlContent.length( ) );
187             while ( match.find( ) )
188             {
189                 String strMarker = match.group( );
190 
191                 // 2) Get all information (idResource, resourceType, extenderType, params)
192                 ResourceExtenderDTO resourceExtender = _mapper.map( match.group( 1 ) );
193                 boolean bParameteredId = StringUtils.equalsIgnoreCase( resourceExtender.getIdExtendableResource( ), EXTEND_PARAMETERED_ID );
194 
195                 if ( bParameteredId )
196                 {
197                     if ( parameterMatch == null )
198                     {
199                         parameterMatch = _extendedParameterRegexPattern.matcher( strHtmlContent );
200                     }
201                     else
202                     {
203                         parameterMatch.reset( );
204                     }
205 
206                     while ( parameterMatch.find( ) )
207                     {
208                         ResourceExtenderDTO realResourceExtender = _mapper.map( parameterMatch.group( 1 ) );
209 
210                         if ( StringUtils.equals( realResourceExtender.getExtendableResourceType( ), resourceExtender.getExtendableResourceType( ) )
211                                 && StringUtils.equals( realResourceExtender.getExtenderType( ), resourceExtender.getExtenderType( ) ) )
212                         {
213                             resourceExtender.setIdExtendableResource( realResourceExtender.getIdExtendableResource( ) );
214 
215                             break;
216                         }
217                     }
218                 }
219 
220                 String strHtml = StringUtils.EMPTY;
221 
222                 if ( !bParameteredId || !StringUtils.equalsIgnoreCase( resourceExtender.getIdExtendableResource( ), EXTEND_PARAMETERED_ID ) )
223                 {
224                     // 3) Get the html content from the given information
225                     if ( !StringUtils.equals( resourceExtender.getExtendableResourceType( ), Page.RESOURCE_TYPE )
226                             || ( StringUtils.isBlank( request.getParameter( PARAM_PAGE ) )
227                                     && StringUtils.isBlank( request.getParameter( PARAM_PORTLET_ID ) ) ) )
228                     {
229                         strHtml = _extenderService.getContent( resourceExtender.getIdExtendableResource( ), resourceExtender.getExtendableResourceType( ),
230                                 resourceExtender.getExtenderType( ), resourceExtender.getParameters( ), request );
231                     }
232                 }
233 
234                 // 4) Replace the markers by the html content
235                 match.appendReplacement( strResultHTML, Matcher.quoteReplacement( strHtml ) );
236             }
237             match.appendTail( strResultHTML );
238             strHtmlContent = strResultHTML.toString( );
239         }
240 
241         if ( StringUtils.isNotBlank( _strExtenderParameterRegexPattern ) )
242         {
243             strHtmlContent = _extendedParameterRegexPattern.matcher( strHtmlContent ).replaceAll( "" );
244         }
245 
246         return strHtmlContent;
247     }
248 
249     /**
250      * {@inheritDoc}
251      */
252     @Override
253     public void afterPropertiesSet( ) throws Exception
254     {
255         Assert.notNull( _strRegexPattern, "The property 'regexPattern' is required." );
256     }
257 }