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