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