001/* 002 * Copyright 2012 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 */ 016 017package org.ametys.cms.transformation.xslt; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.util.ArrayList; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Locale; 025import java.util.Map; 026import java.util.Properties; 027 028import javax.xml.parsers.DocumentBuilder; 029import javax.xml.parsers.DocumentBuilderFactory; 030import javax.xml.transform.OutputKeys; 031import javax.xml.transform.Transformer; 032import javax.xml.transform.TransformerConfigurationException; 033import javax.xml.transform.TransformerException; 034import javax.xml.transform.TransformerFactory; 035import javax.xml.transform.URIResolver; 036import javax.xml.transform.dom.DOMResult; 037import javax.xml.transform.sax.SAXTransformerFactory; 038import javax.xml.transform.sax.TransformerHandler; 039import javax.xml.transform.stream.StreamSource; 040 041import org.apache.avalon.framework.context.Context; 042import org.apache.avalon.framework.context.ContextException; 043import org.apache.avalon.framework.logger.LogEnabled; 044import org.apache.avalon.framework.logger.Logger; 045import org.apache.avalon.framework.service.ServiceException; 046import org.apache.avalon.framework.service.ServiceManager; 047import org.apache.cocoon.components.ContextHelper; 048import org.apache.cocoon.components.source.SourceUtil; 049import org.apache.cocoon.environment.Request; 050import org.apache.cocoon.xml.XMLUtils; 051import org.apache.commons.lang.StringUtils; 052import org.apache.commons.lang3.ArrayUtils; 053import org.apache.excalibur.source.Source; 054import org.apache.excalibur.source.SourceResolver; 055import org.w3c.dom.Document; 056import org.w3c.dom.Element; 057import org.w3c.dom.Node; 058import org.w3c.dom.NodeList; 059import org.xml.sax.SAXException; 060 061import org.ametys.cms.content.ContentHelper; 062import org.ametys.cms.contenttype.ContentType; 063import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 064import org.ametys.cms.repository.Content; 065import org.ametys.cms.tag.CMSTag; 066import org.ametys.cms.tag.Tag; 067import org.ametys.cms.tag.TagProviderExtensionPoint; 068import org.ametys.cms.transformation.dom.TagElement; 069import org.ametys.core.util.dom.AmetysNodeList; 070import org.ametys.core.util.dom.EmptyElement; 071import org.ametys.core.util.dom.StringElement; 072import org.ametys.plugins.explorer.resources.Resource; 073import org.ametys.plugins.explorer.resources.ResourceCollection; 074import org.ametys.plugins.explorer.resources.dom.ResourceCollectionElement; 075import org.ametys.plugins.repository.AmetysObjectResolver; 076import org.ametys.plugins.repository.AmetysRepositoryException; 077import org.ametys.plugins.repository.UnknownAmetysObjectException; 078import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 079import org.ametys.plugins.repository.metadata.CompositeMetadata; 080import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType; 081import org.ametys.plugins.repository.metadata.UnknownMetadataException; 082import org.ametys.plugins.repository.version.VersionAwareAmetysObject; 083import org.ametys.runtime.model.exception.BadDataPathCardinalityException; 084import org.ametys.runtime.model.type.DataContext; 085 086/** 087 * Helper component to be used from XSL stylesheets. 088 */ 089public class AmetysXSLTHelper extends org.ametys.core.util.AmetysXSLTHelper implements LogEnabled 090{ 091 /** The Ametys object resolver */ 092 protected static AmetysObjectResolver _ametysObjectResolver; 093 /** The content types extension point */ 094 protected static ContentTypeExtensionPoint _cTypeExtensionPoint; 095 /** The tags provider */ 096 protected static TagProviderExtensionPoint _tagProviderExtPt; 097 /** Helper for content */ 098 protected static ContentHelper _contentHelper; 099 /** The avalon context */ 100 protected static Context _context; 101 /** The logger */ 102 protected static Logger _logger; 103 /** The source resolver */ 104 protected static SourceResolver _sourceResolver; 105 106 @Override 107 public void contextualize(Context context) throws ContextException 108 { 109 super.contextualize(context); 110 _context = context; 111 } 112 113 @Override 114 public void enableLogging(Logger logger) 115 { 116 _logger = logger; 117 } 118 119 @Override 120 public void service(ServiceManager manager) throws ServiceException 121 { 122 _ametysObjectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 123 _cTypeExtensionPoint = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 124 _tagProviderExtPt = (TagProviderExtensionPoint) manager.lookup(TagProviderExtensionPoint.ROLE); 125 _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE); 126 _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 127 } 128 129 /* ------------------------ */ 130 /* Content methods */ 131 /* ------------------------ */ 132 133 /** 134 * Get the content types of a content 135 * @param contentId The content id 136 * @return The content type or empty if the content does not exist 137 */ 138 public static NodeList contentTypes(String contentId) 139 { 140 ArrayList<StringElement> contentTypes = new ArrayList<>(); 141 142 try 143 { 144 Content content = _ametysObjectResolver.resolveById(contentId); 145 146 try 147 { 148 for (String id : content.getTypes()) 149 { 150 contentTypes.add(new StringElement("content-type", "id", id)); 151 } 152 } 153 catch (AmetysRepositoryException e) 154 { 155 _logger.error("Can not get type of content with id '" + contentId + "'", e); 156 } 157 } 158 catch (UnknownAmetysObjectException e) 159 { 160 _logger.error("Can not get type of content with id '" + contentId + "'", e); 161 } 162 163 return new AmetysNodeList(contentTypes); 164 } 165 166 /** 167 * Get the mixins of a content 168 * @param contentId The content id 169 * @return The content type or empty if the content does not exist 170 */ 171 public static NodeList contentMixinTypes(String contentId) 172 { 173 ArrayList<StringElement> contentTypes = new ArrayList<>(); 174 175 try 176 { 177 Content content = _ametysObjectResolver.resolveById(contentId); 178 179 try 180 { 181 for (String id : content.getMixinTypes()) 182 { 183 contentTypes.add(new StringElement("mixin", "id", id)); 184 } 185 } 186 catch (AmetysRepositoryException e) 187 { 188 _logger.error("Can not get type of content with id '" + contentId + "'", e); 189 } 190 } 191 catch (UnknownAmetysObjectException e) 192 { 193 _logger.error("Can not get type of content with id '" + contentId + "'", e); 194 } 195 196 return new AmetysNodeList(contentTypes); 197 } 198 199 /** 200 * Determines if the content of given id is a entry of reference table 201 * @param contentId the content id 202 * @return true if the content type is a reference table 203 */ 204 public static boolean isReferenceTableContent(String contentId) 205 { 206 try 207 { 208 Content content = _ametysObjectResolver.resolveById(contentId); 209 return _contentHelper.isReferenceTable(content); 210 } 211 catch (UnknownAmetysObjectException e) 212 { 213 _logger.error("Can not get type of unknown content with id '" + contentId + "'", e); 214 return false; 215 } 216 } 217 218 /** 219 * Returns the current language for rendering. 220 * @return the current language for rendering. 221 */ 222 public static String lang() 223 { 224 Request request = ContextHelper.getRequest(_context); 225 return (String) request.getAttribute("renderingLanguage"); 226 } 227 228 /** 229 * Determines if there is a value for the data at the given path 230 * @param contentId The content id 231 * @param dataPath the path of data 232 * @return true if the data exists 233 */ 234 public static boolean hasValue(String contentId, String dataPath) 235 { 236 try 237 { 238 Content content = _ametysObjectResolver.resolveById(contentId); 239 return content.hasValue(dataPath); 240 } 241 catch (UnknownAmetysObjectException e) 242 { 243 if (_logger.isDebugEnabled()) 244 { 245 _logger.debug("Can not check if attribute '" + dataPath + "' exists on unknown content with id '" + contentId + "'", e); 246 } 247 return false; 248 } 249 } 250 251 /** 252 * Get the attribute of a content at the given path 253 * @param contentId The content id 254 * @param dataPath The data path 255 * @return The value into a "value" node or null if an error occurred 256 */ 257 public static NodeList contentAttribute(String contentId, String dataPath) 258 { 259 return contentAttribute(contentId, dataPath, null); 260 } 261 262 /** 263 * Get the attribute of a content at the given path 264 * @param contentId The content id 265 * @param dataPath The data path 266 * @param lang The language for localized attribute. Can be null for non-localized attribute or to get the values for all existing locales. 267 * @return The value into a "value" node or null if an error occurred 268 */ 269 public static NodeList contentAttribute(String contentId, String dataPath, String lang) 270 { 271 try 272 { 273 Content content = _ametysObjectResolver.resolveById(contentId); 274 List<Node> values = _getNodeValues(content.getDataHolder(), dataPath, lang, DataContext.newInstance().withObjectId(contentId)); 275 if (values != null) 276 { 277 return new AmetysNodeList(values); 278 } 279 } 280 catch (UnknownAmetysObjectException e) 281 { 282 _logger.error("Can not get attribute at path '" + dataPath + "' on unknown content with id '" + contentId + "'", e); 283 } 284 285 return null; 286 } 287 288 /** 289 * Get values of an attribute of a model aware data holder at the given path 290 * @param dataHolder the data holder 291 * @param dataPath The data path 292 * @param lang The language for localized attribute. Can be null for non-localized attribute or to get the values for all existing locales. 293 * @return A Node for each values or null if an error occurred 294 */ 295 protected static List<Node> _getNodeValues(ModelAwareDataHolder dataHolder, String dataPath, String lang) 296 { 297 return _getNodeValues(dataHolder, dataPath, lang, DataContext.newInstance()); 298 } 299 300 /** 301 * Get values of an attribute of a model aware data holder at the given path 302 * @param dataHolder the data holder 303 * @param dataPath The data path 304 * @param lang The language for localized attribute. Can be null for non-localized attribute or to get the values for all existing locales. 305 * @param dataContext The data context 306 * @return A Node for each values or null if an error occurred 307 */ 308 protected static List<Node> _getNodeValues(ModelAwareDataHolder dataHolder, String dataPath, String lang, DataContext dataContext) 309 { 310 311 if (dataHolder == null) 312 { 313 return null; 314 } 315 316 try 317 { 318 SAXTransformerFactory saxTransformerFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); 319 TransformerHandler th = saxTransformerFactory.newTransformerHandler(); 320 321 DOMResult result = new DOMResult(); 322 th.setResult(result); 323 324 th.startDocument(); 325 XMLUtils.startElement(th, "value"); 326 327 Locale locale = StringUtils.isEmpty(lang) ? null : new Locale(lang); 328 dataHolder.dataToSAX(th, dataPath, dataContext.withLocale(locale).withEmptyValues(false)); 329 330 XMLUtils.endElement(th, "value"); 331 th.endDocument(); 332 333 List<Node> values = new ArrayList<>(); 334 335 // #getChildNodes() returns a NodeList that contains the value(s) saxed 336 // we cannot returns directly this NodeList because saxed values should be wrapped into a <value> tag. 337 NodeList childNodes = result.getNode().getFirstChild().getChildNodes(); 338 for (int i = 0; i < childNodes.getLength(); i++) 339 { 340 Node n = childNodes.item(i); 341 values.add(n); 342 } 343 344 return values; 345 } 346 catch (BadDataPathCardinalityException e) 347 { 348 _logger.error("Unable to get attribute at path '" + dataPath + "'. Path is invalid.", e); 349 } 350 catch (TransformerConfigurationException | SAXException e) 351 { 352 _logger.error("Fail to sax attribute at path '" + dataPath + "'", e); 353 } 354 catch (Exception e) 355 { 356 _logger.error("An error occurred, impossible to get attribute at path '" + dataPath + "'", e); 357 } 358 359 return null; 360 } 361 362 /** 363 * Get the metadata of a content 364 * @param contentId The content id 365 * @param metadataName The metadata name (/ for composite) 366 * @param lang The language for localized metadata. Can be null to get the current language. 367 * @return The name or empty if the metadata or the content does not exist 368 */ 369 public static String contentMetadata(String contentId, String metadataName, String lang) 370 { 371 try 372 { 373 Content content = _ametysObjectResolver.resolveById(contentId); 374 try 375 { 376 Locale locale = StringUtils.isEmpty(lang) ? null : new Locale(lang); 377 return _getMetadata(content.getMetadataHolder(), metadataName, locale); 378 } 379 catch (UnknownMetadataException e) 380 { 381 _logger.debug("Can not get metadata '" + metadataName + "' on content with id '" + contentId + "'", e); 382 return ""; 383 } 384 } 385 catch (UnknownAmetysObjectException e) 386 { 387 _logger.debug("Can not get metadata '" + metadataName + "' on unknown content with id '" + contentId + "'", e); 388 return ""; 389 } 390 } 391 392 /** 393 * Get the metadata of a content 394 * @param contentId The content id 395 * @param metadataName The metadata name (/ for composite) 396 * @return The name or empty if the metadata or the content does not exist 397 */ 398 public static String contentMetadata(String contentId, String metadataName) 399 { 400 return contentMetadata(contentId, metadataName, null); 401 } 402 403 private static String _getMetadata(CompositeMetadata cm, String metadataName, Locale locale) 404 { 405 int i = metadataName.indexOf("/"); 406 if (i == -1) 407 { 408 if (cm.getType(metadataName).equals(MetadataType.MULTILINGUAL_STRING)) 409 { 410 if (locale == null) 411 { 412 String currentLanguage = lang(); 413 if (StringUtils.isEmpty(currentLanguage)) 414 { 415 _logger.error("Can not get the value of a multilingual metadata " + metadataName + " without a defined locale"); 416 return ""; 417 } 418 return cm.getLocalizedString(metadataName, new Locale(currentLanguage)); 419 } 420 else 421 { 422 return cm.getLocalizedString(metadataName, locale); 423 } 424 } 425 else 426 { 427 return cm.getString(metadataName); 428 } 429 } 430 else 431 { 432 return _getMetadata(cm.getCompositeMetadata(metadataName.substring(0, i)), metadataName.substring(i + 1), locale); 433 } 434 } 435 436 /** 437 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of the current content. 438 * @return an Element containing the attachments of the current content as {@link Resource}s. 439 */ 440 public static Node contentAttachments() 441 { 442 Request request = ContextHelper.getRequest(_context); 443 444 Content content = (Content) request.getAttribute(Content.class.getName()); 445 446 return contentAttachments(content); 447 } 448 449 /** 450 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of a given content. 451 * @param contentId the content ID. 452 * @return an Element containing the attachments of the given content as {@link Resource}s. 453 */ 454 public static Node contentAttachments(String contentId) 455 { 456 Content content = _ametysObjectResolver.resolveById(contentId); 457 458 return contentAttachments(content); 459 } 460 461 /** 462 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of a given content. 463 * @param content the content. 464 * @return an Element containing the attachments of the given content as {@link Resource}s. 465 */ 466 private static Node contentAttachments(Content content) 467 { 468 if (content == null) 469 { 470 return null; 471 } 472 473 ResourceCollection collection = content.getRootAttachments(); 474 475 return collection != null ? new ResourceCollectionElement(collection) : new EmptyElement("collection"); 476 } 477 478 /** 479 * Set the content of given id in request attribute 480 * @param contentId the id of content 481 */ 482 public static void setCurrentContent(String contentId) 483 { 484 setCurrentContent(contentId, null); 485 } 486 487 /** 488 * Set the content of given id and version in request attribute 489 * @param contentId the id of content 490 * @param versionLabel The version label 491 */ 492 public static void setCurrentContent(String contentId, String versionLabel) 493 { 494 Request request = ContextHelper.getRequest(_context); 495 496 Content content = _ametysObjectResolver.resolveById(contentId); 497 498 if (StringUtils.isNotEmpty(versionLabel) && content instanceof VersionAwareAmetysObject) 499 { 500 String[] allLabels = ((VersionAwareAmetysObject) content).getAllLabels(); 501 if (ArrayUtils.contains(allLabels, versionLabel)) 502 { 503 ((VersionAwareAmetysObject) content).switchToLabel(versionLabel); 504 } 505 } 506 507 request.setAttribute(Content.class.getName(), content); 508 509 } 510 511 //************************* 512 // Tag methods 513 //************************* 514 515 /** 516 * Returns all tags of the current content. 517 * @return a list of tags. 518 */ 519 public static NodeList contentTags() 520 { 521 Request request = ContextHelper.getRequest(_context); 522 523 Content content = (Content) request.getAttribute(Content.class.getName()); 524 return _contentTags(content); 525 } 526 527 /** 528 * Returns all tags of the given content 529 * @param contentId The identifier of the content 530 * @return a list of tags. 531 */ 532 public static NodeList contentTags(String contentId) 533 { 534 try 535 { 536 Content content = _ametysObjectResolver.resolveById(contentId); 537 return _contentTags(content); 538 } 539 catch (AmetysRepositoryException e) 540 { 541 _logger.warn("Cannot get tags for content '" + contentId + "'", e); 542 } 543 544 return null; 545 } 546 547 /** 548 * Returns all tags of the given content 549 * @param content The content 550 * @return a list of tags. 551 */ 552 protected static NodeList _contentTags(Content content) 553 { 554 if (content == null) 555 { 556 return null; 557 } 558 559 List<TagElement> list = new ArrayList<>(); 560 561 for (String tag : content.getTags()) 562 { 563 list.add(new TagElement(tag)); 564 } 565 566 return new AmetysNodeList(list); 567 } 568 569 /** 570 * Get the name of the parent of a tag. 571 * @param siteName the site name 572 * @param tagName the tag's name 573 * @return The id of parent or empty if not found 574 */ 575 public static String tagParent(String siteName, String tagName) 576 { 577 Map<String, Object> contextParameters = new HashMap<>(); 578 contextParameters.put("siteName", siteName); 579 580 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 581 if (tag == null) 582 { 583 return StringUtils.EMPTY; 584 } 585 586 String parentName = tag.getParentName(); 587 return parentName != null ? parentName : StringUtils.EMPTY; 588 } 589 590 /** 591 * Get the path of a tag. The path contains the tag's parents seprated by '/'. 592 * @param siteName The site name 593 * @param tagName The unique tag's name 594 * @return The tag's path or empty string if tag does not exist 595 */ 596 public static String tagPath (String siteName, String tagName) 597 { 598 Map<String, Object> contextParameters = new HashMap<>(); 599 contextParameters.put("siteName", siteName); 600 601 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 602 if (tag == null) 603 { 604 return StringUtils.EMPTY; 605 } 606 607 String path = tagName; 608 609 Tag parentTag = tag.getParent(); 610 while (parentTag != null) 611 { 612 path = parentTag.getName() + "/" + path; 613 parentTag = parentTag.getParent(); 614 } 615 616 return path; 617 } 618 619 /** 620 * Get the label of a tag 621 * @param siteName the current site 622 * @param tagName the name of the tag 623 * @param lang the lang (if i18n tag) 624 * @return the label of the tag or empty if it cannot be found 625 */ 626 public static String tagLabel(String siteName, String tagName, String lang) 627 { 628 Map<String, Object> contextParameters = new HashMap<>(); 629 contextParameters.put("siteName", siteName); 630 631 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 632 return tag == null ? "" : _i18nUtils.translate(tag.getTitle(), lang); 633 } 634 635 /** 636 * Get the description of a tag 637 * @param siteName the current site 638 * @param tagName the name of the tag 639 * @param lang the lang (if i18n tag) 640 * @return the label of the tag or empty if it cannot be found 641 */ 642 public static String tagDescription(String siteName, String tagName, String lang) 643 { 644 Map<String, Object> contextParameters = new HashMap<>(); 645 contextParameters.put("siteName", siteName); 646 647 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 648 return tag == null ? "" : _i18nUtils.translate(tag.getDescription(), lang); 649 } 650 651 /** 652 * Get the visibility of a tag 653 * @param siteName the current site 654 * @param tagName the name of the tag 655 * @return the lower-cased visibility of the tag ("public" or "private") 656 */ 657 public static String tagVisibility(String siteName, String tagName) 658 { 659 Map<String, Object> contextParameters = new HashMap<>(); 660 contextParameters.put("siteName", siteName); 661 662 CMSTag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 663 return tag == null ? "" : tag.getVisibility().toString().toLowerCase(); 664 } 665 666 /* ----------------------------- */ 667 /* Content type methods */ 668 /* ----------------------------- */ 669 670 /** 671 * Returns all tags of a content type 672 * @param contentTypeId The id of the content type 673 * @return a list of tags. 674 */ 675 public static NodeList contentTypeTags(String contentTypeId) 676 { 677 ArrayList<TagElement> tags = new ArrayList<>(); 678 679 try 680 { 681 ContentType cType = _cTypeExtensionPoint.getExtension(contentTypeId); 682 if (cType != null) 683 { 684 for (String tag : cType.getTags()) 685 { 686 tags.add(new TagElement(tag)); 687 } 688 } 689 else 690 { 691 _logger.error("Can not get tags of unknown content type of id '" + contentTypeId + "'"); 692 } 693 694 } 695 catch (AmetysRepositoryException e) 696 { 697 _logger.error("Can not get tags of content type of id '" + contentTypeId + "'", e); 698 } 699 700 return new AmetysNodeList(tags); 701 } 702 703 /** 704 * Get the HTML view of a content 705 * @param contentId the id of content 706 * @return the content html view wrapped into a <content> tag 707 */ 708 public static Node getContentView(String contentId) 709 { 710 return getContentView(contentId, null, 1, null, false); 711 } 712 713 /** 714 * Get the HTML view of a content with offset on headings 715 * @param contentId the id of content 716 * @param startHeadingsLevel The start level for headings (h1, h2, h3, ...). For example, set to 2 so that the highest level headings are <h2>. Set to 1 for no offset. 717 * @return the content html view wrapped into a <content> tag 718 */ 719 public static Node getContentView(String contentId, int startHeadingsLevel) 720 { 721 return getContentView(contentId, null, startHeadingsLevel, null, false); 722 } 723 724 /** 725 * Get the HTML view of a content 726 * @param contentId the id of content 727 * @param viewName The content view name 728 * @return the content html view wrapped into a <content> tag 729 */ 730 public static Node getContentView(String contentId, String viewName) 731 { 732 return getContentView(contentId, viewName, 1, null, false); 733 } 734 735 /** 736 * Get the HTML view of a content 737 * @param contentId the id of content 738 * @param viewName The content view name 739 * @param startHeadingsLevel The start level for headings (h1, h2, h3, ...). For example, set to 2 so that the highest level headings are <h2>. Set to 1 for no offset. 740 * @return the content html view wrapped into a <content> tag 741 */ 742 public static Node getContentView(String contentId, String viewName, int startHeadingsLevel) 743 { 744 return getContentView(contentId, viewName, startHeadingsLevel, null, false); 745 } 746 747 /** 748 * Get the HTML view of a content with offset on headings 749 * @param contentId the id of content 750 * @param viewName The content view name 751 * @param lang the language. Can be null. Useful only if the content has a multilingual title. 752 * @param startHeadingsLevel The start level for headings (h1, h2, h3, ...). For example, set to 2 so that the highest level headings are <h2>. Set to 1 for no offset. 753 * @param checkReadAccess Set to <code>true</code> to check the read access on content. Be careful, do not use with <code>true</code> on a cacheable element. 754 * @return the content html view wrapped into a <content> tag 755 */ 756 public static Node getContentView(String contentId, String viewName, int startHeadingsLevel, String lang, boolean checkReadAccess) 757 { 758 Content content = _ametysObjectResolver.resolveById(contentId); 759 760 if (checkReadAccess && !_rightManager.currentUserHasReadAccess(content)) 761 { 762 _logger.warn("Current user is not authorized to see content of id '" + contentId + "'. AmetysXSLHelper#getContentView will return null element"); 763 return null; 764 } 765 766 String uri = "cocoon://_content.html?contentId=" + content.getId(); 767 if (StringUtils.isNotEmpty(viewName)) 768 { 769 uri += "&viewName=" + viewName; 770 } 771 772 Locale requestedLocale = StringUtils.isNotEmpty(lang) ? new Locale(lang) : null; 773 774 DocumentBuilder builder = null; 775 Source source = null; 776 777 try 778 { 779 source = _sourceResolver.resolveURI(uri); 780 781 Source src = null; 782 try 783 { 784 // Wrap HTML view into a <content> tag and move titles hierarchy if needed 785 src = _sourceResolver.resolveURI("plugin:cms://stylesheets/content/content2htmlview.xsl"); 786 try (InputStream is = src.getInputStream()) 787 { 788 SAXTransformerFactory tFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); 789 790 // Set uri resolver to resolve import 791 tFactory.setURIResolver(new URIResolver() 792 { 793 public javax.xml.transform.Source resolve(String href, String base) throws TransformerException 794 { 795 try 796 { 797 Source resolvedSource = _sourceResolver.resolveURI(href); 798 return new StreamSource(resolvedSource.getInputStream()); 799 } 800 catch (IOException e) 801 { 802 throw new TransformerException(e); 803 } 804 } 805 }); 806 807 TransformerHandler transformerHandler = tFactory.newTransformerHandler(new StreamSource(is)); 808 Transformer transformer = transformerHandler.getTransformer(); 809 810 Properties format = new Properties(); 811 format.put(OutputKeys.METHOD, "xml"); 812 format.put(OutputKeys.ENCODING, "UTF-8"); 813 814 transformer.setOutputProperties(format); 815 816 transformer.setParameter("contentId", content.getId()); 817 transformer.setParameter("contentName", content.getName()); 818 transformer.setParameter("contentTitle", content.getTitle(requestedLocale)); 819 if (content.getLanguage() != null) 820 { 821 transformer.setParameter("contentLanguage", content.getLanguage()); 822 } 823 transformer.setParameter("headingLevel", startHeadingsLevel); 824 825 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 826 Document document = builder.newDocument(); 827 DOMResult result = new DOMResult(document); 828 829 transformerHandler.setResult(result); 830 SourceUtil.toSAX(source, transformerHandler); 831 832 return result.getNode(); 833 } 834 } 835 finally 836 { 837 _sourceResolver.release(src); 838 } 839 } 840 catch (Exception e) 841 { 842 _logger.error("Fail to get HTML view of content " + contentId, e); 843 return null; 844 } 845 finally 846 { 847 _sourceResolver.release(source); 848 } 849 850 } 851}