001/* 002 * Copyright 2016 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.web.transformation.xslt; 018 019import java.io.IOException; 020import java.io.InputStream; 021import java.net.MalformedURLException; 022import java.util.ArrayList; 023import java.util.Arrays; 024import java.util.Collection; 025import java.util.Collections; 026import java.util.HashMap; 027import java.util.Iterator; 028import java.util.List; 029import java.util.Map; 030 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.cocoon.components.ContextHelper; 034import org.apache.cocoon.environment.Request; 035import org.apache.cocoon.environment.Response; 036import org.apache.commons.lang.StringUtils; 037import org.apache.excalibur.source.SourceResolver; 038import org.apache.excalibur.source.impl.FileSource; 039import org.w3c.dom.Element; 040import org.w3c.dom.Node; 041import org.w3c.dom.NodeList; 042 043import org.ametys.cms.repository.Content; 044import org.ametys.cms.transformation.ImageResolverHelper; 045import org.ametys.core.right.RightManager; 046import org.ametys.core.right.RightManager.RightResult; 047import org.ametys.core.util.I18nUtils; 048import org.ametys.core.util.dom.AmetysNodeList; 049import org.ametys.core.util.dom.EmptyElement; 050import org.ametys.core.util.dom.FileElement; 051import org.ametys.core.util.dom.MapElement; 052import org.ametys.core.util.dom.MapElement.MapNode; 053import org.ametys.core.util.dom.StringElement; 054import org.ametys.plugins.explorer.resources.Resource; 055import org.ametys.plugins.explorer.resources.ResourceCollection; 056import org.ametys.plugins.explorer.resources.dom.ResourceCollectionElement; 057import org.ametys.plugins.explorer.resources.dom.ResourceElement; 058import org.ametys.plugins.repository.AmetysObject; 059import org.ametys.plugins.repository.AmetysObjectIterable; 060import org.ametys.plugins.repository.AmetysObjectResolver; 061import org.ametys.plugins.repository.UnknownAmetysObjectException; 062import org.ametys.plugins.repository.metadata.CompositeMetadata; 063import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType; 064import org.ametys.plugins.repository.metadata.UnknownMetadataException; 065import org.ametys.plugins.repository.query.expression.Expression.Operator; 066import org.ametys.runtime.parameter.ParameterHelper; 067import org.ametys.web.URIPrefixHandler; 068import org.ametys.web.WebConstants; 069import org.ametys.web.renderingcontext.RenderingContext; 070import org.ametys.web.renderingcontext.RenderingContextHandler; 071import org.ametys.web.repository.content.WebContent; 072import org.ametys.web.repository.dom.PageElement; 073import org.ametys.web.repository.dom.SitemapElement; 074import org.ametys.web.repository.page.Page; 075import org.ametys.web.repository.page.PageQueryHelper; 076import org.ametys.web.repository.page.Zone; 077import org.ametys.web.repository.page.ZoneItem; 078import org.ametys.web.repository.site.Site; 079import org.ametys.web.repository.site.SiteManager; 080import org.ametys.web.repository.sitemap.Sitemap; 081import org.ametys.web.service.ServiceExtensionPoint; 082import org.ametys.web.service.ServiceParameter; 083import org.ametys.web.service.ServiceParameterOrRepeater; 084import org.ametys.web.service.ServiceParameterRepeater; 085import org.ametys.web.site.SiteConfigurationExtensionPoint; 086import org.ametys.web.tags.TagExpression; 087 088/** 089 * Helper component to be used from XSL stylesheets. 090 */ 091public class AmetysXSLTHelper extends org.ametys.cms.transformation.xslt.AmetysXSLTHelper 092{ 093 private static SiteManager _siteManager; 094 private static RenderingContextHandler _renderingContextHandler; 095 private static SiteConfigurationExtensionPoint _siteConf; 096 private static RightManager _rightManager; 097 private static URIPrefixHandler _prefixHandler; 098 private static SourceResolver _sourceResolver; 099 private static ServiceExtensionPoint _serviceEP; 100 101 @Override 102 public void service(ServiceManager manager) throws ServiceException 103 { 104 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 105 _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE); 106 _siteConf = (SiteConfigurationExtensionPoint) manager.lookup(SiteConfigurationExtensionPoint.ROLE); 107 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 108 _ametysObjectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 109 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 110 _prefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE); 111 _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 112 _serviceEP = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 113 } 114 115 /** 116 * Returns the current URI prefix, depending on the rendering context. 117 * @return the current URI prefix. 118 */ 119 public static String uriPrefix() 120 { 121 return _prefixHandler.getUriPrefix(); 122 } 123 124 /** 125 * Returns the URI prefix corresponding to the current site, depending on the rendering context. 126 * @return the URI prefix corresponding to the current site. 127 */ 128 public static String siteUriPrefix() 129 { 130 Request request = ContextHelper.getRequest(_context); 131 String siteName = (String) request.getAttribute("site"); 132 return _prefixHandler.getUriPrefix(siteName); 133 } 134 135 /** 136 * Returns the absolute URI prefix, depending on the rendering context. 137 * @return the absolute URI prefix. 138 */ 139 public static String absoluteUriPrefix() 140 { 141 return _prefixHandler.getAbsoluteUriPrefix(); 142 } 143 144 /** 145 * Returns the absolute URI prefix corresponding to the current site, depending on the rendering context. 146 * @return the absolute URI prefix corresponding to the current site. 147 */ 148 public static String absoluteSiteUriPrefix() 149 { 150 Request request = ContextHelper.getRequest(_context); 151 String siteName = (String) request.getAttribute("site"); 152 return _prefixHandler.getAbsoluteUriPrefix(siteName); 153 } 154 155 /** 156 * Returns the absolute URI prefix corresponding to the given site, depending on the rendering context. 157 * @param siteName The site name. Can be null to get the current site. 158 * @return the absolute URI prefix corresponding to the current site. 159 */ 160 public static String absoluteSiteUriPrefix(String siteName) 161 { 162 if (StringUtils.isEmpty(siteName)) 163 { 164 return absoluteSiteUriPrefix(); 165 } 166 return _prefixHandler.getAbsoluteUriPrefix(siteName); 167 } 168 169 /** 170 * Returns the current skin name. 171 * @return the current skin name. 172 */ 173 public static String skin() 174 { 175 Request request = ContextHelper.getRequest(_context); 176 return (String) request.getAttribute("skin"); 177 } 178 179 /** 180 * Returns the current template name. 181 * @return the current template name. 182 */ 183 public static String template() 184 { 185 Request request = ContextHelper.getRequest(_context); 186 return (String) request.getAttribute("template"); 187 } 188 189 /** 190 * Returns the current sitemap name. 191 * @return the current sitemap name. 192 */ 193 public static String lang() 194 { 195 Request request = ContextHelper.getRequest(_context); 196 return (String) request.getAttribute("sitemapLanguage"); 197 } 198 199 /** 200 * Returns the current sitemap name. 201 * @param pageId The page identifier to get sitemap on 202 * @return the current sitemap name. 203 */ 204 public static String lang(String pageId) 205 { 206 try 207 { 208 Page page = _getPage(pageId); 209 return page.getSitemapName(); 210 } 211 catch (UnknownAmetysObjectException e) 212 { 213 _logger.error("Can not get sitemap lang on page '" + pageId + "'", e); 214 return ""; 215 } 216 } 217 218 /** 219 * Computes the URI for the given resource in the current site's skin.<br> 220 * If the URI is requested by the front-office, it will be absolutized. 221 * @param path the resource path. 222 * @return the URI for the given resource. 223 */ 224 public static String skinURL(String path) 225 { 226 Request request = ContextHelper.getRequest(_context); 227 String siteName = (String) request.getAttribute("site"); 228 Site site = _siteManager.getSite(siteName); 229 String skin = (String) request.getAttribute("skin"); 230 231 String resourcePath = "/skins/" + skin + "/resources/" + path; 232 233 return _getResourceURL(request, site, resourcePath); 234 } 235 236 /** 237 * Computes the URI for the given image with a given heigth and width in the current site's skin.<br> 238 * If the URI is requested by the front-office, it will be absolutized. 239 * @param path the resource path 240 * @param height the height for the resource to get 241 * @param width the width for the resource to get 242 * @return the URI of the given resource 243 */ 244 public static String skinImageURL(String path, int height, int width) 245 { 246 String skinPath = skinURL(path); 247 return StringUtils.substringBeforeLast(skinPath, ".") + "_" + height + "x" + width + "." + StringUtils.substringAfterLast(skinPath, "."); 248 } 249 250 /** 251 * Computes the base 64 representation of the image at the specified path. <br> 252 * @param path the path of the image 253 * @return the base 64-encoded image 254 * @throws IOException if an error occurs while trying to get the file 255 */ 256 public static String skinImageBase64 (String path) throws IOException 257 { 258 FileSource source = (FileSource) _sourceResolver.resolveURI("skin://resources/" + path); 259 return _getResourceBase64(source); 260 } 261 262 /** 263 * Computes the URI for the given image with a given heigth and width in the current site's skin.<br> 264 * If the URI is requested by the front-office, it will be absolutized. 265 * @param path the resource path 266 * @param maxHeight the maximum height for the resource to get 267 * @param maxWidth the maximum width for the resource to get 268 * @return the URI of the given resource 269 */ 270 public static String skinBoundedImageURL(String path, int maxHeight, int maxWidth) 271 { 272 String skinPath = skinURL(path); 273 return StringUtils.substringBeforeLast(skinPath, ".") + "_max" + maxHeight + "x" + maxWidth + "." + StringUtils.substringAfterLast(skinPath, "."); 274 } 275 276 /** 277 * Computes the URI for the given resource in the current template.<br> 278 * If the URI is requested by the front-office, it will be absolutized. 279 * @param path the resource path. 280 * @return the URI for the given resource. 281 */ 282 public static String templateURL(String path) 283 { 284 Request request = ContextHelper.getRequest(_context); 285 String siteName = (String) request.getAttribute("site"); 286 Site site = _siteManager.getSite(siteName); 287 String skin = (String) request.getAttribute("skin"); 288 String template = (String) request.getAttribute("template"); 289 290 String resourcePath = "/skins/" + skin + "/templates/" + template + "/resources/" + path; 291 292 return _getResourceURL(request, site, resourcePath); 293 } 294 295 /** 296 * Computes the URI for the given resource in the given plugin.<br> 297 * If the URI is requested by the front-office, it will be absolutized. 298 * @param plugin the plugin name. 299 * @param path the resource path. 300 * @return the URI for the given resource. 301 */ 302 public static String pluginResourceURL(String plugin, String path) 303 { 304 Request request = ContextHelper.getRequest(_context); 305 String siteName = (String) request.getAttribute("site"); 306 Site site = _siteManager.getSite(siteName); 307 308 String resourcePath = "/plugins/" + plugin + "/resources/" + path; 309 310 return _getResourceURL(request, site, resourcePath); 311 } 312 313 /** 314 * Computes the base 64 representation of the image at the specified path in the given plugin.<br> 315 * @param plugin the plugin's name. 316 * @param path the resource path. 317 * @return the base 64 encoding for the given resource. 318 * @throws IOException if an error occurs when trying to get the file 319 * @throws MalformedURLException if the url is invalid 320 */ 321 public static String pluginImageBase64(String plugin, String path) throws MalformedURLException, IOException 322 { 323 FileSource source = (FileSource) _sourceResolver.resolveURI("plugin:" + plugin + "://resources/" + path); 324 return _getResourceBase64(source); 325 } 326 327 328 private static String _getResourceURL(Request request, Site site, String resourcePath) 329 { 330 String prefix; 331 switch (_renderingContextHandler.getRenderingContext()) 332 { 333 case FRONT: 334 String[] aliases = site.getUrlAliases(); 335 int position = Math.abs(resourcePath.hashCode()) % aliases.length; 336 337 boolean absolute = request.getAttribute("forceAbsoluteUrl") != null ? (Boolean) request.getAttribute("forceAbsoluteUrl") : false; 338 prefix = position == 0 && !absolute ? siteUriPrefix() : aliases[position]; 339 return prefix + resourcePath; 340 341 default: 342 prefix = StringUtils.trimToEmpty((String) request.getAttribute(WebConstants.PATH_PREFIX)); 343 return request.getContextPath() + prefix + resourcePath; 344 } 345 } 346 347 /** 348 * Get the base 64 encoding for the given source 349 * @param source the source 350 * @return the base 64 encoding of the source 351 */ 352 private static String _getResourceBase64(FileSource source) 353 { 354 if (source.exists()) 355 { 356 357 try (InputStream dataIs = source.getInputStream()) 358 { 359 return ImageResolverHelper.resolveImageAsBase64(dataIs, source.getMimeType(), 0, 0, 0, 0); 360 } 361 catch (Exception e) 362 { 363 throw new IllegalStateException(e); 364 } 365 } 366 367 return ""; 368 } 369 370 /** 371 * Returns the current {@link RenderingContext}. 372 * @return the current {@link RenderingContext}. 373 */ 374 public static String renderingContext() 375 { 376 return _renderingContextHandler.getRenderingContext().toString(); 377 } 378 379 /** 380 * Return the name of the zone beeing handled 381 * @param defaultValue If no page is handled currently, this value is returned (can be null, empty...) 382 * @return the name or the default value (so can be null or empty) 383 */ 384 public static String zone(String defaultValue) 385 { 386 Request request = ContextHelper.getRequest(_context); 387 388 return StringUtils.defaultIfEmpty((String) request.getAttribute(Zone.class.getName()), defaultValue); 389 } 390 391 /** 392 * Return the value of a site parameter as a String. 393 * @param parameter the parameter ID. 394 * @return the parameter value as a String. 395 */ 396 public static String siteParameter(String parameter) 397 { 398 Request request = ContextHelper.getRequest(_context); 399 400 String siteName = (String) request.getAttribute("site"); 401 if (StringUtils.isBlank(siteName)) 402 { 403 // In BO xsl 404 siteName = (String) request.getAttribute("siteName"); 405 } 406 407 return siteParameter(siteName, parameter); 408 } 409 410 /** 411 * Return the value of a site parameter as a String. 412 * @param siteName the site name 413 * @param parameter the parameter ID. 414 * @return the parameter value as a String. 415 */ 416 public static String siteParameter(String siteName, String parameter) 417 { 418 try 419 { 420 return _siteConf.getUntypedValue(siteName, parameter); 421 } 422 catch (Exception e) 423 { 424 String message = "Error retrieving the value of the site parameter " + parameter; 425 _logger.error(message, e); 426 throw new RuntimeException(message, e); 427 } 428 } 429 430 /** 431 * Get the service parameters as a {@link Node}. 432 * @return the service parameters as a {@link Node}. 433 */ 434 public static Node serviceParameters() 435 { 436 Request request = ContextHelper.getRequest(_context); 437 ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName()); 438 CompositeMetadata parameters = zoneItem.getServiceParameters(); 439 440 String serviceId = zoneItem.getServiceId(); 441 442 Map<String, MapNode> paramValues = new HashMap<>(); 443 444 for (String paramName : parameters.getMetadataNames()) 445 { 446 ServiceParameterOrRepeater paramDef = _serviceEP.getExtension(serviceId).getParameters().get(paramName); 447 if (paramDef != null && parameters.hasMetadata(paramName)) 448 { 449 paramValues.putAll(_getParameterValue(paramName, parameters, paramDef, "")); 450 } 451 } 452 453 return new MapElement("serviceParameters", paramValues); 454 } 455 456 /** 457 * Returns the value of the given parameter for the current service, or the empty string if the parameter does not exist. 458 * @param parameter the parameter name. 459 * @return the value of the given parameter for the current service. 460 */ 461 public static Node serviceParameter(String parameter) 462 { 463 return serviceParameter(parameter, ""); 464 } 465 466 /** 467 * Returns the value of the given parameter for the current service, or the provided default value if the parameter does not exist. 468 * @param parameter the parameter name or path. 469 * @param defaultValue the default value. Note that default value is ignored if the parameter is a composite parameter. 470 * @return the value of the given parameter for the current service. 471 */ 472 public static Node serviceParameter(String parameter, String defaultValue) 473 { 474 Request request = ContextHelper.getRequest(_context); 475 ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName()); 476 CompositeMetadata parameters = zoneItem.getServiceParameters(); 477 478 String serviceId = zoneItem.getServiceId(); 479 ServiceParameterOrRepeater paramDef = _serviceEP.getExtension(serviceId).getParameters().get(parameter); 480 481 if (paramDef == null) 482 { 483 // The parameter is unknown 484 if (StringUtils.isEmpty(defaultValue)) 485 { 486 return null; 487 } 488 else 489 { 490 return new StringElement(parameter, Collections.EMPTY_MAP, defaultValue); 491 } 492 } 493 494 Map<String, MapNode> value = _getParameterValue(parameter, parameters, paramDef, defaultValue); 495 496 if (!value.containsKey(parameter)) 497 { 498 return null; 499 } 500 else if (paramDef instanceof ServiceParameterRepeater || (paramDef instanceof ServiceParameter && ((ServiceParameter) paramDef).isMultiple())) 501 { 502 MapNode node = value.get(parameter); 503 @SuppressWarnings("unchecked") 504 Map<String, ? extends Object> values = (Map<String, ? extends Object>) node.getValue(); 505 return new MapElement(parameter, node.getAttributes(), values); 506 } 507 else 508 { 509 return new StringElement(parameter, value.get(parameter).getAttributes(), (String) value.get(parameter).getValue()); 510 } 511 } 512 513 private static String _convertTagName(String name) 514 { 515 char c = name.charAt(0); 516 if (c >= '0' && c <= '9') 517 { 518 String hex = Integer.toHexString(c); 519 return "_x" + StringUtils.leftPad(hex, 4, '0') + "_" + name.substring(1); 520 } 521 else 522 { 523 return name; 524 } 525 } 526 527 private static Map<String, MapNode> _getParameterValue(String paramName, CompositeMetadata parentMetadata, ServiceParameterOrRepeater paramDef, String defaultValue) 528 { 529 Map<String, MapNode> paramValues = new HashMap<>(); 530 531 if (paramDef instanceof ServiceParameterRepeater) 532 { 533 if (!parentMetadata.hasMetadata(paramName)) 534 { 535 return paramValues; 536 } 537 538 Map<String, String> attributes = new HashMap<>(); 539 attributes.put("name", paramName); 540 attributes.put("type", MetadataType.COMPOSITE.name().toLowerCase()); 541 542 Map<String, Object> children = new HashMap<>(); 543 544 CompositeMetadata compositeMetadata = parentMetadata.getCompositeMetadata(paramName); 545 String[] entryNames = compositeMetadata.getMetadataNames(); 546 for (String entryName : entryNames) 547 { 548 Map<String, Object> entryValue = new HashMap<>(); 549 550 for (String childParamName : ((ServiceParameterRepeater) paramDef).getChildrenParameters().keySet()) 551 { 552 ServiceParameter childParamDef = ((ServiceParameterRepeater) paramDef).getChildrenParameters().get(childParamName); 553 // Default value is ignored if parameter is a composite metadata 554 Map<String, MapNode> childParamValues = _getParameterValue(childParamName, compositeMetadata.getCompositeMetadata(entryName), childParamDef, ""); 555 entryValue.putAll(childParamValues); 556 } 557 558 Map<String, String> entryAttributes = new HashMap<>(); 559 entryAttributes.put("name", entryName); 560 entryAttributes.put("type", MetadataType.COMPOSITE.name().toLowerCase()); 561 562 MapNode entryNode = new MapNode(entryValue, entryAttributes); 563 children.put(_convertTagName(entryName), entryNode); 564 } 565 566 MapNode node = new MapNode(children, attributes); 567 paramValues.put(paramName, node); 568 } 569 else 570 { 571 if (!parentMetadata.hasMetadata(paramName) && StringUtils.isEmpty(defaultValue)) 572 { 573 return paramValues; 574 } 575 576 Map<String, String> attributes = new HashMap<>(); 577 attributes.put("name", paramName); 578 attributes.put("type", ParameterHelper.typeToString(((ServiceParameter) paramDef).getType())); 579 580 if (((ServiceParameter) paramDef).isMultiple()) 581 { 582 Map<String, Object> values = new HashMap<>(); 583 values.put("value", Arrays.asList(parentMetadata.getStringArray(paramName, new String[] {defaultValue}))); 584 585 MapNode node = new MapNode(values, attributes); 586 paramValues.put(paramName, node); 587 } 588 else 589 { 590 String value = parentMetadata.getString(paramName, defaultValue); 591 if (StringUtils.isEmpty(value)) 592 { 593 value = defaultValue; 594 } 595 MapNode node = new MapNode(value, attributes); 596 paramValues.put(paramName, node); 597 } 598 } 599 600 return paramValues; 601 } 602 603 /** 604 * Returns the current site 605 * @return the current site 606 */ 607 public static String site() 608 { 609 Request request = ContextHelper.getRequest(_context); 610 return (String) request.getAttribute("site"); 611 } 612 613 /** 614 * Returns the current site 615 * @param pageId The identifier ot the page 616 * @return the current site 617 */ 618 public static String site(String pageId) 619 { 620 try 621 { 622 Page page = _getPage(pageId); 623 return page.getSiteName(); 624 } 625 catch (UnknownAmetysObjectException e) 626 { 627 _logger.error("Can not get site on page '" + pageId + "'", e); 628 return ""; 629 } 630 } 631 632 /** 633 * Return the current sitemap as a {@link Node}. 634 * Invisible pages will not be displayed 635 * @return the current sitemap. 636 */ 637 public static Node sitemap() 638 { 639 return sitemap(false); 640 } 641 642 /** 643 * Return the current sitemap as a {@link Node}. 644 * @param includeInvisiblePages Should return child invisible pages 645 * @return the current sitemap. 646 */ 647 public static Node sitemap(boolean includeInvisiblePages) 648 { 649 Request request = ContextHelper.getRequest(_context); 650 Sitemap sitemap = (Sitemap) request.getAttribute(Sitemap.class.getName()); 651 652 if (sitemap == null) 653 { 654 // Try to get sitemap from content 655 Content content = (Content) request.getAttribute(Content.class.getName()); 656 if (content instanceof WebContent) 657 { 658 sitemap = ((WebContent) content).getSite().getSitemap(content.getLanguage()); 659 } 660 } 661 662 if (sitemap == null) 663 { 664 return new EmptyElement("sitemap"); 665 } 666 667 Page page = (Page) request.getAttribute(Page.class.getName()); 668 669 return new SitemapElement(sitemap, page != null ? page.getPathInSitemap() : null, _rightManager, _renderingContextHandler, _currentUserProvider.getUser(), includeInvisiblePages); 670 } 671 672 /** 673 * Return the subsitemap of the given page as a {@link Node}. 674 * Invisible child pages will not be returned; 675 * @param pageId The root page 676 * @return The page as node. 677 */ 678 public static Node sitemap(String pageId) 679 { 680 return sitemap(pageId, false); 681 } 682 683 /** 684 * Return the subsitemap of the given page as a {@link Node}. 685 * @param pageId The root page 686 * @param includeInvisiblePages Should return child invisible pages 687 * @return The page as node. 688 */ 689 public static Node sitemap(String pageId, boolean includeInvisiblePages) 690 { 691 Page rootPage = null; 692 try 693 { 694 rootPage = _ametysObjectResolver.resolveById(pageId); 695 } 696 catch (UnknownAmetysObjectException e) 697 { 698 return new EmptyElement("page"); 699 } 700 701 Request request = ContextHelper.getRequest(_context); 702 Page page = (Page) request.getAttribute(Page.class.getName()); 703 704 return new PageElement(rootPage, _rightManager, _renderingContextHandler, page != null ? page.getPathInSitemap() : null, _currentUserProvider.getUser(), includeInvisiblePages); 705 } 706 707 /** 708 * Computes the breadcrumb of the current page. 709 * @return a NodeList containing all ancestor pages, rooted at the sitemap. 710 */ 711 public static NodeList breadcrumb() 712 { 713 Request request = ContextHelper.getRequest(_context); 714 Page page = (Page) request.getAttribute(Page.class.getName()); 715 716 List<Element> result = new ArrayList<>(); 717 718 AmetysObject parent = page.getParent(); 719 while (parent instanceof Page) 720 { 721 Element node = new StringElement("page", (Map<String, String>) null, parent.getId()); 722 result.add(node); 723 parent = parent.getParent(); 724 } 725 726 Collections.reverse(result); 727 return new AmetysNodeList(result); 728 } 729 730 /** 731 * Returns a DOM {@link Element} representing files and folder of the resources explorer, 732 * under the {@link ResourceCollection} corresponding to the given id. 733 * @param collectionId the id of the root {@link ResourceCollection}. 734 * @return an Element containing files and folders. 735 */ 736 public static Node resourcesById(String collectionId) 737 { 738 ResourceCollection collection = _ametysObjectResolver.resolveById(collectionId); 739 return new ResourceCollectionElement(collection); 740 } 741 742 /** 743 * Returns a DOM {@link Element} representing files and folder of the resources explorer, 744 * under the {@link ResourceCollection} corresponding to the given path. <br> 745 * This path is intended to be relative to the current site's resource explorer. 746 * @param path the path of the root {@link ResourceCollection}, relative to the current site's resource explorer. 747 * @return an Element containing files and folders or null if the specified resource does not exist. 748 */ 749 public static Node resourcesByPath(String path) 750 { 751 Request request = ContextHelper.getRequest(_context); 752 String siteName = (String) request.getAttribute("site"); 753 Site site = _siteManager.getSite(siteName); 754 755 try 756 { 757 ResourceCollection collection = site.getRootResources().getChild(path); 758 return new ResourceCollectionElement(collection); 759 } 760 catch (UnknownAmetysObjectException ex) 761 { 762 return null; 763 } 764 } 765 766 /** 767 * Returns a DOM {@link Element} representing a single file of the resources explorer. <br> 768 * This path is intended to be relative to the current site's resource explorer. 769 * @param path the path of the {@link Resource}, relative to the current site's resource explorer. 770 * @return an Element containing a file or null if the specified resource does not exist. 771 */ 772 public static Node resourceByPath(String path) 773 { 774 Request request = ContextHelper.getRequest(_context); 775 String siteName = (String) request.getAttribute("site"); 776 Site site = _siteManager.getSite(siteName); 777 778 try 779 { 780 Resource resource = site.getRootResources().getChild(path); 781 return new ResourceElement(resource, null); 782 } 783 catch (UnknownAmetysObjectException ex) 784 { 785 return null; 786 } 787 } 788 789 /** 790 * Returns a DOM {@link Element} representing files and folder of a skin directory. <br> 791 * This path is intended to be relative to the current skin's 'resources' directory. 792 * @param path the path of the root {@link ResourceCollection}, relative to the current skin's 'resources' directory. Can be a file path to test its existance. 793 * @return an Element containing files and folders. node name is 'collection' or 'resource' with an attribute 'name'. Return null if the path does not exits. 794 * @throws IOException if an error occured while listing files. 795 */ 796 public static Node skinResources(String path) throws IOException 797 { 798 FileSource source = (FileSource) _sourceResolver.resolveURI("skin://resources/" + path); 799 if (source.exists()) 800 { 801 return new FileElement(source.getFile()); 802 } 803 else 804 { 805 return null; 806 } 807 } 808 809 //************************* 810 // Page methods 811 //************************* 812 813 private static String _getMetadata(CompositeMetadata cm, String metadataName) 814 { 815 int i = metadataName.indexOf("/"); 816 if (i == -1) 817 { 818 return cm.getString(metadataName); 819 } 820 else 821 { 822 return _getMetadata(cm.getCompositeMetadata(metadataName.substring(0, i)), metadataName.substring(i + 1)); 823 } 824 } 825 826 private static Page _getPage(String sitename, String lang, String path) 827 { 828 Site site = _siteManager.getSite(sitename); 829 Sitemap sitemap = site.getSitemap(lang); 830 return sitemap.getChild(path); 831 } 832 833 private static Page _getPage(String id) 834 { 835 return _ametysObjectResolver.resolveById(id); 836 } 837 838 /** 839 * Get the site name of a page. 840 * @param pageId The page id. 841 * @return The name or empty if the page does not exist. 842 */ 843 public static String pageSiteName(String pageId) 844 { 845 try 846 { 847 Page page = _getPage(pageId); 848 return page.getSiteName(); 849 } 850 catch (UnknownAmetysObjectException e) 851 { 852 _logger.error("Can not get site name on page with id '" + pageId + "'", e); 853 return ""; 854 } 855 } 856 857 /** 858 * Get the title of a page. 859 * @param sitename the site name. 860 * @param lang the sitemap name. 861 * @param path the page path. 862 * @return The name or empty if the meta or the page does not exist. 863 */ 864 public static String pageTitle(String sitename, String lang, String path) 865 { 866 try 867 { 868 Page page = _getPage(sitename, lang, path); 869 return page.getTitle(); 870 } 871 catch (UnknownAmetysObjectException e) 872 { 873 _logger.error("Can not get title on page '" + sitename + "/" + lang + "/" + path + "'", e); 874 return ""; 875 } 876 } 877 878 /** 879 * Get the title of a page. 880 * @param pageId The page id. 881 * @return The name or empty if the meta or the page does not exist. 882 */ 883 public static String pageTitle(String pageId) 884 { 885 try 886 { 887 Page page = _getPage(pageId); 888 return page.getTitle(); 889 } 890 catch (UnknownAmetysObjectException e) 891 { 892 _logger.error("Can not get title on page with id '" + pageId + "'", e); 893 return ""; 894 } 895 } 896 897 /** 898 * Get the long title of a page 899 * @param sitename the site name 900 * @param lang the page's language 901 * @param path the page's path 902 * @return The name or empty if the meta or the page does not exist 903 */ 904 public static String pageLongTitle(String sitename, String lang, String path) 905 { 906 try 907 { 908 Page page = _getPage(sitename, lang, path); 909 return page.getLongTitle(); 910 } 911 catch (UnknownAmetysObjectException e) 912 { 913 _logger.error("Can not get long title on page '" + sitename + "/" + lang + "/" + path + "'", e); 914 return ""; 915 } 916 } 917 /** 918 * Get the long title of a page 919 * @param pageId The page id 920 * @return The name or empty if the meta or the page does not exist 921 */ 922 public static String pageLongTitle(String pageId) 923 { 924 try 925 { 926 Page page = _getPage(pageId); 927 return page.getLongTitle(); 928 } 929 catch (UnknownAmetysObjectException e) 930 { 931 _logger.error("Can not get long title on page with id '" + pageId + "'", e); 932 return ""; 933 } 934 } 935 936 /** 937 * Get the meta of a page 938 * @param sitename the site name 939 * @param lang the page's language 940 * @param path the page's path 941 * @param metadataName The meta name (/ for composite) 942 * @return The name or empty if the meta or the page does not exist 943 */ 944 public static String pageMetadata(String sitename, String lang, String path, String metadataName) 945 { 946 try 947 { 948 Page page = _getPage(sitename, lang, path); 949 try 950 { 951 return _getMetadata(page.getMetadataHolder(), metadataName); 952 } 953 catch (UnknownMetadataException e) 954 { 955 _logger.error("Can not get meta '" + metadataName + "' on page with id '" + page.getId() + "'", e); 956 return ""; 957 } 958 } 959 catch (UnknownAmetysObjectException e) 960 { 961 _logger.error("Can not get meta '" + metadataName + "' on page '" + sitename + "/" + lang + "/" + path + "'", e); 962 return ""; 963 } 964 } 965 966 /** 967 * Get the meta of a page 968 * @param pageId The page id 969 * @param metadataName The meta name (/ for composite) 970 * @return The name or empty if the meta or the page does not exist 971 */ 972 public static String pageMetadata(String pageId, String metadataName) 973 { 974 try 975 { 976 Page page = _getPage(pageId); 977 try 978 { 979 return _getMetadata(page.getMetadataHolder(), metadataName); 980 } 981 catch (UnknownMetadataException e) 982 { 983 _logger.error("Can not get meta '" + metadataName + "' on page with id '" + page.getId() + "'", e); 984 return ""; 985 } 986 } 987 catch (UnknownAmetysObjectException e) 988 { 989 _logger.error("Can not get meta '" + metadataName + "' on page with id '" + pageId + "'", e); 990 return ""; 991 } 992 } 993 994 /** 995 * Returns true if the given page is visible into navigation elements 996 * @param pageId the page id. 997 * @return true if the page is visible 998 */ 999 public static boolean pageIsVisible (String pageId) 1000 { 1001 try 1002 { 1003 Page page = _getPage(pageId); 1004 return page.isVisible(); 1005 } 1006 catch (UnknownAmetysObjectException e) 1007 { 1008 _logger.error("Can not get visibility status on page with id '" + pageId + "'", e); 1009 return false; 1010 } 1011 } 1012 1013 /** 1014 * Returns true if the given page is visible into navigation elements 1015 * @param sitename the site name 1016 * @param lang the page's language 1017 * @param path the page's path 1018 * @return true if the page is visible 1019 */ 1020 public static boolean pageIsVisible (String sitename, String lang, String path) 1021 { 1022 try 1023 { 1024 Page page = _getPage(sitename, lang, path); 1025 return page.isVisible(); 1026 } 1027 catch (UnknownAmetysObjectException e) 1028 { 1029 _logger.error("Can not get visibility status on page with id '" + sitename + "/" + lang + "/" + path + "'", e); 1030 return false; 1031 } 1032 } 1033 1034 /** 1035 * Returns true if the given page has restricted access. 1036 * @param pageId the page id. 1037 * @return true if the page exists and has restricted access. 1038 */ 1039 public static boolean pageHasRestrictedAccess(String pageId) 1040 { 1041 try 1042 { 1043 Page page = _getPage(pageId); 1044 return !_rightManager.hasAnonymousReadAccess(page); 1045 } 1046 catch (UnknownAmetysObjectException e) 1047 { 1048 _logger.error("Can not get page access info on page with id '" + pageId + "'", e); 1049 return false; 1050 } 1051 } 1052 1053 /** 1054 * Returns true if the current user has the specified right on the current page 1055 * @param rightId Right Id 1056 * @return true if the current user has the specified right on the current page 1057 */ 1058 public static boolean hasRightOnPage(String rightId) 1059 { 1060 Request request = ContextHelper.getRequest(_context); 1061 Page page = (Page) request.getAttribute(Page.class.getName()); 1062 return _hasRightOnPage(rightId, page); 1063 } 1064 1065 /** 1066 * Returns true if the current user has the specified right on the specified page 1067 * @param rightId Right Id 1068 * @param pageId Page Id 1069 * @return true if the current user has the specified right on the specified page 1070 */ 1071 public static boolean hasRightOnPage(String rightId, String pageId) 1072 { 1073 Page page = _getPage(pageId); 1074 return _hasRightOnPage(rightId, page); 1075 } 1076 1077 /** 1078 * Returns true if the current user has the specified right on the specified page 1079 * @param rightId Right Id 1080 * @param page Page 1081 * @return true if the current user has the specified right on the specified page 1082 */ 1083 private static boolean _hasRightOnPage(String rightId, Page page) 1084 { 1085 RightResult rightResult = _rightManager.currentUserHasRight(rightId, page); 1086 return rightResult == RightResult.RIGHT_ALLOW; 1087 } 1088 1089 /** 1090 * Returns true if the given page has restricted access. 1091 * @param sitename the site name 1092 * @param lang the page's language 1093 * @param path the page's path 1094 * @return true if the page exists and has restricted access. 1095 */ 1096 public static boolean pageHasRestrictedAccess(String sitename, String lang, String path) 1097 { 1098 try 1099 { 1100 Page page = _getPage(sitename, lang, path); 1101 return !_rightManager.hasAnonymousReadAccess(page); 1102 } 1103 catch (UnknownAmetysObjectException e) 1104 { 1105 _logger.error("Can not get page access info on page with id '" + sitename + "/" + lang + "/" + path + "'", e); 1106 return false; 1107 } 1108 } 1109 1110 /** 1111 * Returns the path of the current page, relative to the sitemap's root. 1112 * @return the path of the current Page, or empty if there's no current page. 1113 */ 1114 public static String pagePath() 1115 { 1116 Request request = ContextHelper.getRequest(_context); 1117 Page page = (Page) request.getAttribute(Page.class.getName()); 1118 1119 return page == null ? "" : page.getPathInSitemap(); 1120 } 1121 1122 /** 1123 * Returns the path in sitemap of a page 1124 * @param pageId The id of page 1125 * @return the path of the Page, or empty if not exists 1126 */ 1127 public static String pagePath(String pageId) 1128 { 1129 try 1130 { 1131 Page page = _getPage(pageId); 1132 return page.getPathInSitemap(); 1133 } 1134 catch (UnknownAmetysObjectException e) 1135 { 1136 _logger.error("Can not get title on page with id '" + pageId + "'", e); 1137 return ""; 1138 } 1139 } 1140 1141 /** 1142 * Returns the id of the current page. 1143 * @return the id of the current Page, or empty if there's no current page. 1144 */ 1145 public static String pageId() 1146 { 1147 Request request = ContextHelper.getRequest(_context); 1148 Page page = (Page) request.getAttribute(Page.class.getName()); 1149 1150 return page == null ? "" : page.getId(); 1151 } 1152 1153 /** 1154 * Returns the id of the current zone item id. 1155 * @return the id of the current zone item id, or empty if there's no current zone item. 1156 */ 1157 public static String zoneItemId() 1158 { 1159 Request request = ContextHelper.getRequest(_context); 1160 ZoneItem zoneItem = (ZoneItem) request.getAttribute(ZoneItem.class.getName()); 1161 1162 return zoneItem == null ? "" : StringUtils.defaultString(zoneItem.getId()); 1163 } 1164 1165 /** 1166 * Determines if the current zone item or (if there is no current zone item) the current page is cacheable 1167 * This method is only valid for a page. 1168 * @return true if the current zone item or page is cacheable. 1169 */ 1170 public static boolean isCacheable() 1171 { 1172 Request request = ContextHelper.getRequest(_context); 1173 if (request.getAttribute("IsZoneItemCacheable") != null) 1174 { 1175 return (Boolean) request.getAttribute("IsZoneItemCacheable"); 1176 } 1177 1178 // The method was called from the skin, out of a zone item 1179 Response response = ContextHelper.getResponse(_context); 1180 if (response.containsHeader("X-Ametys-Cacheable")) 1181 { 1182 return true; 1183 } 1184 return false; 1185 } 1186 1187 /** 1188 * Determines if we are in an edition mode 1189 * @return true if we are in edition mode 1190 */ 1191 public static boolean isEditionMode() 1192 { 1193 RenderingContext renderingContext = _renderingContextHandler.getRenderingContext(); 1194 Request request = ContextHelper.getRequest(_context); 1195 if (renderingContext == RenderingContext.FRONT && request.getParameter("_edition") != null && "true".equals(request.getParameter("_edition"))) 1196 { 1197 return true; 1198 } 1199 return false; 1200 } 1201 1202 /** 1203 * Returns the id of pages referencing the content and for which the Front-office user can access 1204 * @param contentId The content's id 1205 * @return The pages' id 1206 */ 1207 public static NodeList accessibleReferencedPages (String contentId) 1208 { 1209 RenderingContext renderingContext = _renderingContextHandler.getRenderingContext(); 1210 boolean inBackOffice = renderingContext == RenderingContext.BACK || renderingContext == RenderingContext.PREVIEW; 1211 1212 List<StringElement> pages = new ArrayList<>(); 1213 1214 Content content = _ametysObjectResolver.resolveById(contentId); 1215 if (content instanceof WebContent) 1216 { 1217 Collection<ZoneItem> zoneItems = ((WebContent) content).getReferencingZoneItems(); 1218 1219 for (ZoneItem zoneItem : zoneItems) 1220 { 1221 String metadataSetName = zoneItem.getMetadataSetName(); 1222 Page page = zoneItem.getZone().getPage(); 1223 1224 if (inBackOffice || _rightManager.hasReadAccess(_currentUserProvider.getUser(), page)) 1225 { 1226 Map<String, String> attrs = new HashMap<>(); 1227 attrs.put("id", page.getId()); 1228 attrs.put("metadataSetName", metadataSetName); 1229 pages.add(new StringElement("page", attrs)); 1230 } 1231 } 1232 } 1233 1234 return new AmetysNodeList(pages); 1235 } 1236 1237 /** 1238 * Returns the id of pages referencing the content 1239 * @param contentId The content's id 1240 * @return The pages' id 1241 */ 1242 public static NodeList referencedPages (String contentId) 1243 { 1244 List<StringElement> pages = new ArrayList<>(); 1245 1246 Content content = _ametysObjectResolver.resolveById(contentId); 1247 if (content instanceof WebContent) 1248 { 1249 Collection<ZoneItem> zoneItems = ((WebContent) content).getReferencingZoneItems(); 1250 1251 for (ZoneItem zoneItem : zoneItems) 1252 { 1253 String metadataSetName = zoneItem.getMetadataSetName(); 1254 Page page = zoneItem.getZone().getPage(); 1255 1256 Map<String, String> attrs = new HashMap<>(); 1257 attrs.put("id", page.getId()); 1258 attrs.put("metadataSetName", metadataSetName); 1259 pages.add(new StringElement("page", attrs)); 1260 } 1261 } 1262 1263 return new AmetysNodeList(pages); 1264 } 1265 1266 /** 1267 * Returns the ids of the pages 1268 * @param sitename The site id 1269 * @param lang The language code 1270 * @param tag The tag id 1271 * @return Array of pages ids 1272 */ 1273 public static NodeList findPagesIdsByTag(String sitename, String lang, String tag) 1274 { 1275 String xpath = PageQueryHelper.getPageXPathQuery(sitename, lang, null, new TagExpression(Operator.EQ, tag), null); 1276 AmetysObjectIterable<Page> pages = _ametysObjectResolver.query(xpath); 1277 Iterator<Page> it = pages.iterator(); 1278 1279 List<StringElement> list = new ArrayList<>(); 1280 while (it.hasNext()) 1281 { 1282 list.add(new StringElement("page", "id", it.next().getId())); 1283 } 1284 return new AmetysNodeList(list); 1285 } 1286 1287 /** 1288 * Returns the ids of the pages 1289 * @param tag The tag id 1290 * @return Array of pages ids 1291 */ 1292 public static NodeList findPagesIdsByTag(String tag) 1293 { 1294 Request request = ContextHelper.getRequest(_context); 1295 String siteName = (String) request.getAttribute("site"); 1296 1297 String lang = (String) request.getAttribute("sitemapLanguage"); 1298 if (lang == null) 1299 { 1300 // Try to get current language from content 1301 Content content = (Content) request.getAttribute(Content.class.getName()); 1302 if (content != null) 1303 { 1304 lang = content.getLanguage(); 1305 } 1306 } 1307 1308 return findPagesIdsByTag(siteName, lang, tag); 1309 } 1310}