001/* 002 * Copyright 2012 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.ametys.cms.transformation.xslt; 018 019import java.util.ArrayList; 020import java.util.HashMap; 021import java.util.List; 022import java.util.Locale; 023import java.util.Map; 024 025import org.apache.avalon.framework.context.Context; 026import org.apache.avalon.framework.context.ContextException; 027import org.apache.avalon.framework.logger.LogEnabled; 028import org.apache.avalon.framework.logger.Logger; 029import org.apache.avalon.framework.service.ServiceException; 030import org.apache.avalon.framework.service.ServiceManager; 031import org.apache.cocoon.components.ContextHelper; 032import org.apache.cocoon.environment.Request; 033import org.apache.commons.lang.StringUtils; 034import org.apache.commons.lang3.ArrayUtils; 035import org.w3c.dom.Element; 036import org.w3c.dom.Node; 037import org.w3c.dom.NodeList; 038 039import org.ametys.cms.content.ContentHelper; 040import org.ametys.cms.contenttype.ContentType; 041import org.ametys.cms.contenttype.ContentTypeExtensionPoint; 042import org.ametys.cms.repository.Content; 043import org.ametys.cms.tag.CMSTag; 044import org.ametys.cms.tag.Tag; 045import org.ametys.cms.tag.TagProviderExtensionPoint; 046import org.ametys.cms.transformation.dom.TagElement; 047import org.ametys.core.util.dom.AmetysNodeList; 048import org.ametys.core.util.dom.EmptyElement; 049import org.ametys.core.util.dom.StringElement; 050import org.ametys.plugins.explorer.resources.Resource; 051import org.ametys.plugins.explorer.resources.ResourceCollection; 052import org.ametys.plugins.explorer.resources.dom.ResourceCollectionElement; 053import org.ametys.plugins.repository.AmetysObjectResolver; 054import org.ametys.plugins.repository.AmetysRepositoryException; 055import org.ametys.plugins.repository.UnknownAmetysObjectException; 056import org.ametys.plugins.repository.metadata.CompositeMetadata; 057import org.ametys.plugins.repository.metadata.CompositeMetadata.MetadataType; 058import org.ametys.plugins.repository.metadata.UnknownMetadataException; 059import org.ametys.plugins.repository.version.VersionAwareAmetysObject; 060 061/** 062 * Helper component to be used from XSL stylesheets. 063 */ 064public class AmetysXSLTHelper extends org.ametys.core.util.AmetysXSLTHelper implements LogEnabled 065{ 066 /** The Ametys object resolver */ 067 protected static AmetysObjectResolver _ametysObjectResolver; 068 /** The content types extension point */ 069 protected static ContentTypeExtensionPoint _cTypeExtensionPoint; 070 /** The tags provider */ 071 protected static TagProviderExtensionPoint _tagProviderExtPt; 072 /** Helper for content */ 073 protected static ContentHelper _contentHelper; 074 /** The avalon context */ 075 protected static Context _context; 076 /** The logger */ 077 protected static Logger _logger; 078 079 080 @Override 081 public void contextualize(Context context) throws ContextException 082 { 083 super.contextualize(context); 084 _context = context; 085 } 086 087 @Override 088 public void enableLogging(Logger logger) 089 { 090 _logger = logger; 091 } 092 093 @Override 094 public void service(ServiceManager manager) throws ServiceException 095 { 096 _ametysObjectResolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 097 _cTypeExtensionPoint = (ContentTypeExtensionPoint) manager.lookup(ContentTypeExtensionPoint.ROLE); 098 _tagProviderExtPt = (TagProviderExtensionPoint) manager.lookup(TagProviderExtensionPoint.ROLE); 099 _contentHelper = (ContentHelper) manager.lookup(ContentHelper.ROLE); 100 } 101 102 /* ------------------------ */ 103 /* Content methods */ 104 /* ------------------------ */ 105 106 /** 107 * Get the content types of a content 108 * @param contentId The content id 109 * @return The content type or empty if the content does not exist 110 */ 111 public static NodeList contentTypes(String contentId) 112 { 113 ArrayList<StringElement> contentTypes = new ArrayList<>(); 114 115 try 116 { 117 Content content = _ametysObjectResolver.resolveById(contentId); 118 119 try 120 { 121 for (String id : content.getTypes()) 122 { 123 contentTypes.add(new StringElement("content-type", "id", id)); 124 } 125 } 126 catch (AmetysRepositoryException e) 127 { 128 _logger.error("Can not get type of content with id '" + contentId + "'", e); 129 } 130 } 131 catch (UnknownAmetysObjectException e) 132 { 133 _logger.error("Can not get type of content with id '" + contentId + "'", e); 134 } 135 136 return new AmetysNodeList(contentTypes); 137 } 138 139 /** 140 * Get the mixins of a content 141 * @param contentId The content id 142 * @return The content type or empty if the content does not exist 143 */ 144 public static NodeList contentMixinTypes(String contentId) 145 { 146 ArrayList<StringElement> contentTypes = new ArrayList<>(); 147 148 try 149 { 150 Content content = _ametysObjectResolver.resolveById(contentId); 151 152 try 153 { 154 for (String id : content.getMixinTypes()) 155 { 156 contentTypes.add(new StringElement("mixin", "id", id)); 157 } 158 } 159 catch (AmetysRepositoryException e) 160 { 161 _logger.error("Can not get type of content with id '" + contentId + "'", e); 162 } 163 } 164 catch (UnknownAmetysObjectException e) 165 { 166 _logger.error("Can not get type of content with id '" + contentId + "'", e); 167 } 168 169 return new AmetysNodeList(contentTypes); 170 } 171 172 /** 173 * Determines if the content of given id is a entry of reference table 174 * @param contentId the content id 175 * @return true if the content type is a reference table 176 */ 177 public static boolean isReferenceTableContent(String contentId) 178 { 179 try 180 { 181 Content content = _ametysObjectResolver.resolveById(contentId); 182 return _contentHelper.isReferenceTable(content); 183 } 184 catch (UnknownAmetysObjectException e) 185 { 186 _logger.error("Can not get type of unknown content with id '" + contentId + "'", e); 187 return false; 188 } 189 } 190 191 /** 192 * Returns the current language for rendering. 193 * @return the current language for rendering. 194 */ 195 public static String lang() 196 { 197 Request request = ContextHelper.getRequest(_context); 198 return (String) request.getAttribute("renderingLanguage"); 199 } 200 201 /** 202 * Get the metadata of a content 203 * @param contentId The content id 204 * @param metadataName The metadata name (/ for composite) 205 * @param lang The language for localized metadata. Can be null to get the current language. 206 * @return The name or empty if the metadata or the content does not exist 207 */ 208 public static String contentMetadata(String contentId, String metadataName, String lang) 209 { 210 try 211 { 212 Content content = _ametysObjectResolver.resolveById(contentId); 213 try 214 { 215 Locale locale = StringUtils.isEmpty(lang) ? null : new Locale(lang); 216 return _getMetadata(content.getMetadataHolder(), metadataName, locale); 217 } 218 catch (UnknownMetadataException e) 219 { 220 _logger.debug("Can not get metadata '" + metadataName + "' on content with id '" + contentId + "'", e); 221 return ""; 222 } 223 } 224 catch (UnknownAmetysObjectException e) 225 { 226 _logger.debug("Can not get metadata '" + metadataName + "' on unknown content with id '" + contentId + "'", e); 227 return ""; 228 } 229 } 230 /** 231 * Get the metadata of a content 232 * @param contentId The content id 233 * @param metadataName The metadata name (/ for composite) 234 * @return The name or empty if the metadata or the content does not exist 235 */ 236 public static String contentMetadata(String contentId, String metadataName) 237 { 238 return contentMetadata(contentId, metadataName, null); 239 } 240 241 private static String _getMetadata(CompositeMetadata cm, String metadataName, Locale locale) 242 { 243 int i = metadataName.indexOf("/"); 244 if (i == -1) 245 { 246 if (cm.getType(metadataName).equals(MetadataType.MULTILINGUAL_STRING)) 247 { 248 if (locale == null) 249 { 250 String currentLanguage = lang(); 251 if (StringUtils.isEmpty(currentLanguage)) 252 { 253 _logger.error("Can not get the value of a multilingual metadata " + metadataName + " without a defined locale"); 254 return ""; 255 } 256 return cm.getLocalizedString(metadataName, new Locale(currentLanguage)); 257 } 258 else 259 { 260 return cm.getLocalizedString(metadataName, locale); 261 } 262 } 263 else 264 { 265 return cm.getString(metadataName); 266 } 267 } 268 else 269 { 270 return _getMetadata(cm.getCompositeMetadata(metadataName.substring(0, i)), metadataName.substring(i + 1), locale); 271 } 272 } 273 274 /** 275 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of the current content. 276 * @return an Element containing the attachments of the current content as {@link Resource}s. 277 */ 278 public static Node contentAttachments() 279 { 280 Request request = ContextHelper.getRequest(_context); 281 282 Content content = (Content) request.getAttribute(Content.class.getName()); 283 284 return contentAttachments(content); 285 } 286 287 /** 288 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of a given content. 289 * @param contentId the content ID. 290 * @return an Element containing the attachments of the given content as {@link Resource}s. 291 */ 292 public static Node contentAttachments(String contentId) 293 { 294 Content content = _ametysObjectResolver.resolveById(contentId); 295 296 return contentAttachments(content); 297 } 298 299 /** 300 * Returns a DOM {@link Element} containing {@link Resource}s representing the attachments of a given content. 301 * @param content the content. 302 * @return an Element containing the attachments of the given content as {@link Resource}s. 303 */ 304 private static Node contentAttachments(Content content) 305 { 306 if (content == null) 307 { 308 return null; 309 } 310 311 ResourceCollection collection = content.getRootAttachments(); 312 313 return collection != null ? new ResourceCollectionElement(collection) : new EmptyElement("collection"); 314 } 315 316 /** 317 * Set the content of given id in request attribute 318 * @param contentId the id of content 319 */ 320 public static void setCurrentContent(String contentId) 321 { 322 setCurrentContent(contentId, null); 323 } 324 325 /** 326 * Set the content of given id and version in request attribute 327 * @param contentId the id of content 328 * @param versionLabel The version label 329 */ 330 public static void setCurrentContent(String contentId, String versionLabel) 331 { 332 Request request = ContextHelper.getRequest(_context); 333 334 Content content = _ametysObjectResolver.resolveById(contentId); 335 336 if (StringUtils.isNotEmpty(versionLabel) && content instanceof VersionAwareAmetysObject) 337 { 338 String[] allLabels = ((VersionAwareAmetysObject) content).getAllLabels(); 339 if (ArrayUtils.contains(allLabels, versionLabel)) 340 { 341 ((VersionAwareAmetysObject) content).switchToLabel(versionLabel); 342 } 343 } 344 345 request.setAttribute(Content.class.getName(), content); 346 347 } 348 349 //************************* 350 // Tag methods 351 //************************* 352 353 /** 354 * Returns all tags of the current content. 355 * @return a list of tags. 356 */ 357 public static NodeList contentTags() 358 { 359 Request request = ContextHelper.getRequest(_context); 360 361 Content content = (Content) request.getAttribute(Content.class.getName()); 362 return _contentTags(content); 363 } 364 365 /** 366 * Returns all tags of the given content 367 * @param contentId The identifier of the content 368 * @return a list of tags. 369 */ 370 public static NodeList contentTags(String contentId) 371 { 372 try 373 { 374 Content content = _ametysObjectResolver.resolveById(contentId); 375 return _contentTags(content); 376 } 377 catch (AmetysRepositoryException e) 378 { 379 _logger.warn("Cannot get tags for content '" + contentId + "'", e); 380 } 381 382 return null; 383 } 384 385 /** 386 * Returns all tags of the given content 387 * @param content The content 388 * @return a list of tags. 389 */ 390 protected static NodeList _contentTags(Content content) 391 { 392 if (content == null) 393 { 394 return null; 395 } 396 397 List<TagElement> list = new ArrayList<>(); 398 399 for (String tag : content.getTags()) 400 { 401 list.add(new TagElement(tag)); 402 } 403 404 return new AmetysNodeList(list); 405 } 406 407 /** 408 * Get the name of the parent of a tag. 409 * @param siteName the site name 410 * @param tagName the tag's name 411 * @return The id of parent or empty if not found 412 */ 413 public static String tagParent(String siteName, String tagName) 414 { 415 Map<String, Object> contextParameters = new HashMap<>(); 416 contextParameters.put("siteName", siteName); 417 418 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 419 if (tag == null) 420 { 421 return StringUtils.EMPTY; 422 } 423 424 String parentName = tag.getParentName(); 425 return parentName != null ? parentName : StringUtils.EMPTY; 426 } 427 428 /** 429 * Get the path of a tag. The path contains the tag's parents seprated by '/'. 430 * @param siteName The site name 431 * @param tagName The unique tag's name 432 * @return The tag's path or empty string if tag does not exist 433 */ 434 public static String tagPath (String siteName, String tagName) 435 { 436 Map<String, Object> contextParameters = new HashMap<>(); 437 contextParameters.put("siteName", siteName); 438 439 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 440 if (tag == null) 441 { 442 return StringUtils.EMPTY; 443 } 444 445 String path = tagName; 446 447 Tag parentTag = tag.getParent(); 448 while (parentTag != null) 449 { 450 path = parentTag.getName() + "/" + path; 451 parentTag = parentTag.getParent(); 452 } 453 454 return path; 455 } 456 457 /** 458 * Get the label of a tag 459 * @param siteName the current site 460 * @param tagName the name of the tag 461 * @param lang the lang (if i18n tag) 462 * @return the label of the tag or empty if it cannot be found 463 */ 464 public static String tagLabel(String siteName, String tagName, String lang) 465 { 466 Map<String, Object> contextParameters = new HashMap<>(); 467 contextParameters.put("siteName", siteName); 468 469 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 470 return tag == null ? "" : _i18nUtils.translate(tag.getTitle(), lang); 471 } 472 473 /** 474 * Get the description of a tag 475 * @param siteName the current site 476 * @param tagName the name of the tag 477 * @param lang the lang (if i18n tag) 478 * @return the label of the tag or empty if it cannot be found 479 */ 480 public static String tagDescription(String siteName, String tagName, String lang) 481 { 482 Map<String, Object> contextParameters = new HashMap<>(); 483 contextParameters.put("siteName", siteName); 484 485 Tag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 486 return tag == null ? "" : _i18nUtils.translate(tag.getDescription(), lang); 487 } 488 489 /** 490 * Get the visibility of a tag 491 * @param siteName the current site 492 * @param tagName the name of the tag 493 * @return the lower-cased visibility of the tag ("public" or "private") 494 */ 495 public static String tagVisibility(String siteName, String tagName) 496 { 497 Map<String, Object> contextParameters = new HashMap<>(); 498 contextParameters.put("siteName", siteName); 499 500 CMSTag tag = _tagProviderExtPt.getTag(tagName, contextParameters); 501 return tag == null ? "" : tag.getVisibility().toString().toLowerCase(); 502 } 503 504 /* ----------------------------- */ 505 /* Content type methods */ 506 /* ----------------------------- */ 507 508 /** 509 * Returns all tags of a content type 510 * @param contentTypeId The id of the content type 511 * @return a list of tags. 512 */ 513 public static NodeList contentTypeTags(String contentTypeId) 514 { 515 ArrayList<TagElement> tags = new ArrayList<>(); 516 517 try 518 { 519 ContentType cType = _cTypeExtensionPoint.getExtension(contentTypeId); 520 if (cType != null) 521 { 522 for (String tag : cType.getTags()) 523 { 524 tags.add(new TagElement(tag)); 525 } 526 } 527 else 528 { 529 _logger.error("Can not get tags of unknown content type of id '" + contentTypeId + "'"); 530 } 531 532 } 533 catch (AmetysRepositoryException e) 534 { 535 _logger.error("Can not get tags of content type of id '" + contentTypeId + "'", e); 536 } 537 538 return new AmetysNodeList(tags); 539 } 540}