1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package fr.paris.lutece.portal.web.includes;
35
36 import java.io.IOException;
37 import java.io.InputStream;
38 import java.net.URI;
39 import java.net.URISyntaxException;
40 import java.util.ArrayList;
41 import java.util.Collection;
42 import java.util.HashMap;
43 import java.util.List;
44 import java.util.Locale;
45 import java.util.Map;
46 import java.util.stream.Collectors;
47
48 import javax.servlet.ServletContext;
49 import javax.servlet.http.HttpServletRequest;
50
51 import fr.paris.lutece.portal.business.style.Theme;
52 import fr.paris.lutece.portal.service.content.PageData;
53 import fr.paris.lutece.portal.service.includes.PageInclude;
54 import fr.paris.lutece.portal.service.plugin.Plugin;
55 import fr.paris.lutece.portal.service.plugin.PluginService;
56 import fr.paris.lutece.portal.service.portal.PortalService;
57 import fr.paris.lutece.portal.service.spring.SpringContextService;
58 import fr.paris.lutece.portal.service.template.AppTemplateService;
59 import fr.paris.lutece.portal.service.util.AppLogService;
60 import fr.paris.lutece.portal.service.util.CryptoService;
61 import fr.paris.lutece.portal.web.xpages.XPageApplicationEntry;
62 import fr.paris.lutece.util.html.HtmlTemplate;
63
64
65
66
67 public class LinksInclude implements PageInclude
68 {
69 private static final String ALGORITHM = "SHA-256";
70
71
72 private static final String PARAMETER_PAGE = "page";
73
74
75 private static final String MARK_FAVOURITE = "favourite";
76 private static final String MARK_PORTAL_NAME = "lutece_name";
77 private static final String MARK_PLUGIN_THEME_CSS = "plugin_theme";
78 private static final String MARK_PLUGINS_CSS_LINKS = "plugins_css_links";
79 private static final String MARK_PLUGINS_JAVASCRIPT_LINKS = "plugins_javascript_links";
80 private static final String MARK_PLUGIN_CSS_STYLESHEET = "plugin_css_stylesheet";
81 private static final String MARK_PLUGIN_JAVASCRIPT_FILE = "plugin_javascript_file";
82
83
84 private static final String TEMPLATE_PLUGIN_CSS_LINK = "skin/site/plugin_css_link.html";
85 private static final String TEMPLATE_PLUGIN_JAVASCRIPT_LINK = "skin/site/plugin_javascript_link.html";
86 private static final String PREFIX_PLUGINS_CSS = "css/plugins/";
87 private static final String PREFIX_PLUGINS_JAVASCRIPT = "js/plugins/";
88
89
90
91
92
93
94
95
96
97
98
99
100
101 public void fillTemplate( Map<String, Object> rootModel, PageData data, int nMode, HttpServletRequest request )
102 {
103 if ( request == null )
104 {
105 return;
106 }
107
108 String strFavourite = ( data.getFavourite( ) != null ) ? data.getFavourite( ) : PortalService.getSiteName( );
109 String strPortalName = PortalService.getSiteName( );
110 rootModel.put( MARK_FAVOURITE, strFavourite );
111 rootModel.put( MARK_PORTAL_NAME, strPortalName );
112
113
114 Collection<Plugin> listPlugins = PluginService.getPluginList( );
115 listPlugins.add( PluginService.getCore( ) );
116
117 String strPage = request.getParameter( PARAMETER_PAGE );
118
119 List<Plugin> installedPlugins = listPlugins.stream( ).filter( Plugin::isInstalled ).collect( Collectors.toList( ) );
120
121 for ( Plugin plugin : installedPlugins )
122 {
123 Theme xpageTheme = plugin.getXPageTheme( request );
124
125 if ( ( strPage != null ) && ( xpageTheme != null ) )
126 {
127 for ( XPageApplicationEntry entry : plugin.getApplications( ) )
128 {
129 if ( strPage.equals( entry.getId( ) ) )
130 {
131 rootModel.put( MARK_PLUGIN_THEME_CSS, xpageTheme );
132 }
133 }
134 }
135 }
136
137 Map<String, Object> links = buildLinks( request, strPage, nMode, installedPlugins );
138 rootModel.putAll( links );
139 }
140
141 private Map<String, Object> buildLinks( HttpServletRequest request, String strPage, int nMode, List<Plugin> installedPlugins )
142 {
143 Locale locale = request.getLocale( );
144 LinksIncludeCacheService cacheService = SpringContextService.getBean( LinksIncludeCacheService.SERVICE_NAME );
145 String strKey = cacheService.getCacheKey( nMode, strPage, locale );
146 @SuppressWarnings( "unchecked" )
147 Map<String, Object> links = (Map<String, Object>) cacheService.getFromCache( strKey );
148
149 if ( links != null )
150 {
151 return links;
152 }
153 StringBuilder sbCssLinks = new StringBuilder( );
154 StringBuilder sbJsLinks = new StringBuilder( );
155
156 for ( Plugin plugin : installedPlugins )
157 {
158 boolean bXPage = isPluginXPage( strPage, plugin );
159
160 if ( plugin.isCssStylesheetsScopePortal( ) || ( bXPage && plugin.isCssStylesheetsScopeXPage( ) ) )
161 {
162 List<String> cssFiles = new ArrayList<>( );
163 cssFiles.addAll( plugin.getCssStyleSheets( ) );
164 cssFiles.addAll( plugin.getCssStyleSheets( nMode ) );
165
166 cssFiles.stream( ).forEach( file -> appendStyleSheet( request.getServletContext( ), sbCssLinks, file, locale ) );
167 }
168
169 if ( plugin.isJavascriptFilesScopePortal( ) || ( bXPage && plugin.isJavascriptFilesScopeXPage( ) ) )
170 {
171 List<String> jsFiles = new ArrayList<>( );
172 jsFiles.addAll( plugin.getJavascriptFiles( ) );
173 jsFiles.addAll( plugin.getJavascriptFiles( nMode ) );
174
175 jsFiles.stream( ).forEach( file -> appendJavascriptFile( request.getServletContext( ), sbJsLinks, file, locale ) );
176 }
177 }
178
179 links = new HashMap<>( 2 );
180 links.put( MARK_PLUGINS_CSS_LINKS, sbCssLinks.toString( ) );
181 links.put( MARK_PLUGINS_JAVASCRIPT_LINKS, sbJsLinks.toString( ) );
182 cacheService.putInCache( strKey, links );
183 return links;
184 }
185
186
187
188
189
190
191
192
193
194
195
196
197
198 private void appendJavascriptFile( ServletContext servletContext, StringBuilder sbJsLinks, String strJavascriptFile, Locale locale )
199 {
200 URI javascripFileURI = getURI( servletContext, strJavascriptFile, PREFIX_PLUGINS_JAVASCRIPT );
201
202 if ( javascripFileURI == null )
203 {
204 return;
205 }
206
207 Map<String, String> model = new HashMap<>( 1 );
208 model.put( MARK_PLUGIN_JAVASCRIPT_FILE, javascripFileURI.toString( ) );
209
210 HtmlTemplate tJs = AppTemplateService.getTemplate( TEMPLATE_PLUGIN_JAVASCRIPT_LINK, locale, model );
211 sbJsLinks.append( tJs.getHtml( ) );
212 }
213
214
215
216
217
218
219
220
221
222
223
224
225
226 private void appendStyleSheet( ServletContext servletContext, StringBuilder sbCssLinks, String strCssStyleSheet, Locale locale )
227 {
228 URI styleSheetURI = getURI( servletContext, strCssStyleSheet, PREFIX_PLUGINS_CSS );
229
230 if ( styleSheetURI == null )
231 {
232 return;
233 }
234
235 Map<String, String> model = new HashMap<>( 2 );
236 model.put( MARK_PLUGIN_CSS_STYLESHEET, styleSheetURI.toString( ) );
237
238 HtmlTemplate tCss = AppTemplateService.getTemplate( TEMPLATE_PLUGIN_CSS_LINK, locale, model );
239 sbCssLinks.append( tCss.getHtml( ) );
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253
254 private URI getURI( ServletContext servletContext, String strResourceURI, String strURIPrefix )
255 {
256 try
257 {
258 URI resourceURI = new URI( strResourceURI );
259 if ( !resourceURI.isAbsolute( ) && resourceURI.getHost( ) == null )
260 {
261 if ( strURIPrefix != null )
262 {
263 resourceURI = new URI( strURIPrefix + strResourceURI );
264 }
265 resourceURI = addHashToUri( servletContext, resourceURI, strResourceURI );
266 }
267 return resourceURI;
268 }
269 catch( URISyntaxException e )
270 {
271 AppLogService.error( "Invalid cssStyleSheetURI : {}", strResourceURI, e );
272 return null;
273 }
274 }
275
276 private URI addHashToUri( ServletContext servletContext, URI resourceURI, String strResourceURI ) throws URISyntaxException
277 {
278 try ( InputStream inputStream = servletContext.getResourceAsStream( resourceURI.getPath( ) ) )
279 {
280 if ( inputStream != null )
281 {
282 String hash = CryptoService.digest( inputStream, ALGORITHM );
283 if ( hash != null )
284 {
285 char separator = '?';
286 if ( resourceURI.getQuery( ) != null )
287 {
288 separator = '&';
289 }
290 resourceURI = new URI( resourceURI.toString( ) + separator + "lutece_h=" + hash );
291 }
292 }
293 }
294 catch( IOException e )
295 {
296 AppLogService.error( "Error while closing stream for {}", strResourceURI, e );
297 }
298 return resourceURI;
299 }
300
301
302
303
304
305
306
307
308
309
310 private boolean isPluginXPage( String strPage, Plugin plugin )
311 {
312 if ( ( strPage != null ) )
313 {
314 for ( XPageApplicationEntry app : plugin.getApplications( ) )
315 {
316 if ( strPage.equals( app.getId( ) ) )
317 {
318 return true;
319 }
320 }
321 }
322
323 return false;
324 }
325 }