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