001/* 002 * Copyright 2010 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.web.repository; 017 018import java.io.IOException; 019import java.net.MalformedURLException; 020import java.util.HashMap; 021import java.util.Map; 022import java.util.Map.Entry; 023import java.util.Set; 024 025import org.apache.avalon.framework.service.ServiceException; 026import org.apache.avalon.framework.service.ServiceManager; 027import org.apache.cocoon.ProcessingException; 028import org.apache.cocoon.components.source.SourceUtil; 029import org.apache.cocoon.environment.ObjectModelHelper; 030import org.apache.cocoon.environment.Request; 031import org.apache.cocoon.generation.ServiceableGenerator; 032import org.apache.cocoon.xml.AttributesImpl; 033import org.apache.cocoon.xml.SaxBuffer; 034import org.apache.cocoon.xml.XMLUtils; 035import org.apache.commons.lang.StringUtils; 036import org.apache.commons.lang.exception.ExceptionUtils; 037import org.apache.excalibur.source.Source; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040import org.xml.sax.ContentHandler; 041import org.xml.sax.SAXException; 042 043import org.ametys.cms.content.GetContentAction; 044import org.ametys.cms.contenttype.ContentTypeDescriptor; 045import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 046import org.ametys.cms.contenttype.ContentTypesHelper; 047import org.ametys.cms.contenttype.DynamicContentTypeDescriptorExtentionPoint; 048import org.ametys.cms.repository.Content; 049import org.ametys.cms.tag.Tag; 050import org.ametys.cms.tag.TagProviderExtensionPoint; 051import org.ametys.core.ui.ClientSideElement.ScriptFile; 052import org.ametys.core.util.IgnoreRootHandler; 053import org.ametys.plugins.repository.AmetysObjectIterable; 054import org.ametys.plugins.repository.AmetysRepositoryException; 055import org.ametys.plugins.repository.provider.WorkspaceSelector; 056import org.ametys.runtime.authentication.AccessDeniedException; 057import org.ametys.runtime.authentication.AuthorizationRequiredException; 058import org.ametys.web.WebConstants; 059import org.ametys.web.cache.monitoring.Constants; 060import org.ametys.web.cache.monitoring.process.access.ResourceAccessComponent; 061import org.ametys.web.cache.monitoring.process.access.impl.PageElementResourceAccess; 062import org.ametys.web.cache.monitoring.process.access.impl.PageElementResourceAccess.PageElementType; 063import org.ametys.web.cache.monitoring.process.access.impl.PageResourceAccess; 064import org.ametys.web.cache.pageelement.PageElementCache; 065import org.ametys.web.renderingcontext.RenderingContext; 066import org.ametys.web.renderingcontext.RenderingContextHandler; 067import org.ametys.web.repository.content.SharedContent; 068import org.ametys.web.repository.page.ContentTypesAssignmentHandler; 069import org.ametys.web.repository.page.ModifiablePage; 070import org.ametys.web.repository.page.MoveablePage; 071import org.ametys.web.repository.page.Page; 072import org.ametys.web.repository.page.Page.PageType; 073import org.ametys.web.repository.page.PagesContainer; 074import org.ametys.web.repository.page.ServicesAssignmentHandler; 075import org.ametys.web.repository.page.Zone; 076import org.ametys.web.repository.page.ZoneItem; 077import org.ametys.web.repository.page.ZoneItem.ZoneType; 078import org.ametys.web.repository.site.Site; 079import org.ametys.web.service.Service; 080import org.ametys.web.service.ServiceExtensionPoint; 081import org.ametys.web.skin.Skin; 082import org.ametys.web.skin.SkinTemplate; 083import org.ametys.web.skin.SkinTemplateZone; 084import org.ametys.web.skin.SkinsManager; 085 086/** 087 * Generator for SAXing <code>Content</code> associated with a Page.<br> 088 * SAX events are like :<br> 089 * <pageContents><br> 090 * <zone id="..."><br> 091 * <i><!-- XHTML content --></i><br> 092 * </zone><br> 093 * ...<br> 094 * </pageContents/><br> 095 */ 096public class PageGenerator extends ServiceableGenerator 097{ 098 private ServiceExtensionPoint _serviceExtPt; 099 private ContentTypeExtensionPoint _contentTypeExtPt; 100 private SkinsManager _skinsManager; 101 private TagProviderExtensionPoint _tagProviderEP; 102 private PageElementCache _zoneItemCache; 103 private WorkspaceSelector _workspaceSelector; 104 private RenderingContextHandler _renderingContextHandler; 105 private ContentTypesHelper _contentTypeHelper; 106 private DynamicContentTypeDescriptorExtentionPoint _dynamicCTDescriptorEP; 107 108 /** The content type assignment handler. */ 109 private ContentTypesAssignmentHandler _cTypeAssignmentHandler; 110 111 /** The service assignment handler. */ 112 private ServicesAssignmentHandler _serviceAssignmentHandler; 113 114 /** The resource access monitoring component */ 115 private ResourceAccessComponent _resourceAccessMonitor; 116 117 /** The monitored resource access */ 118 private PageResourceAccess _pageAccess; 119 120 private int _zoneItemsInCache; 121 private int _zoneItemsSaxed; 122 private int _zonesSaxed; 123 124 private Logger _timeLogger = LoggerFactory.getLogger("org.ametys.web.rendering.time"); 125 126 @Override 127 public void service(ServiceManager serviceManager) throws ServiceException 128 { 129 super.service(serviceManager); 130 _serviceExtPt = (ServiceExtensionPoint) serviceManager.lookup(ServiceExtensionPoint.ROLE); 131 _contentTypeExtPt = (ContentTypeExtensionPoint) serviceManager.lookup(ContentTypeExtensionPoint.ROLE); 132 _skinsManager = (SkinsManager) serviceManager.lookup(SkinsManager.ROLE); 133 _tagProviderEP = (TagProviderExtensionPoint) serviceManager.lookup(TagProviderExtensionPoint.ROLE); 134 _zoneItemCache = (PageElementCache) serviceManager.lookup(PageElementCache.ROLE + "/zoneItem"); 135 _workspaceSelector = (WorkspaceSelector) serviceManager.lookup(WorkspaceSelector.ROLE); 136 _renderingContextHandler = (RenderingContextHandler) serviceManager.lookup(RenderingContextHandler.ROLE); 137 _cTypeAssignmentHandler = (ContentTypesAssignmentHandler) serviceManager.lookup(ContentTypesAssignmentHandler.ROLE); 138 _serviceAssignmentHandler = (ServicesAssignmentHandler) serviceManager.lookup(ServicesAssignmentHandler.ROLE); 139 _resourceAccessMonitor = (ResourceAccessComponent) serviceManager.lookup(ResourceAccessComponent.ROLE); 140 _contentTypeHelper = (ContentTypesHelper) serviceManager.lookup(ContentTypesHelper.ROLE); 141 _dynamicCTDescriptorEP = (DynamicContentTypeDescriptorExtentionPoint) serviceManager.lookup(DynamicContentTypeDescriptorExtentionPoint.ROLE); 142 } 143 144 @Override 145 public void generate() throws IOException, SAXException, ProcessingException 146 { 147 long t0 = System.currentTimeMillis(); 148 149 _zoneItemsInCache = 0; 150 _zoneItemsSaxed = 0; 151 _zonesSaxed = 0; 152 153 Request request = ObjectModelHelper.getRequest(objectModel); 154 155 Page page = (Page) request.getAttribute(WebConstants.REQUEST_ATTR_PAGE); 156 String title = page.getTitle(); 157 158 RenderingContext renderingContext = _renderingContextHandler.getRenderingContext(); 159 String workspace = _workspaceSelector.getWorkspace(); 160 String siteName = page.getSiteName(); 161 162 if (page.getType() != PageType.CONTAINER) 163 { 164 throw new IllegalStateException("Cannot invoke the PageGenerator on a Page without a PageContent"); 165 } 166 167 // Monitor the access to this page. 168 _pageAccess = (PageResourceAccess) request.getAttribute(Constants.REQUEST_ATTRIBUTE_PAGEACCESS); 169 170 if (_pageAccess != null) 171 { 172 _pageAccess.setRenderingContext(renderingContext); 173 _pageAccess.setWorkspaceJCR(workspace); 174 _resourceAccessMonitor.addAccessRecord(_pageAccess); 175 } 176 177 contentHandler.startDocument(); 178 AttributesImpl attrs = new AttributesImpl(); 179 attrs.addCDATAAttribute("title", title); 180 attrs.addCDATAAttribute("long-title", page.getLongTitle()); 181 attrs.addCDATAAttribute("id", page.getId()); 182 XMLUtils.startElement(contentHandler, "page", attrs); 183 184 try 185 { 186 XMLUtils.startElement(contentHandler, "metadata"); 187 page.toSAX(contentHandler); 188 XMLUtils.endElement(contentHandler, "metadata"); 189 190 // Tags 191 XMLUtils.startElement(contentHandler, "tags"); 192 Set<String> tags = page.getTags(); 193 for (String tagName : tags) 194 { 195 Map<String, Object> contextParameters = new HashMap<>(); 196 contextParameters.put("siteName", siteName); 197 198 Tag tag = _tagProviderEP.getTag(tagName, contextParameters); 199 200 if (tag != null) 201 { 202 // tag may be null if it has been registered on the page and then removed from the application 203 AttributesImpl tagattrs = new AttributesImpl(); 204 if (tag.getParentName() != null) 205 { 206 tagattrs.addCDATAAttribute("parent", tag.getParentName()); 207 } 208 209 XMLUtils.startElement(contentHandler, tagName, tagattrs); 210 tag.getTitle().toSAX(contentHandler); 211 XMLUtils.endElement(contentHandler, tagName); 212 } 213 } 214 XMLUtils.endElement(contentHandler, "tags"); 215 } 216 catch (AmetysRepositoryException e) 217 { 218 _pageAccess = null; 219 throw new ProcessingException("Unable to SAX page metadata", e); 220 } 221 222 long t1 = System.currentTimeMillis(); 223 224 AttributesImpl pcattrs = new AttributesImpl(); 225 pcattrs.addCDATAAttribute("modifiable", Boolean.toString(page instanceof ModifiablePage)); 226 pcattrs.addCDATAAttribute("moveable", Boolean.toString(page instanceof MoveablePage)); 227 XMLUtils.startElement(contentHandler, "pageContents", pcattrs); 228 229 try 230 { 231 // Iterate on existing zones 232 for (Zone zone : page.getZones()) 233 { 234 String zoneName = zone.getName(); 235 AmetysObjectIterable<? extends ZoneItem> zoneItems = zone.getZoneItems(); 236 237 _saxZone(page, zoneName, zoneItems, workspace, siteName, renderingContext); 238 } 239 240 // Iterate on defined zone (that are not existing) 241 SkinTemplate skinTemplate = _getTemplateDefinition(page); 242 if (skinTemplate != null) 243 { 244 for (SkinTemplateZone zoneDef : skinTemplate.getZones().values()) 245 { 246 if (!page.hasZone(zoneDef.getId())) 247 { 248 _saxZone(page, zoneDef.getId(), null, workspace, siteName, renderingContext); 249 } 250 } 251 } 252 } 253 catch (AmetysRepositoryException ex) 254 { 255 _pageAccess = null; 256 throw new ProcessingException("Unable to get Content", ex); 257 } 258 259 long t2 = System.currentTimeMillis(); 260 _timeLogger.debug("Zones processing time: {} ms", t2 - t1); 261 if (getLogger().isInfoEnabled()) 262 { 263 getLogger().info("PageGenerator\t/" + page.getSiteName() + "/" + page.getSitemapName() + "/" + page.getPathInSitemap() + "\t" + page.getId() + "\tprocessing time (in ms):\t" + (t2 - t0) + "\tRendering context:\t" + renderingContext + "\tSaxing (zones, total zoneItems, zoneItems from cache):\t" + _zonesSaxed + "\t" + _zoneItemsSaxed + "\t" + _zoneItemsInCache); 264 } 265 266 XMLUtils.endElement(contentHandler, "pageContents"); 267 XMLUtils.endElement(contentHandler, "page"); 268 contentHandler.endDocument(); 269 270 _timeLogger.debug("Page processing time: {} ms", t2 - t0); 271 272 _pageAccess = null; 273 } 274 275 /** 276 * Sax a zone 277 * @param page The page 278 * @param zoneName The zone in the page to sax 279 * @param zoneItems The items of the zone or null 280 * @param workspace the workspace 281 * @param site the site's name 282 * @param renderingContext the rendering context 283 * @throws SAXException if an error occurs while saxing 284 * @throws IOException if an I/O exception occurs 285 * @throws ProcessingException if an error occurs 286 */ 287 private void _saxZone(Page page, String zoneName, AmetysObjectIterable<? extends ZoneItem> zoneItems, String workspace, String site, RenderingContext renderingContext) throws SAXException, IOException, ProcessingException 288 { 289 _zonesSaxed++; 290 291 AmetysObjectIterable<? extends ZoneItem> localZoneItems = zoneItems; 292 293 AttributesImpl zoneAttrs = new AttributesImpl(); 294 zoneAttrs.addCDATAAttribute("name", zoneName); 295 296 if (localZoneItems == null || !localZoneItems.iterator().hasNext()) 297 { 298 // zone is empty => try to inherit 299 Zone parentPageZone = _inherit(page, page, zoneName); 300 if (parentPageZone != null) 301 { 302 zoneAttrs.addCDATAAttribute("inherited", parentPageZone.getPage().getId()); 303 304 localZoneItems = parentPageZone.getZoneItems(); 305 } 306 } 307 308 Request request = ObjectModelHelper.getRequest(objectModel); 309 request.setAttribute(Zone.class.getName(), zoneName); 310 311 XMLUtils.startElement(contentHandler, "zone", zoneAttrs); 312 313 _saxAvailableContentTypes(page, zoneName); 314 _saxAvailableServices(page, zoneName); 315 316 _saxZoneItems(page, localZoneItems, workspace, site, renderingContext); 317 318 XMLUtils.endElement(contentHandler, "zone"); 319 request.setAttribute(Zone.class.getName(), null); 320 321 } 322 323 /** 324 * Generate the list of available services for the given zone. 325 * @param page the page. 326 * @param zoneName the zone name in the page. 327 * @throws SAXException if something goes wrong when saxing the available services 328 */ 329 private void _saxAvailableServices(Page page, String zoneName) throws SAXException 330 { 331 Set<String> services = _serviceAssignmentHandler.getAvailableServices(page, zoneName); 332 333 XMLUtils.startElement(contentHandler, "available-services"); 334 335 for (String service : services) 336 { 337 AttributesImpl attrs = new AttributesImpl(); 338 attrs.addCDATAAttribute("id", service); 339 XMLUtils.createElement(contentHandler, "service", attrs); 340 } 341 342 XMLUtils.endElement(contentHandler, "available-services"); 343 } 344 345 /** 346 * Generate the list of available content types for the given zone. 347 * @param page the page. 348 * @param zoneName the zone name in the page. 349 * @throws SAXException if something goes wrong when saxing the available content types 350 */ 351 private void _saxAvailableContentTypes(Page page, String zoneName) throws SAXException 352 { 353 Set<String> cTypes = _cTypeAssignmentHandler.getAvailableContentTypes(page, zoneName, true); 354 355 XMLUtils.startElement(contentHandler, "available-content-types"); 356 357 for (String cType : cTypes) 358 { 359 AttributesImpl attrs = new AttributesImpl(); 360 attrs.addCDATAAttribute("id", cType); 361 XMLUtils.createElement(contentHandler, "content-type", attrs); 362 } 363 364 XMLUtils.endElement(contentHandler, "available-content-types"); 365 } 366 367 /** 368 * Sax zone items 369 * @param page the page 370 * @param zoneItems The zone items to sax 371 * @param workspace the workspace 372 * @param site the site's name 373 * @param renderingContext the rendering context 374 * @throws SAXException if an error occurs while saxing 375 * @throws IOException if an I/O exception occurs 376 * @throws ProcessingException if an error occurs 377 */ 378 private void _saxZoneItems(Page page, AmetysObjectIterable< ? extends ZoneItem> zoneItems, String workspace, String site, RenderingContext renderingContext) throws SAXException, IOException, ProcessingException 379 { 380 if (zoneItems == null) 381 { 382 return; 383 } 384 385 Request request = ObjectModelHelper.getRequest(objectModel); 386 387 for (ZoneItem zoneItem : zoneItems) 388 { 389 _saxZoneItem(page, workspace, site, renderingContext, request, zoneItem); 390 } 391 } 392 393 private void _handleZoneAccess(PageElementResourceAccess zoneAccess, boolean cacheable, boolean hit) 394 { 395 if (zoneAccess != null) 396 { 397 zoneAccess.setCacheable(cacheable); 398 zoneAccess.setCacheHit(hit); 399 } 400 } 401 402 private void _saxZoneItem(Page page, String workspace, String site, RenderingContext renderingContext, Request request, ZoneItem zoneItem) throws SAXException, ProcessingException, IOException 403 { 404 long t0 = System.currentTimeMillis(); 405 406 _zoneItemsSaxed++; 407 408 String id = zoneItem.getId(); 409 ZoneType type = zoneItem.getType(); 410 411 request.setAttribute(WebConstants.REQUEST_ATTR_ZONEITEM, zoneItem); 412 413 AttributesImpl zoneItemAttrs = new AttributesImpl(); 414 zoneItemAttrs.addCDATAAttribute("id", id); 415 416 PageElementResourceAccess zoneAccess = _pageAccess != null ? _pageAccess.createPageElementAccess(id, PageElementType.fromZoneItemType(type)) : null; 417 418 // try to get content from cache 419 SaxBuffer cachedContent = _zoneItemCache.getPageElement(workspace, site, _getType(zoneItem), id, page.getId(), renderingContext); 420 421 if (cachedContent != null) 422 { 423 _handleZoneAccess(zoneAccess, true, true); 424 425 _zoneItemsInCache++; 426 427 request.setAttribute("IsZoneItemCacheable", true); 428 429 cachedContent.toSAX(contentHandler); 430 } 431 else 432 { 433 Exception ex = null; 434 435 boolean isCacheable = false; 436 if (type == ZoneType.CONTENT) 437 { 438 // a Content is always cacheable 439 isCacheable = true; 440 } 441 else if (type == ZoneType.SERVICE) 442 { 443 String serviceId = zoneItem.getServiceId(); 444 Service service = _serviceExtPt.getExtension(serviceId); 445 446 if (service != null) 447 { 448 try 449 { 450 isCacheable = service.isCacheable(page, zoneItem); 451 } 452 catch (Exception e) 453 { 454 ex = new ProcessingException("Error testing the service cachability.", e); 455 } 456 } 457 // if service is null, an exception will be thrown later 458 } 459 460 _handleZoneAccess(zoneAccess, isCacheable, false); 461 462 request.setAttribute("IsZoneItemCacheable", isCacheable); 463 464 SaxBuffer buffer = null; 465 ContentHandler handler; // the actual ContentHandler, either the real one, or a buffer 466 if (isCacheable) 467 { 468 buffer = new SaxBuffer(); 469 handler = buffer; 470 } 471 else 472 { 473 handler = contentHandler; 474 } 475 476 XMLUtils.startElement(handler, "zoneItem", zoneItemAttrs); 477 478 XMLUtils.startElement(handler, "information"); 479 XMLUtils.createElement(handler, "type", type.toString()); 480 481 Object result = null; 482 if (type == ZoneType.CONTENT) 483 { 484 if (getLogger().isDebugEnabled()) 485 { 486 Content content = zoneItem.getContent(); 487 getLogger().debug("Processing content " + content.getId() + " / " + content.getPath()); 488 } 489 490 result = _saxContentZoneItem(zoneItem, handler, request); 491 } 492 else if (type == ZoneType.SERVICE) 493 { 494 if (getLogger().isDebugEnabled()) 495 { 496 getLogger().debug("Processing service " + zoneItem.getServiceId()); 497 } 498 499 result = _saxServiceZoneItem(zoneItem, handler, ex); 500 } 501 502 Source src = null; 503 if (result instanceof Source) 504 { 505 src = (Source) result; 506 } 507 else 508 { 509 ex = (Exception) result; 510 } 511 512 XMLUtils.endElement(handler, "information"); 513 514 _saxSource(handler, src, ex); 515 516 XMLUtils.endElement(handler, "zoneItem"); 517 518 // finally store the buffered data in the cache and SAX it to the pipeline 519 if (buffer != null) 520 { 521 _zoneItemCache.storePageElement(workspace, site, _getType(zoneItem), id, page.getId(), renderingContext, buffer); 522 buffer.toSAX(contentHandler); 523 } 524 } 525 526 // Monitor the access to this zone item. 527 _resourceAccessMonitor.addAccessRecord(zoneAccess); 528 529 // Empty content request attributes 530 request.setAttribute(Content.class.getName(), null); 531 // Empty zone item request attribute 532 request.setAttribute(WebConstants.REQUEST_ATTR_ZONEITEM, null); 533 // Empty zone item cacheable attribute 534 request.setAttribute("IsZoneItemCacheable", null); 535 536 _timeLogger.debug("Zone item {} processing time: {} ms", id, System.currentTimeMillis() - t0); 537 } 538 539 private Object _saxContentZoneItem(ZoneItem zoneItem, ContentHandler handler, Request request) throws SAXException, MalformedURLException, IOException 540 { 541 try 542 { 543 Content content = zoneItem.getContent(); 544 String metadataSetName = StringUtils.defaultString(zoneItem.getMetadataSetName(), "main"); 545 546 XMLUtils.createElement(handler, "contentId", content.getId()); 547 XMLUtils.createElement(handler, "contentName", content.getName()); 548 XMLUtils.createElement(handler, "metadataSetName", metadataSetName); 549 if (content instanceof SharedContent) 550 { 551 XMLUtils.createElement(handler, "sharedContent", "true"); 552 } 553 554 String contentTypeId = _contentTypeHelper.getContentTypeIdForRendering(content); 555 556// ContentType contentType = _contentTypeExtPt.getExtension(contentTypeId); 557// if (contentType == null) 558// { 559// return new IllegalStateException("The content type '" + contentTypeId + "' is referenced but does not exist"); 560// } 561 562 ContentTypeDescriptor contentType = _contentTypeExtPt.getExtension(contentTypeId); 563 if (contentType == null) 564 { 565 contentType = _dynamicCTDescriptorEP.getExtension(contentTypeId); 566 } 567 568 if (contentType == null) 569 { 570 return new IllegalStateException("The content type '" + contentTypeId + "' is referenced but does not exist"); 571 } 572 else 573 { 574 AttributesImpl contentTypeAttrs = new AttributesImpl(); 575 contentTypeAttrs.addCDATAAttribute("id", contentType.getId()); 576 XMLUtils.startElement(handler, "type-information", contentTypeAttrs); 577 578 contentType.getLabel().toSAX(handler, "label"); 579 contentType.getDescription().toSAX(handler, "description"); 580 581 if (contentType.getIconGlyph() != null) 582 { 583 XMLUtils.createElement(handler, "iconGlyph", contentType.getIconGlyph()); 584 } 585 if (contentType.getIconDecorator() != null) 586 { 587 XMLUtils.createElement(handler, "iconDecorator", contentType.getIconDecorator()); 588 } 589 590 if (contentType.getSmallIcon() != null) 591 { 592 XMLUtils.createElement(handler, "smallIcon", contentType.getSmallIcon()); 593 XMLUtils.createElement(handler, "mediumIcon", contentType.getMediumIcon()); 594 XMLUtils.createElement(handler, "largeIcon", contentType.getLargeIcon()); 595 } 596 597 XMLUtils.startElement(handler, "css"); 598 for (ScriptFile cssFile : contentType.getCSSFiles()) 599 { 600 _saxCSSFile(handler, cssFile); 601 } 602 XMLUtils.endElement(handler, "css"); 603 604 XMLUtils.endElement(handler, "type-information"); 605 606 String url = "cocoon://_plugins/" + contentType.getPluginName() + "/" + contentType.getId() + ".html"; 607 if (StringUtils.isNotEmpty(metadataSetName)) 608 { 609 url = url + "?metadataSetName=" + metadataSetName; 610 } 611 612 // FIXME use a context 613 request.setAttribute(Content.class.getName(), content); 614 request.setAttribute(GetContentAction.RESULT_CONTENTTYPE, contentTypeId); 615 return resolver.resolveURI(url); 616 } 617 } 618 catch (AmetysRepositoryException e) 619 { 620 return new ProcessingException("Unable to get content property", e); 621 } 622 } 623 624 private Object _saxServiceZoneItem(ZoneItem zoneItem, ContentHandler handler, Exception ex) throws SAXException, MalformedURLException, IOException 625 { 626 String serviceId = zoneItem.getServiceId(); 627 Service service = _serviceExtPt.getExtension(serviceId); 628 629 if (service == null) 630 { 631 return new ProcessingException("Unable to get service for name '" + serviceId + "'"); 632 } 633 else if (ex == null) // If an exception was caught while testing the service cacheability, do not generate 634 { 635 AttributesImpl serviceAttrs = new AttributesImpl(); 636 serviceAttrs.addCDATAAttribute("id", service.getId()); 637 XMLUtils.startElement(handler, "type-information", serviceAttrs); 638 639 service.getLabel().toSAX(handler, "label"); 640 service.getDescription().toSAX(handler, "description"); 641 642 if (service.getIconGlyph() != null) 643 { 644 XMLUtils.createElement(handler, "iconGlyph", service.getIconGlyph()); 645 } 646 if (service.getIconDecorator() != null) 647 { 648 XMLUtils.createElement(handler, "iconDecorator", service.getIconDecorator()); 649 } 650 if (service.getSmallIcon() != null) 651 { 652 XMLUtils.createElement(handler, "smallIcon", service.getSmallIcon()); 653 XMLUtils.createElement(handler, "mediumIcon", service.getMediumIcon()); 654 } 655 656 XMLUtils.startElement(handler, "css"); 657 for (ScriptFile cssFile : service.getCSSFiles()) 658 { 659 _saxCSSFile(handler, cssFile); 660 } 661 XMLUtils.endElement(handler, "css"); 662 663 XMLUtils.endElement(handler, "type-information"); 664 665 return resolver.resolveURI(service.getURL(), null, PageGeneratorHelper.getParameters(service, zoneItem)); 666 } 667 else 668 { 669 return ex; 670 } 671 } 672 673 private void _saxCSSFile(ContentHandler handler, ScriptFile cssFile) throws SAXException 674 { 675 AttributesImpl fileAttrs = new AttributesImpl(); 676 if (!cssFile.isLangSpecific()) 677 { 678 String rtlMode = cssFile.getRtlMode(); 679 if (rtlMode != null && !"all".equals(rtlMode)) 680 { 681 fileAttrs.addCDATAAttribute("rtl", rtlMode); 682 } 683 684 XMLUtils.createElement(handler, "file", fileAttrs, cssFile.getPath()); 685 } 686 else 687 { 688 fileAttrs.addCDATAAttribute("lang", "true"); 689 XMLUtils.startElement(handler, "file", fileAttrs); 690 691 String defaultLang = cssFile.getDefaultLang(); 692 Map<String, String> langPaths = cssFile.getLangPaths(); 693 694 for (Entry<String, String> langPath : langPaths.entrySet()) 695 { 696 AttributesImpl langAttrs = new AttributesImpl(); 697 698 String codeLang = langPath.getKey(); 699 langAttrs.addCDATAAttribute("code", codeLang); 700 if (codeLang.equals(defaultLang)) 701 { 702 langAttrs.addCDATAAttribute("default", "true"); 703 } 704 705 XMLUtils.createElement(handler, "lang", langAttrs, langPath.getValue()); 706 } 707 708 XMLUtils.endElement(handler, "file"); 709 } 710 } 711 712 private void _saxSource(ContentHandler handler, Source src, Exception ex) throws SAXException, IOException, ProcessingException 713 { 714 if (src == null) 715 { 716 getLogger().error("Unable to display zone item", ex); 717 _saxError(handler, ex); 718 } 719 else 720 { 721 try 722 { 723 SourceUtil.toSAX(src, new IgnoreRootHandler(handler)); 724 } 725 catch (ProcessingException e) 726 { 727 getLogger().error("Unable to display zone item", e); 728 729 if (_throwException(e)) 730 { 731 throw e; 732 } 733 else 734 { 735 _saxError(handler, e.getCause()); 736 } 737 } 738 finally 739 { 740 resolver.release(src); 741 } 742 } 743 } 744 745 private String _getType(ZoneItem zoneItem) 746 { 747 ZoneType type = zoneItem.getType(); 748 749 if (type == ZoneType.CONTENT) 750 { 751 return "CONTENT"; 752 } 753 else 754 { 755 return "SERVICE:" + zoneItem.getServiceId(); 756 } 757 } 758 759 /** 760 * Get the template definition for a page 761 * @param page The page. Cannot be null. 762 * @return The template definition. Null if the page is not a container or if the template is not declared. 763 */ 764 private SkinTemplate _getTemplateDefinition(Page page) 765 { 766 if (page.getType() != PageType.CONTAINER) 767 { 768 return null; 769 } 770 771 Site site = page.getSite(); 772 String skinId = site.getSkinId(); 773 String templateName = page.getTemplate(); 774 try 775 { 776 Skin skinDef = _skinsManager.getSkin(skinId); 777 return skinDef.getTemplate(templateName); 778 } 779 catch (IllegalStateException e) 780 { 781 getLogger().error("Cannot get template definition for page '" + page.getId() + "' using template '" + templateName + "' in skin '" + skinId + "'"); 782 return null; 783 } 784 } 785 786 /** 787 * Try to inherit the zone (as it is empty) 788 * @param childPage The child page that do inherit. Cannot be null 789 * @param page The page to inherit. Cannot be null 790 * @param zoneName The zone name in the page to inherit. Cannot be null or empty 791 * @return The zone inherited or null. 792 */ 793 private Zone _inherit(Page childPage, Page page, String zoneName) 794 { 795 // The page has an existing zone at this place ? 796 if (page.hasZone(zoneName)) 797 { 798 Zone zone = page.getZone(zoneName); 799 AmetysObjectIterable<? extends ZoneItem> zoneItems = zone.getZoneItems(); 800 801 // With data ? 802 if (zoneItems.iterator().hasNext()) 803 { 804 // This is it (end of the recursion) 805 return zone; 806 } 807 } 808 809 // Get the definition for the zone 810 SkinTemplateZone zoneDef; 811 812 Site site = page.getSite(); 813 String skinId = site.getSkinId(); 814 String templateName = page.getTemplate(); 815 try 816 { 817 SkinTemplate templateDef = _getTemplateDefinition(page); 818 zoneDef = templateDef.getZone(zoneName); 819 } 820 catch (IllegalStateException e) 821 { 822 getLogger().error("The page '" + childPage.getId() + "' cannot inherit a zone '" + zoneName + "' of template '" + templateName + "' in skin '" + skinId + "' as asked for page '" + page.getId() + "' in site '" + site.getName() + "'", e); 823 return null; 824 } 825 826 // This zone is not defined for the template 827 if (zoneDef == null) 828 { 829 getLogger().warn("The page '" + childPage.getId() + "' cannot inherit the undefined zone '" + zoneName + "' of template '" + templateName + "' in skin '" + skinId + "' as asked for page '" + page.getId() + "' in site '" + site.getName() + "'."); 830 return null; 831 } 832 833 // Support inheritance ? 834 if (!zoneDef.hasInheritance()) 835 { 836 return null; 837 } 838 839 // Get the parent page (that is a container page) 840 Page parentPage = page; 841 do 842 { 843 PagesContainer pc = parentPage.getParent(); 844 if (!(pc instanceof Page)) 845 { 846 // inheritance goes back to the root 847 return null; 848 } 849 850 parentPage = (Page) pc; 851 } 852 while (parentPage.getType() != PageType.CONTAINER); 853 854 // Get the name of the zone which will be the inheritance source 855 String parentPageTemplate = parentPage.getTemplate(); 856 String inheritanceSrc = zoneDef.getInheritance(parentPageTemplate); 857 if (inheritanceSrc == null) 858 { 859 // No inheritance for this template 860 return null; 861 } 862 863 // Finally we will inherit from the parentPage and the zone inheritanceSrc 864 return _inherit(childPage, parentPage, inheritanceSrc); 865 } 866 867 /** 868 * Test if the error has to be thrown instead of SAXing it as an error zone item. 869 * @param ex the exception. 870 * @return true to throw the exception, false to catch it and SAX it as an error zone item. 871 */ 872 private boolean _throwException(Exception ex) 873 { 874 boolean isFront = _renderingContextHandler.getRenderingContext() == RenderingContext.FRONT; 875 boolean isAuthorizationRequired = ExceptionUtils.indexOfThrowable(ex, AuthorizationRequiredException.class) > -1; 876 boolean isAccessDenied = ExceptionUtils.indexOfThrowable(ex, AccessDeniedException.class) > -1; 877 878 return isFront && (isAuthorizationRequired || isAccessDenied); 879 } 880 881 private void _saxError (ContentHandler handler, Throwable e) throws SAXException 882 { 883 XMLUtils.startElement(handler, "zone-item-error"); 884 885 XMLUtils.createElement(handler, "exception-message", e != null ? StringUtils.defaultString(e.getMessage()) : ""); 886 XMLUtils.createElement(handler, "exception-stack-trace", StringUtils.defaultString(ExceptionUtils.getFullStackTrace(e))); 887 888 XMLUtils.endElement(handler, "zone-item-error"); 889 } 890}