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.hasNonEmptyValue(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 if (dataHolder.hasNonEmptyValue(dataPath)) 327 { 328 Locale locale = StringUtils.isEmpty(lang) ? null : new Locale(lang); 329 dataHolder.dataToSAX(th, dataPath, dataContext.withLocale(locale)); 330 } 331 XMLUtils.endElement(th, "value"); 332 th.endDocument(); 333 334 List<Node> values = new ArrayList<>(); 335 336 // #getChildNodes() returns a NodeList that contains the value(s) saxed 337 // we cannot returns directly this NodeList because saxed values should be wrapped into a <value> tag. 338 NodeList childNodes = result.getNode().getFirstChild().getChildNodes(); 339 for (int i = 0; i < childNodes.getLength(); i++) 340 { 341 Node n = childNodes.item(i); 342 values.add(n); 343 } 344 345 return values; 346 } 347 catch (BadDataPathCardinalityException e) 348 { 349 _logger.error("Unable to get attribute at path '" + dataPath + "'. Path is invalid.", e); 350 } 351 catch (TransformerConfigurationException | SAXException | IOException e) 352 { 353 _logger.error("Fail to sax attribute at path '" + dataPath + "'", e); 354 } 355 catch (Exception e) 356 { 357 _logger.error("An error occurred, impossible to get attribute at path '" + dataPath + "'", e); 358 } 359 360 return null; 361 } 362 363 /** 364 * Get the metadata of a content 365 * @param contentId The content id 366 * @param metadataName The metadata name (/ for composite) 367 * @param lang The language for localized metadata. Can be null to get the current language. 368 * @return The name or empty if the metadata or the content does not exist 369 */ 370 public static String contentMetadata(String contentId, String metadataName, String lang) 371 { 372 try 373 { 374 Content content = _ametysObjectResolver.resolveById(contentId); 375 try 376 { 377 Locale locale = StringUtils.isEmpty(lang) ? null : new Locale(lang); 378 return _getMetadata(content.getMetadataHolder(), metadataName, locale); 379 } 380 catch (UnknownMetadataException e) 381 { 382 _logger.debug("Can not get metadata '" + metadataName + "' on content with id '" + contentId + "'", e); 383 return ""; 384 } 385 } 386 catch (UnknownAmetysObjectException e) 387 { 388 _logger.debug("Can not get metadata '" + metadataName + "' on unknown content with id '" + contentId + "'", e); 389 return ""; 390 } 391 } 392 393 /** 394 * Get the metadata of a content 395 * @param contentId The content id 396 * @param metadataName The metadata name (/ for composite) 397 * @return The name or empty if the metadata or the content does not exist 398 */ 399 public static String contentMetadata(String contentId, String metadataName) 400 { 401 return contentMetadata(contentId, metadataName, null); 402 } 403 404 private static String _getMetadata(CompositeMetadata cm, String metadataName, Locale locale) 405 { 406 int i = metadataName.indexOf("/"); 407 if (i == -1) 408 { 409 if (cm.getType(metadataName).equals(MetadataType.MULTILINGUAL_STRING)) 410 { 411 if (locale == null) 412 { 413 String currentLanguage = lang(); 414 if (StringUtils.isEmpty(currentLanguage)) 415 { 416 _logger.error("Can not get the value of a multilingual metadata " + metadataName + " without a defined locale"); 417 return ""; 418 } 419 return cm.getLocalizedString(metadataName, new Locale(currentLanguage)); 420 } 421 else 422 { 423 return cm.getLocalizedString(metadataName, locale); 424 } 425 } 426 else 427 { 428 return cm.getString(metadataName); 429 } 430 } 431 else 432 { 433 return _getMetadata(cm.getCompositeMetadata(metadataName.substring(0, i)), metadataName.substring(i + 1), locale); 434 } 435 } 436 437 /** 438 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of the current content. 439 * @return an Element containing the attachments of the current content as {@link Resource}s. 440 */ 441 public static Node contentAttachments() 442 { 443 Request request = ContextHelper.getRequest(_context); 444 445 Content content = (Content) request.getAttribute(Content.class.getName()); 446 447 return contentAttachments(content); 448 } 449 450 /** 451 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of a given content. 452 * @param contentId the content ID. 453 * @return an Element containing the attachments of the given content as {@link Resource}s. 454 */ 455 public static Node contentAttachments(String contentId) 456 { 457 Content content = _ametysObjectResolver.resolveById(contentId); 458 459 return contentAttachments(content); 460 } 461 462 /** 463 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of a given content. 464 * @param content the content. 465 * @return an Element containing the attachments of the given content as {@link Resource}s. 466 */ 467 private static Node contentAttachments(Content content) 468 { 469 if (content == null) 470 { 471 return null; 472 } 473 474 ResourceCollection collection = content.getRootAttachments(); 475 476 return collection != null ? new ResourceCollectionElement(collection) : new EmptyElement("collection"); 477 } 478 479 /** 480 * Set the content of given id in request attribute 481 * @param contentId the id of content 482 */ 483 public static void setCurrentContent(String contentId) 484 { 485 setCurrentContent(contentId, null); 486 } 487 488 /** 489 * Set the content of given id and version in request attribute 490 * @param contentId the id of content 491 * @param versionLabel The version label 492 */ 493 public static void setCurrentContent(String contentId, String versionLabel) 494 { 495 Request request = ContextHelper.getRequest(_context); 496 497 Content content = _ametysObjectResolver.resolveById(contentId); 498 499 if (StringUtils.isNotEmpty(versionLabel) && content instanceof VersionAwareAmetysObject) 500 { 501 String[] allLabels = ((VersionAwareAmetysObject) content).getAllLabels(); 502 if (ArrayUtils.contains(allLabels, versionLabel)) 503 { 504 ((VersionAwareAmetysObject) content).switchToLabel(versionLabel); 505 } 506 } 507 508 request.setAttribute(Content.class.getName(), content); 509 510 } 511 512 //************************* 513 // Tag methods 514 //************************* 515 516 /** 517 * Returns all tags of the current content. 518 * @return a list of tags. 519 */ 520 public static NodeList contentTags() 521 { 522 Request request = ContextHelper.getRequest(_context); 523 524 Content content = (Content) request.getAttribute(Content.class.getName()); 525 return _contentTags(content); 526 } 527 528 /** 529 * Returns all tags of the given content 530 * @param contentId The identifier of the content 531 * @return a list of tags. 532 */ 533 public static NodeList contentTags(String contentId) 534 { 535 try 536 { 537 Content content = _ametysObjectResolver.resolveById(contentId); 538 return _contentTags(content); 539 } 540 catch (AmetysRepositoryException e) 541 { 542 _logger.warn("Cannot get tags for content '" + contentId + "'", e); 543 } 544 545 return null; 546 } 547 548 /** 549 * Returns all tags of the given content 550 * @param content The content 551 * @return a list of tags. 552 */ 553 protected static NodeList _contentTags(Content content) 554 { 555 if (content == null) 556 { 557 return null; 558 } 559 560 List<TagElement> list = new ArrayList<>(); 561 562 for (String tag : content.getTags()) 563 { 564 list.add(new TagElement(tag)); 565 } 566 567 return new AmetysNodeList(list); 568 } 569 570 /** 571 * Get the name of the parent of a tag. 572 * @param siteName the site name 573 * @param tagName the tag's name 574 * @return The id of parent or empty if not found 575 */ 576 public static String tagParent(String siteName, String tagName) 577 { 578 Map<String, Object> contextParameters = new HashMap<>(); 579 contextParameters.put("siteName", siteName); 580 581 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 582 if (tag == null) 583 { 584 return StringUtils.EMPTY; 585 } 586 587 String parentName = tag.getParentName(); 588 return parentName != null ? parentName : StringUtils.EMPTY; 589 } 590 591 /** 592 * Get the path of a tag. The path contains the tag's parents seprated by '/'. 593 * @param siteName The site name 594 * @param tagName The unique tag's name 595 * @return The tag's path or empty string if tag does not exist 596 */ 597 public static String tagPath (String siteName, String tagName) 598 { 599 Map<String, Object> contextParameters = new HashMap<>(); 600 contextParameters.put("siteName", siteName); 601 602 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 603 if (tag == null) 604 { 605 return StringUtils.EMPTY; 606 } 607 608 String path = tagName; 609 610 Tag parentTag = tag.getParent(); 611 while (parentTag != null) 612 { 613 path = parentTag.getName() + "/" + path; 614 parentTag = parentTag.getParent(); 615 } 616 617 return path; 618 } 619 620 /** 621 * Get the label of a tag 622 * @param siteName the current site 623 * @param tagName the name of the tag 624 * @param lang the lang (if i18n tag) 625 * @return the label of the tag or empty if it cannot be found 626 */ 627 public static String tagLabel(String siteName, String tagName, String lang) 628 { 629 Map<String, Object> contextParameters = new HashMap<>(); 630 contextParameters.put("siteName", siteName); 631 632 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 633 return tag == null ? "" : _i18nUtils.translate(tag.getTitle(), lang); 634 } 635 636 /** 637 * Get the description of a tag 638 * @param siteName the current site 639 * @param tagName the name of the tag 640 * @param lang the lang (if i18n tag) 641 * @return the label of the tag or empty if it cannot be found 642 */ 643 public static String tagDescription(String siteName, String tagName, String lang) 644 { 645 Map<String, Object> contextParameters = new HashMap<>(); 646 contextParameters.put("siteName", siteName); 647 648 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 649 return tag == null ? "" : _i18nUtils.translate(tag.getDescription(), lang); 650 } 651 652 /** 653 * Get the visibility of a tag 654 * @param siteName the current site 655 * @param tagName the name of the tag 656 * @return the lower-cased visibility of the tag ("public" or "private") 657 */ 658 public static String tagVisibility(String siteName, String tagName) 659 { 660 Map<String, Object> contextParameters = new HashMap<>(); 661 contextParameters.put("siteName", siteName); 662 663 CMSTag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 664 return tag == null ? "" : tag.getVisibility().toString().toLowerCase(); 665 } 666 667 /* ----------------------------- */ 668 /* Content type methods */ 669 /* ----------------------------- */ 670 671 /** 672 * Returns all tags of a content type 673 * @param contentTypeId The id of the content type 674 * @return a list of tags. 675 */ 676 public static NodeList contentTypeTags(String contentTypeId) 677 { 678 ArrayList<TagElement> tags = new ArrayList<>(); 679 680 try 681 { 682 ContentType cType = _cTypeExtensionPoint.getExtension(contentTypeId); 683 if (cType != null) 684 { 685 for (String tag : cType.getTags()) 686 { 687 tags.add(new TagElement(tag)); 688 } 689 } 690 else 691 { 692 _logger.error("Can not get tags of unknown content type of id '" + contentTypeId + "'"); 693 } 694 695 } 696 catch (AmetysRepositoryException e) 697 { 698 _logger.error("Can not get tags of content type of id '" + contentTypeId + "'", e); 699 } 700 701 return new AmetysNodeList(tags); 702 } 703 704 /** 705 * Get the HTML view of a content 706 * @param contentId the id of content 707 * @return the content html view wrapped into a <content> tag 708 */ 709 public static Node getContentView(String contentId) 710 { 711 return getContentView(contentId, null, 1, null, false); 712 } 713 714 /** 715 * Get the HTML view of a content with offset on headings 716 * @param contentId the id of content 717 * @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. 718 * @return the content html view wrapped into a <content> tag 719 */ 720 public static Node getContentView(String contentId, int startHeadingsLevel) 721 { 722 return getContentView(contentId, null, startHeadingsLevel, null, false); 723 } 724 725 /** 726 * Get the HTML view of a content 727 * @param contentId the id of content 728 * @param viewName The content view name 729 * @return the content html view wrapped into a <content> tag 730 */ 731 public static Node getContentView(String contentId, String viewName) 732 { 733 return getContentView(contentId, viewName, 1, null, false); 734 } 735 736 /** 737 * Get the HTML view of a content 738 * @param contentId the id of content 739 * @param viewName The content view name 740 * @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. 741 * @return the content html view wrapped into a <content> tag 742 */ 743 public static Node getContentView(String contentId, String viewName, int startHeadingsLevel) 744 { 745 return getContentView(contentId, viewName, startHeadingsLevel, null, false); 746 } 747 748 /** 749 * Get the HTML view of a content with offset on headings 750 * @param contentId the id of content 751 * @param viewName The content view name 752 * @param lang the language. Can be null. Useful only if the content has a multilingual title. 753 * @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. 754 * @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. 755 * @return the content html view wrapped into a <content> tag 756 */ 757 public static Node getContentView(String contentId, String viewName, int startHeadingsLevel, String lang, boolean checkReadAccess) 758 { 759 Content content = _ametysObjectResolver.resolveById(contentId); 760 761 if (checkReadAccess && !_rightManager.currentUserHasReadAccess(content)) 762 { 763 _logger.warn("Current user is not authorized to see content of id '" + contentId + "'. AmetysXSLHelper#getContentView will return null element"); 764 return null; 765 } 766 767 String uri = "cocoon://_content.html?contentId=" + content.getId(); 768 if (StringUtils.isNotEmpty(viewName)) 769 { 770 uri += "&viewName=" + viewName; 771 } 772 773 Locale requestedLocale = StringUtils.isNotEmpty(lang) ? new Locale(lang) : null; 774 775 DocumentBuilder builder = null; 776 Source source = null; 777 778 try 779 { 780 source = _sourceResolver.resolveURI(uri); 781 782 Source src = null; 783 try 784 { 785 // Wrap HTML view into a <content> tag and move titles hierarchy if needed 786 src = _sourceResolver.resolveURI("plugin:cms://stylesheets/content/content2htmlview.xsl"); 787 try (InputStream is = src.getInputStream()) 788 { 789 SAXTransformerFactory tFactory = (SAXTransformerFactory) TransformerFactory.newInstance(); 790 791 // Set uri resolver to resolve import 792 tFactory.setURIResolver(new URIResolver() 793 { 794 public javax.xml.transform.Source resolve(String href, String base) throws TransformerException 795 { 796 try 797 { 798 Source resolvedSource = _sourceResolver.resolveURI(href); 799 return new StreamSource(resolvedSource.getInputStream()); 800 } 801 catch (IOException e) 802 { 803 throw new TransformerException(e); 804 } 805 } 806 }); 807 808 TransformerHandler transformerHandler = tFactory.newTransformerHandler(new StreamSource(is)); 809 Transformer transformer = transformerHandler.getTransformer(); 810 811 Properties format = new Properties(); 812 format.put(OutputKeys.METHOD, "xml"); 813 format.put(OutputKeys.ENCODING, "UTF-8"); 814 815 transformer.setOutputProperties(format); 816 817 transformer.setParameter("contentId", content.getId()); 818 transformer.setParameter("contentName", content.getName()); 819 transformer.setParameter("contentTitle", content.getTitle(requestedLocale)); 820 if (content.getLanguage() != null) 821 { 822 transformer.setParameter("contentLanguage", content.getLanguage()); 823 } 824 transformer.setParameter("headingLevel", startHeadingsLevel); 825 826 builder = DocumentBuilderFactory.newInstance().newDocumentBuilder(); 827 Document document = builder.newDocument(); 828 DOMResult result = new DOMResult(document); 829 830 transformerHandler.setResult(result); 831 SourceUtil.toSAX(source, transformerHandler); 832 833 return result.getNode(); 834 } 835 } 836 finally 837 { 838 _sourceResolver.release(src); 839 } 840 } 841 catch (Exception e) 842 { 843 _logger.error("Fail to get HTML view of content " + contentId, e); 844 return null; 845 } 846 finally 847 { 848 _sourceResolver.release(source); 849 } 850 851 } 852}