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