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; 030import java.util.Set; 031 032import org.apache.avalon.framework.service.ServiceException; 033import org.apache.avalon.framework.service.ServiceManager; 034import org.apache.cocoon.components.ContextHelper; 035import org.apache.cocoon.environment.Request; 036import org.apache.cocoon.environment.Response; 037import org.apache.cocoon.xml.dom.DOMBuilder; 038import org.apache.commons.lang.StringUtils; 039import org.apache.excalibur.source.SourceResolver; 040import org.apache.excalibur.source.impl.FileSource; 041import org.w3c.dom.Element; 042import org.w3c.dom.Node; 043import org.w3c.dom.NodeList; 044import org.xml.sax.SAXException; 045 046import org.ametys.cms.repository.Content; 047import org.ametys.cms.repository.ContentQueryHelper; 048import org.ametys.cms.repository.LanguageExpression; 049import org.ametys.cms.transformation.ImageResolverHelper; 050import org.ametys.core.right.RightManager.RightResult; 051import org.ametys.core.user.User; 052import org.ametys.core.user.UserIdentity; 053import org.ametys.core.user.population.PopulationContextHelper; 054import org.ametys.core.util.I18nUtils; 055import org.ametys.core.util.dom.AmetysNodeList; 056import org.ametys.core.util.dom.EmptyElement; 057import org.ametys.core.util.dom.FileElement; 058import org.ametys.core.util.dom.MapElement; 059import org.ametys.core.util.dom.MapElement.MapNode; 060import org.ametys.core.util.dom.StringElement; 061import org.ametys.plugins.explorer.resources.Resource; 062import org.ametys.plugins.explorer.resources.ResourceCollection; 063import org.ametys.plugins.explorer.resources.dom.ResourceCollectionElement; 064import org.ametys.plugins.explorer.resources.dom.ResourceElement; 065import org.ametys.plugins.repository.AmetysObject; 066import org.ametys.plugins.repository.AmetysObjectIterable; 067import org.ametys.plugins.repository.AmetysObjectResolver; 068import org.ametys.plugins.repository.UnknownAmetysObjectException; 069import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 070import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeater; 071import org.ametys.plugins.repository.data.holder.group.impl.ModelAwareRepeaterEntry; 072import org.ametys.plugins.repository.data.holder.impl.DataHolderHelper; 073import org.ametys.plugins.repository.model.RepeaterDefinition; 074import org.ametys.plugins.repository.query.expression.AndExpression; 075import org.ametys.plugins.repository.query.expression.Expression; 076import org.ametys.plugins.repository.query.expression.Expression.Operator; 077import org.ametys.plugins.repository.query.expression.MetadataExpression; 078import org.ametys.plugins.repository.query.expression.NotExpression; 079import org.ametys.plugins.repository.query.expression.StringExpression; 080import org.ametys.runtime.model.ElementDefinition; 081import org.ametys.runtime.model.ModelItem; 082import org.ametys.runtime.model.type.ElementType; 083import org.ametys.runtime.model.type.ModelItemType; 084import org.ametys.web.URIPrefixHandler; 085import org.ametys.web.WebConstants; 086import org.ametys.web.renderingcontext.RenderingContext; 087import org.ametys.web.renderingcontext.RenderingContextHandler; 088import org.ametys.web.repository.content.WebContent; 089import org.ametys.web.repository.dom.PageElement; 090import org.ametys.web.repository.dom.SitemapElement; 091import org.ametys.web.repository.page.Page; 092import org.ametys.web.repository.page.PageQueryHelper; 093import org.ametys.web.repository.page.Zone; 094import org.ametys.web.repository.page.ZoneItem; 095import org.ametys.web.repository.site.Site; 096import org.ametys.web.repository.site.SiteManager; 097import org.ametys.web.repository.sitemap.Sitemap; 098import org.ametys.web.service.Service; 099import org.ametys.web.service.ServiceExtensionPoint; 100import org.ametys.web.tags.TagExpression; 101import org.ametys.web.url.UrlPreview; 102import org.ametys.web.url.UrlPreviewComponent; 103 104/** 105 * Helper component to be used from XSL stylesheets. 106 */ 107public class AmetysXSLTHelper extends org.ametys.cms.transformation.xslt.AmetysXSLTHelper 108{ 109 static final int _PAGINATION_CURRENT = -1; 110 static final int _PAGINATION_SEPARATOR = -2; 111 static final int _PAGINATION_SPACE = -3; 112 113 private static final String __NAME_ATTRIBUTE = "name"; 114 private static final String __TYPE_ATTRIBUTE = "type"; 115 private static final String __REPEATER_ENTRY_TYPE = RepeaterDefinition.TYPE + "_entry"; 116 117 private static SiteManager _siteManager; 118 private static RenderingContextHandler _renderingContextHandler; 119 private static URIPrefixHandler _prefixHandler; 120 private static SourceResolver _sourceResolver; 121 private static ServiceExtensionPoint _serviceEP; 122 private static PopulationContextHelper _populationContextHelper; 123 private static UrlPreviewComponent _urlPreview; 124 125 @Override 126 public void service(ServiceManager manager) throws ServiceException 127 { 128 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 129 _renderingContextHandler = (RenderingContextHandler) manager.lookup(RenderingContextHandler.ROLE); 130 _ametysObjectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 131 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 132 _prefixHandler = (URIPrefixHandler) manager.lookup(URIPrefixHandler.ROLE); 133 _sourceResolver = (SourceResolver) manager.lookup(SourceResolver.ROLE); 134 _serviceEP = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 135 _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE); 136 _urlPreview = (UrlPreviewComponent) manager.lookup(UrlPreviewComponent.ROLE); 137 } 138 139 /** 140 * Returns the current URI prefix, depending on the rendering context. 141 * @return the current URI prefix. 142 */ 143 public static String uriPrefix() 144 { 145 return _prefixHandler.getUriPrefix(); 146 } 147 148 /** 149 * Returns the URI prefix corresponding to the current site, depending on the rendering context. 150 * @return the URI prefix corresponding to the current site. 151 */ 152 public static String siteUriPrefix() 153 { 154 Request request = ContextHelper.getRequest(_context); 155 String siteName = (String) request.getAttribute("site"); 156 return _prefixHandler.getUriPrefix(siteName); 157 } 158 159 /** 160 * Returns the absolute URI prefix, depending on the rendering context. 161 * @return the absolute URI prefix. 162 */ 163 public static String absoluteUriPrefix() 164 { 165 return _prefixHandler.getAbsoluteUriPrefix(); 166 } 167 168 /** 169 * Returns the absolute URI prefix corresponding to the current site, depending on the rendering context. 170 * @return the absolute URI prefix corresponding to the current site. 171 */ 172 public static String absoluteSiteUriPrefix() 173 { 174 Request request = ContextHelper.getRequest(_context); 175 String siteName = (String) request.getAttribute("site"); 176 return _prefixHandler.getAbsoluteUriPrefix(siteName); 177 } 178 179 /** 180 * Returns the absolute URI prefix corresponding to the given site, depending on the rendering context. 181 * @param siteName The site name. Can be null to get the current site. 182 * @return the absolute URI prefix corresponding to the current site. 183 */ 184 public static String absoluteSiteUriPrefix(String siteName) 185 { 186 if (StringUtils.isEmpty(siteName)) 187 { 188 return absoluteSiteUriPrefix(); 189 } 190 return _prefixHandler.getAbsoluteUriPrefix(siteName); 191 } 192 193 /** 194 * Returns the current skin name. 195 * @return the current skin name. 196 */ 197 public static String skin() 198 { 199 Request request = ContextHelper.getRequest(_context); 200 return (String) request.getAttribute("skin"); 201 } 202 203 /** 204 * Returns the current template name. 205 * @return the current template name. 206 */ 207 public static String template() 208 { 209 Request request = ContextHelper.getRequest(_context); 210 return (String) request.getAttribute("template"); 211 } 212 213 /** 214 * Returns the current sitemap name. 215 * @return the current sitemap name. 216 */ 217 public static String lang() 218 { 219 Request request = ContextHelper.getRequest(_context); 220 return (String) request.getAttribute("sitemapLanguage"); 221 } 222 223 /** 224 * Returns the current sitemap name. 225 * @param pageId The page identifier to get sitemap on 226 * @return the current sitemap name. 227 */ 228 public static String lang(String pageId) 229 { 230 try 231 { 232 Page page = _getPage(pageId); 233 return page.getSitemapName(); 234 } 235 catch (UnknownAmetysObjectException e) 236 { 237 _logger.error("Can not get sitemap lang on page '" + pageId + "'", e); 238 return ""; 239 } 240 } 241 242 /** 243 * Computes the URI for the given resource in the current site's skin.<br> 244 * If the URI is requested by the front-office, it will be absolutized. 245 * @param path the resource path. 246 * @return the URI for the given resource. 247 */ 248 public static String skinURL(String path) 249 { 250 Request request = ContextHelper.getRequest(_context); 251 String siteName = (String) request.getAttribute("site"); 252 Site site = _siteManager.getSite(siteName); 253 String skin = (String) request.getAttribute("skin"); 254 255 String resourcePath = "/skins/" + skin + "/resources/" + path; 256 257 return _getResourceURL(request, site, resourcePath); 258 } 259 260 /** 261 * Computes the URI for the given image with a given heigth and width in the current site's skin.<br> 262 * If the URI is requested by the front-office, it will be absolutized. 263 * @param path the resource path 264 * @param height the height for the resource to get 265 * @param width the width for the resource to get 266 * @return the URI of the given resource 267 */ 268 public static String skinImageURL(String path, int height, int width) 269 { 270 String skinPath = skinURL(path); 271 return StringUtils.substringBeforeLast(skinPath, ".") + "_" + height + "x" + width + "." + StringUtils.substringAfterLast(skinPath, "."); 272 } 273 274 /** 275 * Computes the base 64 representation of the image at the specified path. <br> 276 * @param path the path of the image 277 * @return the base 64-encoded image 278 * @throws IOException if an error occurs while trying to get the file 279 */ 280 public static String skinImageBase64 (String path) throws IOException 281 { 282 FileSource source = (FileSource) _sourceResolver.resolveURI("skin://resources/" + path); 283 return _getResourceBase64(source); 284 } 285 286 /** 287 * Computes the URI for the given image with a given heigth and width in the current site's skin.<br> 288 * If the URI is requested by the front-office, it will be absolutized. 289 * @param path the resource path 290 * @param maxHeight the maximum height for the resource to get 291 * @param maxWidth the maximum width for the resource to get 292 * @return the URI of the given resource 293 */ 294 public static String skinBoundedImageURL(String path, int maxHeight, int maxWidth) 295 { 296 String skinPath = skinURL(path); 297 return StringUtils.substringBeforeLast(skinPath, ".") + "_max" + maxHeight + "x" + maxWidth + "." + StringUtils.substringAfterLast(skinPath, "."); 298 } 299 300 /** 301 * Computes the URI for the given resource in the current template.<br> 302 * If the URI is requested by the front-office, it will be absolutized. 303 * @param path the resource path. 304 * @return the URI for the given resource. 305 */ 306 public static String templateURL(String path) 307 { 308 Request request = ContextHelper.getRequest(_context); 309 String siteName = (String) request.getAttribute("site"); 310 Site site = _siteManager.getSite(siteName); 311 String skin = (String) request.getAttribute("skin"); 312 String template = (String) request.getAttribute("template"); 313 314 String resourcePath = "/skins/" + skin + "/templates/" + template + "/resources/" + path; 315 316 return _getResourceURL(request, site, resourcePath); 317 } 318 319 /** 320 * Computes the URI for the given resource in the given plugin.<br> 321 * If the URI is requested by the front-office, it will be absolutized. 322 * @param plugin the plugin name. 323 * @param path the resource path. 324 * @return the URI for the given resource. 325 */ 326 public static String pluginResourceURL(String plugin, String path) 327 { 328 Request request = ContextHelper.getRequest(_context); 329 String siteName = (String) request.getAttribute("site"); 330 Site site = _siteManager.getSite(siteName); 331 332 String resourcePath = "/plugins/" + plugin + "/resources/" + path; 333 334 return _getResourceURL(request, site, resourcePath); 335 } 336 337 /** 338 * Computes the base 64 representation of the image at the specified path in the given plugin.<br> 339 * @param plugin the plugin's name. 340 * @param path the resource path. 341 * @return the base 64 encoding for the given resource. 342 * @throws IOException if an error occurs when trying to get the file 343 * @throws MalformedURLException if the url is invalid 344 */ 345 public static String pluginImageBase64(String plugin, String path) throws MalformedURLException, IOException 346 { 347 FileSource source = (FileSource) _sourceResolver.resolveURI("plugin:" + plugin + "://resources/" + path); 348 return _getResourceBase64(source); 349 } 350 351 352 private static String _getResourceURL(Request request, Site site, String resourcePath) 353 { 354 String prefix; 355 switch (_renderingContextHandler.getRenderingContext()) 356 { 357 case FRONT: 358 String[] aliases = site.getUrlAliases(); 359 int position = Math.abs(resourcePath.hashCode()) % aliases.length; 360 361 boolean absolute = request.getAttribute("forceAbsoluteUrl") != null ? (Boolean) request.getAttribute("forceAbsoluteUrl") : false; 362 prefix = position == 0 && !absolute ? siteUriPrefix() : aliases[position]; 363 return prefix + resourcePath; 364 365 default: 366 prefix = StringUtils.trimToEmpty((String) request.getAttribute(WebConstants.PATH_PREFIX)); 367 return request.getContextPath() + prefix + resourcePath; 368 } 369 } 370 371 /** 372 * Get the base 64 encoding for the given source 373 * @param source the source 374 * @return the base 64 encoding of the source 375 */ 376 private static String _getResourceBase64(FileSource source) 377 { 378 if (source.exists()) 379 { 380 381 try (InputStream dataIs = source.getInputStream()) 382 { 383 return ImageResolverHelper.resolveImageAsBase64(dataIs, source.getMimeType(), 0, 0, 0, 0); 384 } 385 catch (Exception e) 386 { 387 throw new IllegalStateException(e); 388 } 389 } 390 391 return ""; 392 } 393 394 /** 395 * Returns the current {@link RenderingContext}. 396 * @return the current {@link RenderingContext}. 397 */ 398 public static String renderingContext() 399 { 400 return _renderingContextHandler.getRenderingContext().toString(); 401 } 402 403 /** 404 * Return the name of the zone beeing handled 405 * @param defaultValue If no page is handled currently, this value is returned (can be null, empty...) 406 * @return the name or the default value (so can be null or empty) 407 */ 408 public static String zone(String defaultValue) 409 { 410 Request request = ContextHelper.getRequest(_context); 411 412 return StringUtils.defaultIfEmpty((String) request.getAttribute(Zone.class.getName()), defaultValue); 413 } 414 415 /** 416 * Return the value of a site parameter as a String. 417 * @param parameter the parameter ID. 418 * @return the parameter value as a String. 419 */ 420 public static String siteParameter(String parameter) 421 { 422 Request request = ContextHelper.getRequest(_context); 423 424 String siteName = (String) request.getAttribute("site"); 425 if (StringUtils.isBlank(siteName)) 426 { 427 // In BO xsl 428 siteName = (String) request.getAttribute("siteName"); 429 } 430 431 return siteParameter(siteName, parameter); 432 } 433 434 /** 435 * Return the value of a site parameter as a String. 436 * @param siteName the site name 437 * @param parameter the parameter ID. 438 * @return the parameter value as a String. 439 */ 440 public static String siteParameter(String siteName, String parameter) 441 { 442 try 443 { 444 Site site = _siteManager.getSite(siteName); 445 Object value = site.getValue(parameter); 446 if (value != null) 447 { 448 return value.toString(); 449 } 450 else 451 { 452 return null; 453 } 454 } 455 catch (Exception e) 456 { 457 String message = "Error retrieving the value of the site parameter " + parameter; 458 _logger.error(message, e); 459 throw new RuntimeException(message, e); 460 } 461 } 462 463 /** 464 * Get the service parameters as a {@link Node}. 465 * @return the service parameters as a {@link Node}. 466 */ 467 public static Node serviceParameters() 468 { 469 Request request = ContextHelper.getRequest(_context); 470 ZoneItem zoneItem = (ZoneItem) request.getAttribute(WebConstants.REQUEST_ATTR_ZONEITEM); 471 return _serviceParameters(zoneItem); 472 } 473 474 static Node _serviceParameters(ZoneItem zoneItem) 475 { 476 Map<String, MapNode> values = new HashMap<>(); 477 478 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 479 String serviceId = zoneItem.getServiceId(); 480 Service service = _serviceEP.getExtension(serviceId); 481 482 for (String parameterName : dataHolder.getDataNames()) 483 { 484 if (service.hasModelItem(parameterName) && dataHolder.hasValue(parameterName)) 485 { 486 ModelItem modelItem = service.getModelItem(parameterName); 487 values.putAll(_getParameterValue(parameterName, modelItem, dataHolder, null)); 488 } 489 } 490 491 return new MapElement("serviceParameters", values); 492 } 493 494 /** 495 * Returns the value of the given parameter for the current service, or the empty string if the parameter does not exist. 496 * @param parameterPath the parameter path. 497 * @return the value of the given parameter for the current service. 498 */ 499 public static Node serviceParameter(String parameterPath) 500 { 501 return serviceParameter(parameterPath, ""); 502 } 503 504 /** 505 * Returns the value of the given parameter for the current service, or the provided default value if the parameter does not exist. 506 * @param parameterPath the parameter path. 507 * @param defaultValue the default value. Note that default value is ignored if the parameter is a composite parameter. 508 * @return the value of the given parameter for the current service. 509 */ 510 public static Node serviceParameter(String parameterPath, Object defaultValue) 511 { 512 Request request = ContextHelper.getRequest(_context); 513 ZoneItem zoneItem = (ZoneItem) request.getAttribute(WebConstants.REQUEST_ATTR_ZONEITEM); 514 return _serviceParameter(zoneItem, parameterPath, defaultValue); 515 } 516 517 static Node _serviceParameter(ZoneItem zoneItem, String parameterPath, Object defaultValue) 518 { 519 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 520 521 String serviceId = zoneItem.getServiceId(); 522 String definitionPath = DataHolderHelper.getDefinitionPathFromDataPath(parameterPath); 523 524 Service service = _serviceEP.getExtension(serviceId); 525 ModelItem paramDef; 526 if (service.hasModelItem(definitionPath)) 527 { 528 paramDef = service.getModelItem(definitionPath); 529 } 530 else 531 { 532 // The parameter is unknown 533 if (defaultValue == null || (defaultValue instanceof String && StringUtils.isEmpty((String) defaultValue))) 534 { 535 return null; 536 } 537 else 538 { 539 return new StringElement(parameterPath, Collections.EMPTY_MAP, defaultValue.toString()); 540 } 541 } 542 543 Map<String, MapNode> value = _getParameterValue(parameterPath, paramDef, dataHolder, defaultValue); 544 545 if (!value.containsKey(parameterPath)) 546 { 547 return null; 548 } 549 else if (paramDef instanceof RepeaterDefinition || (paramDef instanceof ElementDefinition && ((ElementDefinition) paramDef).isMultiple())) 550 { 551 MapNode node = value.get(parameterPath); 552 @SuppressWarnings("unchecked") 553 Map<String, ? extends Object> values = (Map<String, ? extends Object>) node.getValue(); 554 return new MapElement(parameterPath, node.getAttributes(), values); 555 } 556 else 557 { 558 return new StringElement(parameterPath, value.get(parameterPath).getAttributes(), (String) value.get(parameterPath).getValue()); 559 } 560 } 561 562 private static String _convertTagName(String name) 563 { 564 char c = name.charAt(0); 565 if (c >= '0' && c <= '9') 566 { 567 String hex = Integer.toHexString(c); 568 return "_x" + StringUtils.leftPad(hex, 4, '0') + "_" + name.substring(1); 569 } 570 else 571 { 572 return name; 573 } 574 } 575 576 @SuppressWarnings("unchecked") 577 private static Map<String, MapNode> _getParameterValue(String parameterPath, ModelItem modelItem, ModelAwareDataHolder dataHolder, Object defaultValue) 578 { 579 String[] pathSegments = StringUtils.split(parameterPath, ModelItem.ITEM_PATH_SEPARATOR); 580 String parameterName = pathSegments[pathSegments.length - 1]; 581 Map<String, MapNode> paramValues = new HashMap<>(); 582 583 if (modelItem instanceof RepeaterDefinition) 584 { 585 if (!dataHolder.hasValue(parameterPath)) 586 { 587 return paramValues; 588 } 589 590 Map<String, String> attributes = new HashMap<>(); 591 attributes.put(__NAME_ATTRIBUTE, parameterName); 592 attributes.put(__TYPE_ATTRIBUTE, RepeaterDefinition.TYPE); 593 594 Map<String, Object> children = new HashMap<>(); 595 596 ModelAwareRepeater repeater = dataHolder.getRepeater(parameterPath); 597 for (ModelAwareRepeaterEntry entry : repeater.getEntries()) 598 { 599 Map<String, Object> entryValue = new HashMap<>(); 600 601 for (ModelItem childModelItem : ((RepeaterDefinition) modelItem).getChildren()) 602 { 603 // Default value is ignored if parameter is a repeater 604 Map<String, MapNode> childParamValues = _getParameterValue(childModelItem.getName(), childModelItem, entry, null); 605 entryValue.putAll(childParamValues); 606 } 607 608 Map<String, String> entryAttributes = new HashMap<>(); 609 entryAttributes.put(__NAME_ATTRIBUTE, String.valueOf(entry.getPosition())); 610 entryAttributes.put(__TYPE_ATTRIBUTE, __REPEATER_ENTRY_TYPE); 611 612 MapNode entryNode = new MapNode(entryValue, entryAttributes); 613 children.put(_convertTagName(String.valueOf(entry.getPosition())), entryNode); 614 } 615 616 MapNode node = new MapNode(children, attributes); 617 paramValues.put(parameterPath, node); 618 } 619 else 620 { 621 if (!dataHolder.hasValue(parameterPath) && (defaultValue == null || (defaultValue instanceof String && StringUtils.isEmpty((String) defaultValue)))) 622 { 623 return paramValues; 624 } 625 626 Map<String, String> attributes = new HashMap<>(); 627 attributes.put(__NAME_ATTRIBUTE, parameterName); 628 ElementDefinition elementDefinition = (ElementDefinition) modelItem; 629 ElementType elementType = elementDefinition.getType(); 630 attributes.put(__TYPE_ATTRIBUTE, elementType.getId()); 631 632 if (elementDefinition.isMultiple()) 633 { 634 Map<String, List<String>> values = new HashMap<>(); 635 List<String> value = new ArrayList<>(); 636 Object[] valueArray = (Object[]) dataHolder.getValue(parameterPath, false, defaultValue); 637 for (Object arrayItem : valueArray) 638 { 639 value.add(elementType.toString(arrayItem)); 640 } 641 values.put("value", value); 642 643 MapNode node = new MapNode(values, attributes); 644 paramValues.put(parameterPath, node); 645 } 646 else 647 { 648 649 String value = elementType.toString(dataHolder.getValue(parameterPath, false, defaultValue)); 650 MapNode node = new MapNode(value, attributes); 651 paramValues.put(parameterPath, node); 652 } 653 } 654 655 return paramValues; 656 } 657 658 /** 659 * Returns the current site 660 * @return the current site 661 */ 662 public static String site() 663 { 664 Request request = ContextHelper.getRequest(_context); 665 return (String) request.getAttribute("site"); 666 } 667 668 /** 669 * Returns the current site 670 * @param pageId The identifier ot the page 671 * @return the current site 672 */ 673 public static String site(String pageId) 674 { 675 try 676 { 677 Page page = _getPage(pageId); 678 return page.getSiteName(); 679 } 680 catch (UnknownAmetysObjectException e) 681 { 682 _logger.error("Can not get site on page '" + pageId + "'", e); 683 return ""; 684 } 685 } 686 687 /** 688 * Return the current sitemap as a {@link Node}. 689 * Invisible pages will not be displayed 690 * @return the current sitemap. 691 */ 692 public static Node sitemap() 693 { 694 return sitemap(false); 695 } 696 697 /** 698 * Return the current sitemap as a {@link Node}. 699 * @param includeInvisiblePages Should return child invisible pages 700 * @return the current sitemap. 701 */ 702 public static Node sitemap(boolean includeInvisiblePages) 703 { 704 Request request = ContextHelper.getRequest(_context); 705 Sitemap sitemap = (Sitemap) request.getAttribute(Sitemap.class.getName()); 706 707 if (sitemap == null) 708 { 709 // Try to get sitemap from content 710 Content content = (Content) request.getAttribute(Content.class.getName()); 711 if (content instanceof WebContent) 712 { 713 sitemap = ((WebContent) content).getSite().getSitemap(content.getLanguage()); 714 } 715 } 716 717 if (sitemap == null) 718 { 719 return new EmptyElement("sitemap"); 720 } 721 722 Page page = (Page) request.getAttribute(Page.class.getName()); 723 724 return new SitemapElement(sitemap, page != null ? page.getPathInSitemap() : null, _rightManager, _renderingContextHandler, _currentUserProvider.getUser(), includeInvisiblePages); 725 } 726 727 /** 728 * Return the subsitemap of the given page as a {@link Node}. 729 * Invisible child pages will not be returned; 730 * @param pageId The root page 731 * @return The page as node. 732 */ 733 public static Node sitemap(String pageId) 734 { 735 return sitemap(pageId, false); 736 } 737 738 /** 739 * Return the subsitemap of the given page as a {@link Node}. 740 * @param pageId The root page 741 * @param includeInvisiblePages Should return child invisible pages 742 * @return The page as node. 743 */ 744 public static Node sitemap(String pageId, boolean includeInvisiblePages) 745 { 746 Page rootPage = null; 747 try 748 { 749 rootPage = _ametysObjectResolver.resolveById(pageId); 750 } 751 catch (UnknownAmetysObjectException e) 752 { 753 return new EmptyElement("page"); 754 } 755 756 Request request = ContextHelper.getRequest(_context); 757 Page page = (Page) request.getAttribute(Page.class.getName()); 758 759 return new PageElement(rootPage, _rightManager, _renderingContextHandler, page != null ? page.getPathInSitemap() : null, _currentUserProvider.getUser(), includeInvisiblePages); 760 } 761 762 /** 763 * Computes the breadcrumb of the current page. 764 * @return a NodeList containing all ancestor pages, rooted at the sitemap. 765 */ 766 public static NodeList breadcrumb() 767 { 768 Request request = ContextHelper.getRequest(_context); 769 Page page = (Page) request.getAttribute(Page.class.getName()); 770 771 List<Element> result = new ArrayList<>(); 772 773 AmetysObject parent = page.getParent(); 774 while (parent instanceof Page) 775 { 776 Element node = new StringElement("page", (Map<String, String>) null, parent.getId()); 777 result.add(node); 778 parent = parent.getParent(); 779 } 780 781 Collections.reverse(result); 782 return new AmetysNodeList(result); 783 } 784 785 /** 786 * Returns a DOM {@link Element} representing files and folder of the resources explorer, 787 * under the {@link ResourceCollection} corresponding to the given id. 788 * @param collectionId the id of the root {@link ResourceCollection}. 789 * @return an Element containing files and folders. 790 */ 791 public static Node resourcesById(String collectionId) 792 { 793 ResourceCollection collection = _ametysObjectResolver.resolveById(collectionId); 794 return new ResourceCollectionElement(collection); 795 } 796 797 /** 798 * Returns a DOM {@link Element} representing files and folder of the resources explorer, 799 * under the {@link ResourceCollection} corresponding to the given path. <br> 800 * This path is intended to be relative to the current site's resource explorer. 801 * @param path the path of the root {@link ResourceCollection}, relative to the current site's resource explorer. 802 * @return an Element containing files and folders or null if the specified resource does not exist. 803 */ 804 public static Node resourcesByPath(String path) 805 { 806 Request request = ContextHelper.getRequest(_context); 807 String siteName = (String) request.getAttribute("site"); 808 Site site = _siteManager.getSite(siteName); 809 810 try 811 { 812 ResourceCollection collection = site.getRootResources().getChild(path); 813 return new ResourceCollectionElement(collection); 814 } 815 catch (UnknownAmetysObjectException ex) 816 { 817 return null; 818 } 819 } 820 821 /** 822 * Returns a DOM {@link Element} representing a single file of the resources explorer. <br> 823 * This path is intended to be relative to the current site's resource explorer. 824 * @param path the path of the {@link Resource}, relative to the current site's resource explorer. 825 * @return an Element containing a file or null if the specified resource does not exist. 826 */ 827 public static Node resourceByPath(String path) 828 { 829 Request request = ContextHelper.getRequest(_context); 830 String siteName = (String) request.getAttribute("site"); 831 Site site = _siteManager.getSite(siteName); 832 833 try 834 { 835 Resource resource = site.getRootResources().getChild(path); 836 return new ResourceElement(resource, null); 837 } 838 catch (UnknownAmetysObjectException ex) 839 { 840 return null; 841 } 842 } 843 844 /** 845 * Returns a DOM {@link Element} representing files and folder of a skin directory. <br> 846 * This path is intended to be relative to the current skin's 'resources' directory. 847 * @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. 848 * @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. 849 * @throws IOException if an error occured while listing files. 850 */ 851 public static Node skinResources(String path) throws IOException 852 { 853 FileSource source = (FileSource) _sourceResolver.resolveURI("skin://resources/" + path); 854 if (source.exists()) 855 { 856 return new FileElement(source.getFile()); 857 } 858 else 859 { 860 return null; 861 } 862 } 863 864 //************************* 865 // Page methods 866 //************************* 867 868 /** 869 * Get the site name of a page. 870 * @param pageId The page id. 871 * @return The name or empty if the page does not exist. 872 */ 873 public static String pageSiteName(String pageId) 874 { 875 try 876 { 877 Page page = _getPage(pageId); 878 return page.getSiteName(); 879 } 880 catch (UnknownAmetysObjectException e) 881 { 882 _logger.error("Can not get site name on page with id '" + pageId + "'", e); 883 return ""; 884 } 885 } 886 887 /** 888 * Get the title of a page. 889 * @param sitename the site name. 890 * @param lang the sitemap name. 891 * @param path the page path. 892 * @return The name or empty if the meta or the page does not exist. 893 */ 894 public static String pageTitle(String sitename, String lang, String path) 895 { 896 try 897 { 898 Page page = _getPage(sitename, lang, path); 899 return page.getTitle(); 900 } 901 catch (UnknownAmetysObjectException e) 902 { 903 _logger.warn("Unknown page at path '" + sitename + "/" + lang + "/" + path + "'", e); 904 return ""; 905 } 906 } 907 908 /** 909 * Determines if page exists 910 * @param sitename the site name. 911 * @param lang the sitemap name. 912 * @param path the page path. 913 * @return <code>false</code> the page does not exist. 914 */ 915 public static boolean pageExists(String sitename, String lang, String path) 916 { 917 try 918 { 919 _getPage(sitename, lang, path); 920 return true; 921 } 922 catch (UnknownAmetysObjectException e) 923 { 924 _logger.debug("Page at path '" + sitename + "/" + lang + "/" + path + "' does not exists", e); 925 return false; 926 } 927 } 928 929 /** 930 * Get the title of a page. 931 * @param pageId The page id. 932 * @return The name or empty if the meta or the page does not exist. 933 */ 934 public static String pageTitle(String pageId) 935 { 936 try 937 { 938 Page page = _getPage(pageId); 939 return page.getTitle(); 940 } 941 catch (UnknownAmetysObjectException e) 942 { 943 _logger.error("Can not get title on page with id '" + pageId + "'", e); 944 return ""; 945 } 946 } 947 948 /** 949 * Get the long title of a page 950 * @param sitename the site name 951 * @param lang the page's language 952 * @param path the page's path 953 * @return The name or empty if the meta or the page does not exist 954 */ 955 public static String pageLongTitle(String sitename, String lang, String path) 956 { 957 try 958 { 959 Page page = _getPage(sitename, lang, path); 960 return page.getLongTitle(); 961 } 962 catch (UnknownAmetysObjectException e) 963 { 964 _logger.error("Can not get long title on page '" + sitename + "/" + lang + "/" + path + "'", e); 965 return ""; 966 } 967 } 968 /** 969 * Get the long title of a page 970 * @param pageId The page id 971 * @return The name or empty if the meta or the page does not exist 972 */ 973 public static String pageLongTitle(String pageId) 974 { 975 try 976 { 977 Page page = _getPage(pageId); 978 return page.getLongTitle(); 979 } 980 catch (UnknownAmetysObjectException e) 981 { 982 _logger.error("Can not get long title on page with id '" + pageId + "'", e); 983 return ""; 984 } 985 } 986 987 /** 988 * Get the data of a page at the given path 989 * @param sitename the site name 990 * @param lang the page's language 991 * @param path the page's path 992 * @param dataPath The data path (use '/' as separator for composites and repeaters) 993 * @return The value or empty if the data or the page does not exist 994 */ 995 public static String pageMetadata(String sitename, String lang, String path, String dataPath) 996 { 997 try 998 { 999 Page page = _getPage(sitename, lang, path); 1000 return _getPageData(page, dataPath); 1001 } 1002 catch (UnknownAmetysObjectException e) 1003 { 1004 _logger.error("Can not get data at path '" + dataPath + "' on page '" + sitename + "/" + lang + "/" + path + "'", e); 1005 return StringUtils.EMPTY; 1006 } 1007 } 1008 1009 /** 1010 * Get the data of a page at the given path 1011 * @param pageId The page id 1012 * @param dataPath The data path (use '/' as separator for composites and repeaters) 1013 * @return The value or empty if the data or the page does not exist 1014 */ 1015 public static String pageMetadata(String pageId, String dataPath) 1016 { 1017 try 1018 { 1019 Page page = _getPage(pageId); 1020 return _getPageData(page, dataPath); 1021 } 1022 catch (UnknownAmetysObjectException e) 1023 { 1024 _logger.error("Can not get data at path '" + dataPath + "' on page with id '" + pageId + "'", e); 1025 return StringUtils.EMPTY; 1026 } 1027 } 1028 1029 @SuppressWarnings("unchecked") 1030 private static String _getPageData(Page page, String dataPath) 1031 { 1032 try 1033 { 1034 Object value = page.getValue(dataPath); 1035 ModelItemType type = page.getType(dataPath); 1036 if (value != null && type instanceof ElementType) 1037 { 1038 return ((ElementType) type).toString(value); 1039 } 1040 else 1041 { 1042 _logger.error("Can not get data at path '" + dataPath + "' on page with id '" + page.getId() + "'"); 1043 return StringUtils.EMPTY; 1044 } 1045 } 1046 catch (Exception e) 1047 { 1048 _logger.error("Can not get data at path '" + dataPath + "' on page with id '" + page.getId() + "'", e); 1049 return StringUtils.EMPTY; 1050 } 1051 } 1052 1053 /** 1054 * Returns true if the given page is visible into navigation elements 1055 * @param pageId the page id. 1056 * @return true if the page is visible 1057 */ 1058 public static boolean pageIsVisible (String pageId) 1059 { 1060 try 1061 { 1062 Page page = _getPage(pageId); 1063 return page.isVisible(); 1064 } 1065 catch (UnknownAmetysObjectException e) 1066 { 1067 _logger.error("Can not get visibility status on page with id '" + pageId + "'", e); 1068 return false; 1069 } 1070 } 1071 1072 /** 1073 * Returns true if the given page is visible into navigation elements 1074 * @param sitename the site name 1075 * @param lang the page's language 1076 * @param path the page's path 1077 * @return true if the page is visible 1078 */ 1079 public static boolean pageIsVisible (String sitename, String lang, String path) 1080 { 1081 try 1082 { 1083 Page page = _getPage(sitename, lang, path); 1084 return page.isVisible(); 1085 } 1086 catch (UnknownAmetysObjectException e) 1087 { 1088 _logger.error("Can not get visibility status on page with id '" + sitename + "/" + lang + "/" + path + "'", e); 1089 return false; 1090 } 1091 } 1092 1093 /** 1094 * Returns true if the given page has restricted access. 1095 * @param pageId the page id. 1096 * @return true if the page exists and has restricted access. 1097 */ 1098 public static boolean pageHasRestrictedAccess(String pageId) 1099 { 1100 try 1101 { 1102 Page page = _getPage(pageId); 1103 return !_rightManager.hasAnonymousReadAccess(page); 1104 } 1105 catch (UnknownAmetysObjectException e) 1106 { 1107 _logger.error("Can not get page access info on page with id '" + pageId + "'", e); 1108 return false; 1109 } 1110 } 1111 1112 /** 1113 * Returns true if the current user has the specified right on the current page 1114 * @param rightId Right Id 1115 * @return true if the current user has the specified right on the current page 1116 */ 1117 public static boolean hasRightOnPage(String rightId) 1118 { 1119 Request request = ContextHelper.getRequest(_context); 1120 Page page = (Page) request.getAttribute(Page.class.getName()); 1121 return _hasRightOnPage(rightId, page); 1122 } 1123 1124 /** 1125 * Returns true if the current user has the specified right on the specified page 1126 * @param rightId Right Id 1127 * @param pageId Page Id 1128 * @return true if the current user has the specified right on the specified page 1129 */ 1130 public static boolean hasRightOnPage(String rightId, String pageId) 1131 { 1132 Page page = _getPage(pageId); 1133 return _hasRightOnPage(rightId, page); 1134 } 1135 1136 /** 1137 * Returns true if the current user has the specified right on the specified page 1138 * @param rightId Right Id 1139 * @param page Page 1140 * @return true if the current user has the specified right on the specified page 1141 */ 1142 private static boolean _hasRightOnPage(String rightId, Page page) 1143 { 1144 RightResult rightResult = _rightManager.currentUserHasRight(rightId, page); 1145 return rightResult == RightResult.RIGHT_ALLOW; 1146 } 1147 1148 /** 1149 * Returns true if the given page has restricted access. 1150 * @param sitename the site name 1151 * @param lang the page's language 1152 * @param path the page's path 1153 * @return true if the page exists and has restricted access. 1154 */ 1155 public static boolean pageHasRestrictedAccess(String sitename, String lang, String path) 1156 { 1157 try 1158 { 1159 Page page = _getPage(sitename, lang, path); 1160 return !_rightManager.hasAnonymousReadAccess(page); 1161 } 1162 catch (UnknownAmetysObjectException e) 1163 { 1164 _logger.error("Can not get page access info on page with id '" + sitename + "/" + lang + "/" + path + "'", e); 1165 return false; 1166 } 1167 } 1168 1169 private static Page _getPage(String id) 1170 { 1171 return _ametysObjectResolver.resolveById(id); 1172 } 1173 1174 private static Page _getPage(String sitename, String lang, String path) 1175 { 1176 Site site = _siteManager.getSite(sitename); 1177 Sitemap sitemap = site.getSitemap(lang); 1178 return sitemap.getChild(path); 1179 } 1180 1181 /** 1182 * Returns the path of the current page, relative to the sitemap's root. 1183 * @return the path of the current Page, or empty if there's no current page. 1184 */ 1185 public static String pagePath() 1186 { 1187 Request request = ContextHelper.getRequest(_context); 1188 Page page = (Page) request.getAttribute(Page.class.getName()); 1189 1190 return page == null ? "" : page.getPathInSitemap(); 1191 } 1192 1193 /** 1194 * Returns the path in sitemap of a page 1195 * @param pageId The id of page 1196 * @return the path of the Page, or empty if not exists 1197 */ 1198 public static String pagePath(String pageId) 1199 { 1200 try 1201 { 1202 Page page = _getPage(pageId); 1203 return page.getPathInSitemap(); 1204 } 1205 catch (UnknownAmetysObjectException e) 1206 { 1207 _logger.error("Can not get title on page with id '" + pageId + "'", e); 1208 return ""; 1209 } 1210 } 1211 1212 /** 1213 * Returns the id of the current page. 1214 * @return the id of the current Page, or empty if there's no current page. 1215 */ 1216 public static String pageId() 1217 { 1218 Request request = ContextHelper.getRequest(_context); 1219 Page page = (Page) request.getAttribute(Page.class.getName()); 1220 1221 return page == null ? "" : page.getId(); 1222 } 1223 1224 /** 1225 * Returns the id of the current zone item id. 1226 * @return the id of the current zone item id, or empty if there's no current zone item. 1227 */ 1228 public static String zoneItemId() 1229 { 1230 Request request = ContextHelper.getRequest(_context); 1231 ZoneItem zoneItem = (ZoneItem) request.getAttribute(WebConstants.REQUEST_ATTR_ZONEITEM); 1232 1233 return zoneItem == null ? "" : StringUtils.defaultString(zoneItem.getId()); 1234 } 1235 1236 /** 1237 * Determines if the current zone item or (if there is no current zone item) the current page is cacheable 1238 * This method is only valid for a page. 1239 * @return true if the current zone item or page is cacheable. 1240 */ 1241 public static boolean isCacheable() 1242 { 1243 Request request = ContextHelper.getRequest(_context); 1244 if (request.getAttribute("IsZoneItemCacheable") != null) 1245 { 1246 return (Boolean) request.getAttribute("IsZoneItemCacheable"); 1247 } 1248 1249 // The method was called from the skin, out of a zone item 1250 Response response = ContextHelper.getResponse(_context); 1251 if (response.containsHeader("X-Ametys-Cacheable")) 1252 { 1253 return true; 1254 } 1255 return false; 1256 } 1257 1258 /** 1259 * Determines if we are in an edition mode 1260 * @return true if we are in edition mode 1261 */ 1262 public static boolean isEditionMode() 1263 { 1264 RenderingContext renderingContext = _renderingContextHandler.getRenderingContext(); 1265 Request request = ContextHelper.getRequest(_context); 1266 if (renderingContext == RenderingContext.FRONT && request.getParameter("_edition") != null && "true".equals(request.getParameter("_edition"))) 1267 { 1268 return true; 1269 } 1270 return false; 1271 } 1272 1273 /** 1274 * Returns the id of pages referencing the content and for which the Front-office user can access 1275 * @param contentId The content's id 1276 * @return The pages' id 1277 */ 1278 public static NodeList accessibleReferencedPages (String contentId) 1279 { 1280 RenderingContext renderingContext = _renderingContextHandler.getRenderingContext(); 1281 boolean inBackOffice = renderingContext == RenderingContext.BACK || renderingContext == RenderingContext.PREVIEW; 1282 1283 List<StringElement> pages = new ArrayList<>(); 1284 1285 Content content = _ametysObjectResolver.resolveById(contentId); 1286 if (content instanceof WebContent) 1287 { 1288 Collection<ZoneItem> zoneItems = ((WebContent) content).getReferencingZoneItems(); 1289 1290 for (ZoneItem zoneItem : zoneItems) 1291 { 1292 String metadataSetName = zoneItem.getMetadataSetName(); 1293 Page page = zoneItem.getZone().getPage(); 1294 1295 if (inBackOffice || _rightManager.hasReadAccess(_currentUserProvider.getUser(), page)) 1296 { 1297 Map<String, String> attrs = new HashMap<>(); 1298 attrs.put("id", page.getId()); 1299 attrs.put("metadataSetName", metadataSetName); 1300 pages.add(new StringElement("page", attrs)); 1301 } 1302 } 1303 } 1304 1305 return new AmetysNodeList(pages); 1306 } 1307 1308 /** 1309 * Returns the id of pages referencing the content 1310 * @param contentId The content's id 1311 * @return The pages' id 1312 */ 1313 public static NodeList referencedPages (String contentId) 1314 { 1315 List<StringElement> pages = new ArrayList<>(); 1316 1317 Content content = _ametysObjectResolver.resolveById(contentId); 1318 if (content instanceof WebContent) 1319 { 1320 Collection<ZoneItem> zoneItems = ((WebContent) content).getReferencingZoneItems(); 1321 1322 for (ZoneItem zoneItem : zoneItems) 1323 { 1324 String metadataSetName = zoneItem.getMetadataSetName(); 1325 Page page = zoneItem.getZone().getPage(); 1326 1327 Map<String, String> attrs = new HashMap<>(); 1328 attrs.put("id", page.getId()); 1329 attrs.put("metadataSetName", metadataSetName); 1330 pages.add(new StringElement("page", attrs)); 1331 } 1332 } 1333 1334 return new AmetysNodeList(pages); 1335 } 1336 1337 /** 1338 * Returns the ids of the contents 1339 * @param tag The tag id 1340 * @return Array of contents ids 1341 */ 1342 public static NodeList findContentsIdsByTag(String tag) 1343 { 1344 Request request = ContextHelper.getRequest(_context); 1345 String siteName = (String) request.getAttribute("site"); 1346 1347 String lang = (String) request.getAttribute("sitemapLanguage"); 1348 if (lang == null) 1349 { 1350 // Try to get current language from content 1351 Content content = (Content) request.getAttribute(Content.class.getName()); 1352 if (content != null) 1353 { 1354 lang = content.getLanguage(); 1355 } 1356 } 1357 return findContentsIdsByTag(siteName, lang, tag); 1358 } 1359 1360 /** 1361 * Returns the ids of the contents 1362 * @param sitename site name. '+' for any site, '*'/null for any site, including contents without site, '^' for only contents without site 1363 * @param lang lang of the contents 1364 * @param tag The tag id 1365 * @return Array of contents ids 1366 */ 1367 public static NodeList findContentsIdsByTag(String sitename, String lang, String tag) 1368 { 1369 List<Expression> expressions = new ArrayList<>(); 1370 TagExpression tagExpression = new TagExpression(Operator.EQ, tag); 1371 expressions.add(tagExpression); 1372 1373 if (lang != null && !lang.equals("*")) 1374 { 1375 LanguageExpression le = new LanguageExpression(Operator.EQ, lang); 1376 expressions.add(le); 1377 } 1378 1379 if (sitename != null) 1380 { 1381 if (sitename.equals("+")) 1382 { 1383 MetadataExpression me = new MetadataExpression("site"); 1384 expressions.add(me); 1385 } 1386 else if (sitename.equals("*")) 1387 { 1388 // no filter 1389 } 1390 else if (sitename.equals("^")) 1391 { 1392 MetadataExpression me = new MetadataExpression("site"); 1393 expressions.add(new NotExpression(me)); 1394 } 1395 else 1396 { 1397 StringExpression se = new StringExpression("site", Operator.EQ, sitename); 1398 expressions.add(se); 1399 } 1400 } 1401 1402 Expression[] expressionsArray = expressions.toArray(new Expression[expressions.size()]); 1403 1404 String xpath = ContentQueryHelper.getContentXPathQuery(new AndExpression(expressionsArray)); 1405 AmetysObjectIterable<Content> contents = _ametysObjectResolver.query(xpath); 1406 Iterator<Content> it = contents.iterator(); 1407 1408 List<StringElement> list = new ArrayList<>(); 1409 while (it.hasNext()) 1410 { 1411 list.add(new StringElement("content", "id", it.next().getId())); 1412 } 1413 return new AmetysNodeList(list); 1414 } 1415 1416 /** 1417 * Returns the ids of the pages 1418 * @param sitename The site id 1419 * @param lang The language code 1420 * @param tag The tag id 1421 * @return Array of pages ids 1422 */ 1423 public static NodeList findPagesIdsByTag(String sitename, String lang, String tag) 1424 { 1425 String xpath = PageQueryHelper.getPageXPathQuery(sitename, lang, null, new TagExpression(Operator.EQ, tag), null); 1426 AmetysObjectIterable<Page> pages = _ametysObjectResolver.query(xpath); 1427 Iterator<Page> it = pages.iterator(); 1428 1429 List<StringElement> list = new ArrayList<>(); 1430 while (it.hasNext()) 1431 { 1432 list.add(new StringElement("page", "id", it.next().getId())); 1433 } 1434 return new AmetysNodeList(list); 1435 } 1436 1437 /** 1438 * Returns the ids of the pages 1439 * @param tag The tag id 1440 * @return Array of pages ids 1441 */ 1442 public static NodeList findPagesIdsByTag(String tag) 1443 { 1444 Request request = ContextHelper.getRequest(_context); 1445 String siteName = (String) request.getAttribute("site"); 1446 1447 String lang = (String) request.getAttribute("sitemapLanguage"); 1448 if (lang == null) 1449 { 1450 // Try to get current language from content 1451 Content content = (Content) request.getAttribute(Content.class.getName()); 1452 if (content != null) 1453 { 1454 lang = content.getLanguage(); 1455 } 1456 } 1457 1458 return findPagesIdsByTag(siteName, lang, tag); 1459 } 1460 1461 /** 1462 * Return the given user by its email over all authorized population on current site 1463 * @param email the concerned user's email 1464 * @return The informations about the given user 1465 * @throws SAXException If an error occurred while saxing the user 1466 */ 1467 public static Node userByMail(String email) throws SAXException 1468 { 1469 String siteName = site(); 1470 1471 Set<String> userPopulationsOnSite = _populationContextHelper.getUserPopulationsOnContexts(Arrays.asList("/sites/" + siteName, "/sites-fo/" + siteName), false, false); 1472 1473 for (String populationId : userPopulationsOnSite) 1474 { 1475 User user = _userHelper.getUserByEmail(populationId, email); 1476 if (user != null) 1477 { 1478 return user(UserIdentity.userIdentityToString(user.getIdentity())); 1479 } 1480 } 1481 1482 return null; 1483 } 1484 1485 /** 1486 * Get the preview of a url (title, description, image, favico) 1487 * @param url the web link 1488 * @return the url preview 1489 * @throws SAXException If an error occurred while saxing 1490 */ 1491 public static Node urlPreview(String url) throws SAXException 1492 { 1493 try 1494 { 1495 String lang = StringUtils.defaultIfEmpty(lang(), "en"); 1496 UrlPreview urlPreview = _urlPreview.getUrlPreview(url, lang); 1497 DOMBuilder domBuilder = new DOMBuilder(); 1498 1499 urlPreview.toSAX(domBuilder, "preview"); 1500 1501 return domBuilder.getDocument(); 1502 } 1503 catch (IOException e) 1504 { 1505 _logger.error("Unable to get preview URL at " + url, e); 1506 return null; 1507 } 1508 } 1509 1510 /** 1511 * Generates a XML structure to help to creates a complex pagination. 1512 * <br> 1513 * <br>Example: pagination(39, 19, 2, 5, 2) will return 1514 * 1515 * <pre> 1516 * <gotofirstpage enabled="true">1</gotofirstpage> 1517 * <gotopreviouspage enabled="true">18</gotopreviouspage> 1518 * <page>1</page> 1519 * <page>2</page> 1520 * <separator/> 1521 * <page>17</page> 1522 * <page>18</page> 1523 * <current>19</current> 1524 * <page>20</page> 1525 * <page>21</page> 1526 * <separator/> 1527 * <page>38</page> 1528 * <page>39</page> 1529 * <gotonextpage enabled="true">20</gotonextpage> 1530 * <gotolastpage enabled="true">39</gotonextpage> 1531 * </pre> 1532 * 1533 * Example: pagination(5, 2, 2, 5, 2) will return 1534 * 1535 * <pre> 1536 * <gotofirstpage enabled="true">1</gotofirstpage> 1537 * <gotopreviouspage enabled="true">1</gotopreviouspage> 1538 * <page>1</page> 1539 * <current>2</page> 1540 * <page>3</page> 1541 * <page>4</page> 1542 * <page>5</page> 1543 * <space/> 1544 * <space/> 1545 * <space/> 1546 * <space/> 1547 * <gotonextpage enabled="true">3</gotonextpage> 1548 * <gotolastpage enabled="true">5</gotonextpage> 1549 * </pre> 1550 * 1551 * @param nbPages The total number of pages 1552 * @param currentPage The currently displayed page (1 based) 1553 * @param nbFirstPages The max number of pages to display before the 1st separator 1554 * @param nbCentralPages The max number of pages to display around the current page 1555 * @param nbLastPages The max number of pages to display after the 2nd separator 1556 * @return The xml described 1557 */ 1558 public static AmetysNodeList pagination(int nbPages, int currentPage, int nbFirstPages, int nbCentralPages, int nbLastPages) 1559 { 1560 List<Node> elements = new ArrayList<>(); 1561 1562 1563 elements.add(new StringElement("gotofirstpage", "enabled", Boolean.toString(currentPage > 1), "1")); 1564 elements.add(new StringElement("gotopreviouspage", "enabled", Boolean.toString(currentPage > 1), currentPage > 1 ? currentPage - 1 + "" : "")); 1565 1566 int[] pagination = _pagination(nbPages, currentPage, nbFirstPages, nbCentralPages, nbLastPages); 1567 for (int page : pagination) 1568 { 1569 if (page == _PAGINATION_SEPARATOR) 1570 { 1571 elements.add(new StringElement("separator", "")); 1572 } 1573 else if (page == _PAGINATION_SPACE) 1574 { 1575 elements.add(new StringElement("space", "")); 1576 } 1577 else if (page == _PAGINATION_CURRENT) 1578 { 1579 elements.add(new StringElement("current", Integer.toString(currentPage))); 1580 } 1581 else 1582 { 1583 elements.add(new StringElement("page", Integer.toString(page))); 1584 } 1585 } 1586 1587 elements.add(new StringElement("gotonextpage", "enabled", Boolean.toString(currentPage < nbPages), currentPage < nbPages ? currentPage + 1 + "" : "")); 1588 elements.add(new StringElement("gotolastpage", "enabled", Boolean.toString(currentPage < nbPages), nbPages + "")); 1589 1590 return new AmetysNodeList(elements); 1591 } 1592 1593 static int[] _pagination(int nbPages, int currentPage, int nbFirstPages, int nbCentralPages, int nbLastPages) 1594 { 1595 int displayedPages = nbFirstPages + 1 + nbCentralPages + 1 + nbLastPages; // The +1 are the room for separators 1596 1597 int[] values = new int[displayedPages]; 1598 1599 int centerOfCentralPages = (int) Math.ceil(nbCentralPages / 2.0); 1600 int centralCursor = nbFirstPages + 1 + centerOfCentralPages; 1601 boolean firstSeparator = nbPages > displayedPages && currentPage > centralCursor; 1602 boolean secondSeparator = nbPages > displayedPages && currentPage <= nbPages - centralCursor; 1603 1604 int cursor = 1; 1605 1606 // Before first separator 1607 cursor = _paginationFirstPages(nbPages, nbFirstPages, currentPage, values, cursor); 1608 1609 // First separator 1610 cursor = _paginationFirstSeparator(nbPages, firstSeparator, currentPage, values, cursor); 1611 1612 int offset = _paginationComputeOffsetAfterFirstSeparator(nbPages, currentPage, nbCentralPages, nbLastPages, centerOfCentralPages, firstSeparator, secondSeparator, cursor); 1613 1614 // Middle part 1615 cursor = _paginationMiddle(nbPages, currentPage, nbFirstPages, nbCentralPages, values, cursor, offset); 1616 1617 // Second separator 1618 cursor = _paginationSecondSeparator(nbPages, secondSeparator, currentPage, nbLastPages, values, cursor, offset); 1619 1620 // After second separator 1621 cursor = _paginationLastPages(nbPages, currentPage, displayedPages, values, cursor, offset); 1622 1623 return values; 1624 } 1625 1626 private static int _paginationMiddle(int nbPages, int currentPage, int nbFirstPages, int nbCentralPages, int[] values, int cursorP, int offset) 1627 { 1628 int cursor = cursorP; 1629 for (; cursor <= nbFirstPages + 1 + nbCentralPages; cursor++) 1630 { 1631 if (cursor + offset > nbPages) 1632 { 1633 values[cursor - 1] = _PAGINATION_SPACE; 1634 } 1635 else if (cursor + offset == currentPage) 1636 { 1637 values[cursor - 1] = _PAGINATION_CURRENT; 1638 } 1639 else 1640 { 1641 values[cursor - 1] = offset + cursor; 1642 } 1643 } 1644 return cursor; 1645 } 1646 1647 private static int _paginationComputeOffsetAfterFirstSeparator(int nbPages, int currentPage, int nbCentralPages, int nbLastPages, int centerOfCentralPages, boolean firstSeparator, boolean secondSeparator, int cursor) 1648 { 1649 if (!firstSeparator) 1650 { 1651 return 0; 1652 } 1653 else if (!secondSeparator) 1654 { 1655 return nbPages - nbLastPages - nbCentralPages - cursor; 1656 } 1657 else 1658 { 1659 return currentPage + 1 - centerOfCentralPages - cursor; 1660 } 1661 } 1662 1663 private static int _paginationLastPages(int nbPages, int currentPage, int displayedPages, int[] values, int cursorP, int offset) 1664 { 1665 int cursor = cursorP; 1666 for (; cursor <= displayedPages; cursor++) 1667 { 1668 if (cursor > nbPages) 1669 { 1670 values[cursor - 1] = _PAGINATION_SPACE; 1671 } 1672 else if (cursor + offset == currentPage) 1673 { 1674 values[cursor - 1] = _PAGINATION_CURRENT; 1675 } 1676 else 1677 { 1678 values[cursor - 1] = nbPages - (displayedPages - cursor); 1679 } 1680 } 1681 return cursor; 1682 } 1683 1684 private static int _paginationSecondSeparator(int nbPages, boolean secondSeparator, int currentPage, int nbLastPages, int[] values, int cursor, int offset) 1685 { 1686 if (cursor + offset > nbPages) 1687 { 1688 values[cursor - 1] = _PAGINATION_SPACE; 1689 } 1690 else if (currentPage == cursor + offset) 1691 { 1692 values[cursor - 1] = _PAGINATION_CURRENT; 1693 } 1694 else if (secondSeparator) 1695 { 1696 values[cursor - 1] = _PAGINATION_SEPARATOR; 1697 } 1698 else 1699 { 1700 values[cursor - 1] = nbPages - nbLastPages; 1701 } 1702 return cursor + 1; 1703 } 1704 1705 private static int _paginationFirstSeparator(int nbPages, boolean firstSeparator, int currentPage, int[] values, int cursor) 1706 { 1707 if (cursor > nbPages) 1708 { 1709 values[cursor - 1] = _PAGINATION_SPACE; 1710 } 1711 else if (currentPage == cursor) 1712 { 1713 values[cursor - 1] = _PAGINATION_CURRENT; 1714 } 1715 else if (firstSeparator) 1716 { 1717 values[cursor - 1] = _PAGINATION_SEPARATOR; 1718 } 1719 else 1720 { 1721 values[cursor - 1] = cursor; 1722 } 1723 return cursor + 1; 1724 } 1725 1726 private static int _paginationFirstPages(int nbPages, int nbFirstPages, int currentPage, int[] values, int cursorP) 1727 { 1728 int cursor = cursorP; 1729 for (; cursor <= nbFirstPages; cursor++) 1730 { 1731 if (cursor > nbPages) 1732 { 1733 values[cursor - 1] = _PAGINATION_SPACE; 1734 } 1735 else if (cursor == currentPage) 1736 { 1737 values[cursor - 1] = _PAGINATION_CURRENT; 1738 } 1739 else 1740 { 1741 values[cursor - 1] = cursor; 1742 } 1743 } 1744 return cursor; 1745 } 1746}