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
35
36
37 package fr.paris.lutece.plugins.participatorybudget.service;
38
39 import java.io.FileInputStream;
40 import java.util.ArrayList;
41 import java.util.Enumeration;
42 import java.util.HashMap;
43 import java.util.Hashtable;
44 import java.util.List;
45 import java.util.Map;
46 import java.util.Properties;
47 import java.util.concurrent.ConcurrentHashMap;
48 import java.util.concurrent.ConcurrentMap;
49
50 import javax.servlet.http.HttpServletRequest;
51 import javax.xml.transform.Source;
52 import javax.xml.transform.stream.StreamSource;
53
54 import org.apache.commons.lang.StringUtils;
55
56 import fr.paris.lutece.plugins.document.business.Document;
57 import fr.paris.lutece.plugins.document.business.DocumentHome;
58 import fr.paris.lutece.plugins.document.business.DocumentType;
59 import fr.paris.lutece.plugins.document.business.DocumentTypeHome;
60 import fr.paris.lutece.plugins.document.business.portlet.DocumentListPortletHome;
61 import fr.paris.lutece.plugins.document.business.portlet.DocumentPortletHome;
62 import fr.paris.lutece.plugins.document.business.publication.DocumentPublication;
63 import fr.paris.lutece.plugins.document.service.DocumentEvent;
64 import fr.paris.lutece.plugins.document.service.DocumentEventListener;
65 import fr.paris.lutece.plugins.document.service.DocumentException;
66 import fr.paris.lutece.plugins.document.service.publishing.PublishingService;
67 import fr.paris.lutece.plugins.document.utils.IntegerUtils;
68 import fr.paris.lutece.portal.business.page.Page;
69 import fr.paris.lutece.portal.business.page.PageHome;
70 import fr.paris.lutece.portal.business.portlet.AliasPortlet;
71 import fr.paris.lutece.portal.business.portlet.AliasPortletHome;
72 import fr.paris.lutece.portal.business.portlet.Portlet;
73 import fr.paris.lutece.portal.business.portlet.PortletHome;
74 import fr.paris.lutece.portal.business.resourceenhancer.ResourceEnhancer;
75 import fr.paris.lutece.portal.business.style.ModeHome;
76 import fr.paris.lutece.portal.service.cache.AbstractCacheableService;
77 import fr.paris.lutece.portal.service.cache.ICacheKeyService;
78 import fr.paris.lutece.portal.service.content.PageData;
79 import fr.paris.lutece.portal.service.html.XmlTransformerService;
80 import fr.paris.lutece.portal.service.message.SiteMessageException;
81 import fr.paris.lutece.portal.service.page.PortletCacheService;
82 import fr.paris.lutece.portal.service.portal.PortalService;
83 import fr.paris.lutece.portal.service.security.LuteceUser;
84 import fr.paris.lutece.portal.service.security.SecurityService;
85 import fr.paris.lutece.portal.service.security.UserNotSignedException;
86 import fr.paris.lutece.portal.service.spring.SpringContextService;
87 import fr.paris.lutece.portal.service.template.AppTemplateService;
88 import fr.paris.lutece.portal.service.util.AppLogService;
89 import fr.paris.lutece.portal.service.util.AppPathService;
90 import fr.paris.lutece.portal.service.util.AppPropertiesService;
91 import fr.paris.lutece.portal.web.PortalJspBean;
92 import fr.paris.lutece.util.ReferenceList;
93 import fr.paris.lutece.util.date.DateUtil;
94 import fr.paris.lutece.util.html.HtmlTemplate;
95 import fr.paris.lutece.util.xml.XmlUtil;
96 import net.sf.ehcache.CacheException;
97 import net.sf.ehcache.Ehcache;
98 import net.sf.ehcache.Element;
99 import net.sf.ehcache.event.CacheEventListener;
100
101
102
103
104 public final class DocumentBodyService extends AbstractCacheableService implements IDocumentBodyService, CacheEventListener, DocumentEventListener
105 {
106
107
108 public static final String BEAN_NAME = "participatorybudget.DocumentBodyService";
109 private static final String CACHE_NAME = "DocumentBodyService Cache";
110 private static final String SLASH = "/";
111 private static final int MODE_ADMIN = 1;
112 private static final String CONSTANT_DEFAULT_PORTLET_DOCUMENT_LIST_XSL = "WEB-INF/xsl/normal/portlet_document_list.xsl";
113 private static final String DOCUMENT_STYLE_PREFIX_ID = "document-";
114 private static final String LOCALE_EN = "en";
115 private static final String LOCALE_FR = "fr";
116
117
118 private static final String XML_TAG_CONTENT = "content";
119 private static final String XML_TAG_SITE_LOCALE = "site_locale";
120
121
122 private static final String PARAMETER_SITE_PATH = "site-path";
123 private static final String PARAMETER_PUBLICATION_DATE = "publication-date";
124 private static final String PARAMETER_SITE_LOCALE = "site_locale";
125
126
127 private static final String MARK_PUBLICATION = "publication";
128 private static final String MARK_DOCUMENT = "document";
129 private static final String MARK_PORTLET = "portlet";
130 private static final String MARK_CATEGORY = "categories";
131 private static final String MARK_DOCUMENT_ID = "document_id";
132 private static final String MARK_PORTLET_ID = "portlet_id";
133 private static final String MARK_PORTLET_ID_LIST = "portlet_id_list";
134 private static final String MARK_DOCUMENT_CATEGORIES_LIST = "document_categories_list";
135 private static final String MARK_URL_LOGIN = "url_login";
136 private static final String MARKER_TARGET = "target";
137 private static final String MARK_IS_EXTEND_INSTALLED = "isExtendInstalled";
138
139
140 private static final String TEMPLATE_DOCUMENT_PAGE_DEFAULT = "/skin/plugins/document/document_content_service.html";
141 private static final String TEMPLATE_DOCUMENT_CATEGORIES = "/skin/plugins/document/document_categories.html";
142
143
144 private static final String PROPERTY_DEFAULT_PORTLET_DOCUMENT_LIST_XSL = "document.contentService.defaultPortletDocumentListXSL";
145 private static final String PROPERTY_CACHE_ENABLED = "documentBody.cache.enabled";
146 private static final String TARGET_TOP = "target=_top";
147 private static final String PROPERTY_RESOURCE_TYPE = "document";
148
149
150 private static ConcurrentMap<String, String> _keyMemory = new ConcurrentHashMap<String, String>( );
151
152
153
154 private static final String PARAMETER_PORTLET = "portlet";
155 private static final String PORTLET_CACHE_KEY_SUFFIX = "[documentBodyService]";
156
157 private boolean _bInit;
158
159 private PortletCacheService _cachePortlets;
160 private ICacheKeyService _cksPortlet;
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176 public String getPage( HttpServletRequest request, String strDocumentId, String strPortletId, int nMode )
177 throws UserNotSignedException, SiteMessageException
178 {
179 if ( !_bInit )
180 {
181 init( );
182 }
183
184 String strSiteLocale = request.getParameter( PARAMETER_SITE_LOCALE );
185
186 if ( ( strSiteLocale == null ) || !strSiteLocale.equalsIgnoreCase( LOCALE_EN ) )
187 {
188 strSiteLocale = LOCALE_FR;
189 }
190
191 String strKey = getKey( strDocumentId, strPortletId, strSiteLocale, nMode );
192 String strPage = (String) getFromCache( strKey );
193
194 if ( strPage == null )
195 {
196
197 synchronized( strKey )
198 {
199
200 strPage = (String) getFromCache( strKey );
201
202
203
204 if ( strPage == null )
205 {
206 AppLogService.debug( " -- Page generation " + strKey + " : doc=" + strDocumentId + " portletid=" + strPortletId + "site_locale="
207 + strSiteLocale + "nMode=" + nMode );
208 strPage = buildPage( request, strDocumentId, strPortletId, strSiteLocale, nMode );
209
210 if ( IntegerUtils.isNumeric( strDocumentId ) )
211 {
212 int nDocumentId = IntegerUtils.convert( strDocumentId );
213 Document document = DocumentHome.findByPrimaryKeyWithoutBinaries( nDocumentId );
214
215 if ( ( document != null ) )
216 {
217 putInCache( strKey, strPage );
218 }
219 }
220 }
221 else
222 {
223 AppLogService.debug( "Page read from cache after synchronisation " + strKey );
224 }
225 }
226 }
227 else
228 {
229 AppLogService.debug( "Page read from cache " + strKey );
230 }
231
232 return strPage;
233 }
234
235
236
237
238 private void init( )
239 {
240
241
242 String strCache = AppPropertiesService.getProperty( PROPERTY_CACHE_ENABLED, "true" );
243
244 if ( strCache.equalsIgnoreCase( "true" ) )
245 {
246 initCache( getName( ) );
247 }
248
249
250 _cachePortlets = SpringContextService.getBean( "portletCacheService" );
251
252 _cksPortlet = SpringContextService.getBean( "portletCacheKeyService" );
253
254 _bInit = true;
255 }
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 private String buildPage( HttpServletRequest request, String strDocumentId, String strPortletId, String strSiteLocale, int nMode )
275 throws UserNotSignedException, SiteMessageException
276 {
277 int nPortletId;
278 int nDocumentId;
279 boolean bPortletExist = false;
280 Map<String, String> mapXslParams = new HashMap<String, String>( );
281
282 try
283 {
284 nPortletId = Integer.parseInt( strPortletId );
285 }
286 catch( NumberFormatException nfe )
287 {
288 AppLogService.error( "Error parsing strPortletId '" + strPortletId + "' ", nfe );
289 return "";
290 }
291
292 try
293 {
294 nDocumentId = Integer.parseInt( strDocumentId );
295 }
296 catch( NumberFormatException nfe )
297 {
298 AppLogService.error( "Error parsing strDocumentId '" + strDocumentId + "' ", nfe );
299 return "";
300 }
301
302 Document document = DocumentHome.findByPrimaryKeyWithoutBinaries( nDocumentId );
303
304 if ( ( document == null ) || ( !document.isValid( ) ) )
305 {
306 AppLogService.error( "participatorybudget, VotesSolrAddon, doc is not valid or null: " + document + ", id=" + nDocumentId );
307 return "";
308 }
309
310 DocumentType type = DocumentTypeHome.findByPrimaryKey( document.getCodeDocumentType( ) );
311 DocumentPublication documentPublication = PublishingService.getInstance( ).getDocumentPublication( nPortletId, nDocumentId );
312
313 Map<String, Object> model = new HashMap<String, Object>( );
314
315 if ( documentPublication != null )
316 {
317
318 boolean bIsAlias = DocumentListPortletHome.checkIsAliasPortlet( documentPublication.getPortletId( ) );
319
320 if ( bIsAlias && ( documentPublication.getPortletId( ) != nPortletId ) )
321 {
322 AliasPortlet alias = (AliasPortlet) AliasPortletHome.findByPrimaryKey( nPortletId );
323 nPortletId = alias.getAliasId( );
324 strPortletId = Integer.toString( nPortletId );
325 }
326
327 if ( ( documentPublication.getPortletId( ) == nPortletId ) && ( documentPublication.getStatus( ) == DocumentPublication.STATUS_PUBLISHED ) )
328 {
329 bPortletExist = true;
330 }
331
332
333 mapXslParams.put( PARAMETER_PUBLICATION_DATE, DateUtil.getDateString( documentPublication.getDatePublishing( ), request.getLocale( ) ) );
334 model.put( MARK_PUBLICATION, documentPublication );
335 }
336
337 if ( bPortletExist )
338 {
339
340 PageData data = new PageData( );
341 data.setName( document.getTitle( ) );
342 data.setPagePath( PortalService.getXPagePathContent( document.getTitle( ), 0, request ) );
343
344 Portlet portlet = PortletHome.findByPrimaryKey( nPortletId );
345 Page page = PageHome.getPage( portlet.getPageId( ) );
346 String strRole = page.getRole( );
347
348 if ( !strRole.equals( Page.ROLE_NONE ) && SecurityService.isAuthenticationEnable( ) )
349 {
350 LuteceUser user = SecurityService.getInstance( ).getRegisteredUser( request );
351
352 if ( ( user == null ) && ( !SecurityService.getInstance( ).isExternalAuthentication( ) ) )
353 {
354
355 String strAccessControledTemplate = SecurityService.getInstance( ).getAccessControledTemplate( );
356 HashMap<String, Object> modelAccessControledTemplate = new HashMap<String, Object>( );
357 String strLoginUrl = SecurityService.getInstance( ).getLoginPageUrl( );
358 modelAccessControledTemplate.put( MARK_URL_LOGIN, strLoginUrl );
359
360 HtmlTemplate tAccessControled = AppTemplateService.getTemplate( strAccessControledTemplate, request.getLocale( ),
361 modelAccessControledTemplate );
362 data.setContent( tAccessControled.getHtml( ) );
363
364 return data.getContent( );
365 }
366
367 if ( !SecurityService.getInstance( ).isUserInRole( request, strRole ) )
368 {
369
370 String strAccessDeniedTemplate = SecurityService.getInstance( ).getAccessDeniedTemplate( );
371 HtmlTemplate tAccessDenied = AppTemplateService.getTemplate( strAccessDeniedTemplate, request.getLocale( ) );
372 data.setContent( tAccessDenied.getHtml( ) );
373
374 return data.getContent( );
375 }
376 }
377
378
379 Enumeration<?> enumParam = request.getParameterNames( );
380 Hashtable<String, String> htParamRequest = new Hashtable<String, String>( );
381 String paramName = "";
382
383 while ( enumParam.hasMoreElements( ) )
384 {
385 paramName = (String) enumParam.nextElement( );
386 htParamRequest.put( paramName, request.getParameter( paramName ) );
387 }
388
389 XmlTransformerService xmlTransformerService = new XmlTransformerService( );
390 StringBuffer strXml = new StringBuffer( );
391 XmlUtil.beginElement( strXml, XML_TAG_CONTENT );
392 XmlUtil.addElement( strXml, XML_TAG_SITE_LOCALE, strSiteLocale );
393 strXml.append( document.getXmlValidatedContent( ) );
394 XmlUtil.endElement( strXml, XML_TAG_CONTENT );
395
396 String strDocument = xmlTransformerService.transformBySourceWithXslCache( strXml.toString( ), type.getContentServiceXslSource( ),
397 DOCUMENT_STYLE_PREFIX_ID + type.getStyleSheetId( nMode ), htParamRequest, null );
398
399 model.put( MARK_DOCUMENT, strDocument );
400 model.put( MARK_PORTLET, getPortlet( request, strPortletId, nMode ) );
401 model.put( MARK_CATEGORY, getRelatedDocumentsPortlet( request, document, nPortletId, nMode ) );
402 model.put( MARK_DOCUMENT_ID, strDocumentId );
403 model.put( MARK_PORTLET_ID, strPortletId );
404 model.put( MARK_IS_EXTEND_INSTALLED, PortalService.isExtendActivated( ) );
405
406
407 ResourceEnhancer.buildPageAddOn( model, PROPERTY_RESOURCE_TYPE, nDocumentId, strPortletId, request );
408
409 HtmlTemplate template = AppTemplateService.getTemplate( getTemplatePage( document ), request.getLocale( ), model );
410
411 data.setContent( template.getHtml( ) );
412
413 return data.getContent( );
414 }
415 return "";
416 }
417
418
419
420
421
422
423
424 private String getTemplatePage( Document document )
425 {
426 if ( document.getPageTemplateDocumentId( ) != 0 )
427 {
428 String strPageTemplateDocument = DocumentHome.getPageTemplateDocumentPath( document.getPageTemplateDocumentId( ) );
429
430 return strPageTemplateDocument;
431 }
432 else
433 {
434 return TEMPLATE_DOCUMENT_PAGE_DEFAULT;
435 }
436 }
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453 private String getPortlet( HttpServletRequest request, String strPortletId, int nMode ) throws SiteMessageException
454 {
455 try
456 {
457 int nPortletId = Integer.parseInt( strPortletId );
458
459 Portlet portlet = PortletHome.findByPrimaryKey( nPortletId );
460
461
462
463
464
465 String strFilePath = AppPropertiesService.getProperty( PROPERTY_DEFAULT_PORTLET_DOCUMENT_LIST_XSL, CONSTANT_DEFAULT_PORTLET_DOCUMENT_LIST_XSL );
466
467 if ( strFilePath == null )
468 {
469 return StringUtils.EMPTY;
470 }
471
472 if ( !strFilePath.startsWith( SLASH ) )
473 {
474 strFilePath = SLASH + strFilePath;
475 }
476
477 String strFileName = strFilePath.substring( strFilePath.lastIndexOf( SLASH ) + 1 );
478 strFilePath = strFilePath.substring( 0, strFilePath.lastIndexOf( SLASH ) + 1 );
479
480 FileInputStream fis = AppPathService.getResourceAsStream( strFilePath, strFileName );
481 Source xslSource = new StreamSource( fis );
482
483
484 Enumeration<?> enumParam = request.getParameterNames( );
485 Hashtable<String, String> htParamRequest = new Hashtable<String, String>( );
486 String paramName = "";
487
488 while ( enumParam.hasMoreElements( ) )
489 {
490 paramName = (String) enumParam.nextElement( );
491 htParamRequest.put( paramName, request.getParameter( paramName ) );
492 }
493
494 Properties outputProperties = ModeHome.getOuputXslProperties( nMode );
495
496
497 if ( nMode != MODE_ADMIN )
498 {
499 htParamRequest.put( PARAMETER_SITE_PATH, AppPathService.getPortalUrl( ) );
500 }
501 else
502 {
503 htParamRequest.put( PARAMETER_SITE_PATH, AppPathService.getAdminPortalUrl( ) );
504 htParamRequest.put( MARKER_TARGET, TARGET_TOP );
505 }
506
507 if ( _cachePortlets.isCacheEnable( ) )
508 {
509 LuteceUser user = null;
510
511 if ( SecurityService.isAuthenticationEnable( ) )
512 {
513 user = SecurityService.getInstance( ).getRegisteredUser( request );
514 }
515
516 boolean bCanBeCached = ( user != null ) ? ( portlet.canBeCachedForConnectedUsers( ) ) : ( portlet.canBeCachedForAnonymousUsers( ) );
517
518 if ( bCanBeCached )
519 {
520
521 htParamRequest.put( PARAMETER_PORTLET, String.valueOf( portlet.getId( ) ) );
522
523
524 String strKey = getKey( _cksPortlet.getKey( htParamRequest, nMode, user ) + PORTLET_CACHE_KEY_SUFFIX );
525
526
527 String strPortlet = (String) _cachePortlets.getFromCache( strKey );
528
529 if ( strPortlet == null )
530 {
531
532 synchronized( strKey )
533 {
534
535
536 strPortlet = (String) _cachePortlets.getFromCache( strKey );
537
538
539
540
541 if ( strPortlet == null )
542 {
543 String strXml = portlet.getXmlDocument( request );
544
545 XmlTransformerService xmlTransformerService = new XmlTransformerService( );
546 String strXslUniquePrefix = DOCUMENT_STYLE_PREFIX_ID + strFilePath + strFileName;
547
548 strPortlet = xmlTransformerService.transformBySourceWithXslCache( strXml, xslSource, strXslUniquePrefix, htParamRequest,
549 outputProperties );
550
551 _cachePortlets.putInCache( strKey, strPortlet );
552 }
553 }
554 }
555
556 return strPortlet;
557 }
558 }
559
560 String strXml = portlet.getXmlDocument( request );
561
562 XmlTransformerService xmlTransformerService = new XmlTransformerService( );
563 String strXslUniquePrefix = DOCUMENT_STYLE_PREFIX_ID + strFilePath + strFileName;
564
565 return xmlTransformerService.transformBySourceWithXslCache( strXml, xslSource, strXslUniquePrefix, htParamRequest, outputProperties );
566 }
567 catch( NumberFormatException e )
568 {
569 return null;
570 }
571 }
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586 private String getRelatedDocumentsPortlet( HttpServletRequest request, Document document, int nPortletId, int nMode )
587 {
588 if ( ( nMode != MODE_ADMIN ) && ( document.getCategories( ) != null ) && ( document.getCategories( ).size( ) > 0 ) )
589 {
590 HashMap<String, Object> model = new HashMap<String, Object>( );
591 List<Document> listRelatedDocument = DocumentHome.findByRelatedCategories( document, request.getLocale( ) );
592
593 List<Document> listDocument = new ArrayList<Document>( );
594 ReferenceList listDocumentPortlet = new ReferenceList( );
595
596
597 for ( Document relatedDocument : listRelatedDocument )
598 {
599
600 for ( Portlet portlet : PublishingService.getInstance( ).getPortletsByDocumentId( Integer.toString( relatedDocument.getId( ) ) ) )
601 {
602
603 if ( ( PublishingService.getInstance( ).isPublished( relatedDocument.getId( ), portlet.getId( ) ) )
604 && ( portlet.getStatus( ) == Portlet.STATUS_PUBLISHED ) && ( relatedDocument.isValid( ) )
605 && ( relatedDocument.getId( ) != document.getId( ) ) )
606 {
607 listDocumentPortlet.addItem( Integer.toString( relatedDocument.getId( ) ), Integer.toString( portlet.getId( ) ) );
608 listDocument.add( relatedDocument );
609
610 break;
611 }
612 }
613 }
614
615 model.put( MARK_DOCUMENT_CATEGORIES_LIST, listDocument );
616 model.put( MARK_PORTLET_ID_LIST, listDocumentPortlet );
617
618 HtmlTemplate template = AppTemplateService.getTemplate( TEMPLATE_DOCUMENT_CATEGORIES, request.getLocale( ), model );
619
620 return template.getHtml( );
621 }
622 else
623 {
624 return StringUtils.EMPTY;
625 }
626 }
627
628
629
630
631 public void dispose( )
632 {
633 }
634
635
636
637
638 public void notifyElementEvicted( Ehcache cache, Element element )
639 {
640 _keyMemory.remove( element.getKey( ) );
641 }
642
643
644
645
646 public void notifyElementExpired( Ehcache cache, Element element )
647 {
648 _keyMemory.remove( element.getKey( ) );
649 }
650
651
652
653
654 public void notifyElementPut( Ehcache cache, Element element ) throws CacheException
655 {
656 }
657
658
659
660
661 public void notifyElementRemoved( Ehcache cache, Element element ) throws CacheException
662 {
663 _keyMemory.remove( element.getKey( ) );
664 }
665
666
667
668
669 public void notifyElementUpdated( Ehcache cache, Element element ) throws CacheException
670 {
671 }
672
673
674
675
676 public void notifyRemoveAll( Ehcache cache )
677 {
678 _keyMemory.clear( );
679 }
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695 private String getKey( String strDocumentId, String strPortletId, String strSiteLocale, int nMode )
696 {
697 String key = "D" + strDocumentId + "P" + strPortletId + "L" + strSiteLocale + "M" + nMode;
698 String keyInMemory = _keyMemory.putIfAbsent( key, key );
699
700 if ( keyInMemory != null )
701 {
702 return keyInMemory;
703 }
704
705 return key;
706 }
707
708
709
710
711
712
713
714
715 private String getKey( String strPortletKey )
716 {
717 String keyInMemory = _keyMemory.putIfAbsent( strPortletKey, strPortletKey );
718
719 if ( keyInMemory != null )
720 {
721 return keyInMemory;
722 }
723
724 return strPortletKey;
725 }
726
727
728
729
730
731
732
733
734
735 public void removeFromCache( String strDocumentId, String strPortletId )
736 {
737 if ( getCache( ) != null )
738 {
739 String strKey = getKey( strDocumentId, strPortletId, LOCALE_FR, PortalJspBean.MODE_HTML );
740
741 getCache( ).remove( strKey );
742
743 strKey = getKey( strDocumentId, strPortletId, LOCALE_EN, PortalJspBean.MODE_HTML );
744
745 getCache( ).remove( strKey );
746 }
747 }
748
749 @Override
750 public String getName( )
751 {
752 return CACHE_NAME;
753 }
754
755 @Override
756 public void processDocumentEvent( DocumentEvent event ) throws DocumentException
757 {
758 for ( int nIdPortlet : DocumentPortletHome.findPortletForDocument( event.getDocument( ).getId( ) ) )
759 {
760 removeFromCache( Integer.toString( event.getDocument( ).getId( ) ), Integer.toString( nIdPortlet ) );
761 }
762 }
763 }