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.core.util; 018 019import java.util.ArrayList; 020import java.util.Collection; 021import java.util.Date; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import org.apache.avalon.framework.context.Context; 028import org.apache.avalon.framework.context.ContextException; 029import org.apache.avalon.framework.context.Contextualizable; 030import org.apache.avalon.framework.service.ServiceException; 031import org.apache.avalon.framework.service.ServiceManager; 032import org.apache.avalon.framework.service.Serviceable; 033import org.apache.cocoon.components.ContextHelper; 034import org.apache.cocoon.environment.Request; 035import org.apache.cocoon.xml.dom.DOMBuilder; 036import org.apache.commons.lang3.StringUtils; 037import org.apache.commons.text.StringEscapeUtils; 038import org.slf4j.Logger; 039import org.slf4j.LoggerFactory; 040import org.w3c.dom.Node; 041import org.w3c.dom.NodeList; 042import org.xml.sax.SAXException; 043 044import org.ametys.core.DevMode; 045import org.ametys.core.DevMode.DEVMODE; 046import org.ametys.core.group.Group; 047import org.ametys.core.group.GroupIdentity; 048import org.ametys.core.group.GroupManager; 049import org.ametys.core.right.RightManager; 050import org.ametys.core.right.RightManager.RightResult; 051import org.ametys.core.user.CurrentUserProvider; 052import org.ametys.core.user.User; 053import org.ametys.core.user.UserIdentity; 054import org.ametys.core.user.UserManager; 055import org.ametys.core.user.directory.NotUniqueUserException; 056import org.ametys.core.util.dom.AmetysNodeList; 057import org.ametys.core.util.dom.MapElement; 058import org.ametys.core.util.dom.StringElement; 059import org.ametys.core.version.Version; 060import org.ametys.core.version.VersionsHandler; 061import org.ametys.plugins.core.user.UserHelper; 062import org.ametys.runtime.config.Config; 063import org.ametys.runtime.i18n.I18nizableText; 064import org.ametys.runtime.servlet.RuntimeConfig; 065import org.ametys.runtime.workspace.WorkspaceManager; 066import org.ametys.runtime.workspace.WorkspaceMatcher; 067 068/** 069 * Helper component to be used from XSL stylesheets. 070 */ 071public class AmetysXSLTHelper implements Contextualizable, Serviceable 072{ 073 /** The logger */ 074 protected static final Logger _LOGGER = LoggerFactory.getLogger(AmetysXSLTHelper.class.getName()); 075 076 /** The i18n utils instance */ 077 protected static I18nUtils _i18nUtils; 078 079 /** The versions handler */ 080 protected static VersionsHandler _versionHandler; 081 082 /** The current user provider */ 083 protected static CurrentUserProvider _currentUserProvider; 084 /** The groups manager */ 085 protected static GroupManager _groupManager; 086 /** The user helper */ 087 protected static UserHelper _userHelper; 088 /** The json utils */ 089 protected static JSONUtils _jsonUtils; 090 /** The right manager */ 091 protected static RightManager _rightManager; 092 /** The user manager */ 093 protected static UserManager _userManager; 094 095 private static Context _context; 096 097 @Override 098 public void contextualize(Context context) throws ContextException 099 { 100 _context = context; 101 } 102 103 public void service(ServiceManager manager) throws ServiceException 104 { 105 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 106 _versionHandler = (VersionsHandler) manager.lookup(VersionsHandler.ROLE); 107 108 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 109 _groupManager = (GroupManager) manager.lookup(GroupManager.ROLE); 110 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 111 _jsonUtils = (JSONUtils) manager.lookup(JSONUtils.ROLE); 112 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 113 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 114 } 115 116 /** 117 * Returns the current URI prefix. 118 * @return the current URI prefix. 119 */ 120 public static String uriPrefix() 121 { 122 return uriPrefix(true); 123 } 124 125 /** 126 * Returns the current URI prefix. 127 * @param withWorkspaceURI true to add the workspace URI (recommended) 128 * @return the current URI prefix. 129 */ 130 public static String uriPrefix(boolean withWorkspaceURI) 131 { 132 return getUriPrefix(withWorkspaceURI); 133 } 134 135 /** 136 * Returns the absolute URI prefix. 137 * @return the absolute URI prefix. 138 */ 139 public static String absoluteUriPrefix() 140 { 141 return absoluteUriPrefix(true); 142 } 143 144 /** 145 * Returns the absolute URI prefix. 146 * @param withWorkspaceURI true to add the workspace URI (recommended) 147 * @return the absolute URI prefix. 148 */ 149 public static String absoluteUriPrefix(boolean withWorkspaceURI) 150 { 151 return getAbsoluteUriPrefix(withWorkspaceURI); 152 } 153 154 /** 155 * Return the current workspace name 156 * @return The workspace name. Cannot be empty. 157 */ 158 public static String workspaceName() 159 { 160 return getWorkspaceName(); 161 } 162 163 /** 164 * Return the current workspace URI 165 * @return The workspace name. Can be empty. 166 */ 167 public static String workspacePrefix() 168 { 169 return getWorkspacePrefix(); 170 } 171 172 /** 173 * Return the current workspace theme name 174 * @return The name 175 */ 176 public static String workspaceTheme() 177 { 178 Request request = ContextHelper.getRequest(_context); 179 return (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_THEME); 180 } 181 182 /** 183 * Return the current workspace theme url 184 * @return The url without any prefix 185 */ 186 public static String workspaceThemeURL() 187 { 188 Request request = ContextHelper.getRequest(_context); 189 190 String workspaceThemeUrl = (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_THEME_URL); 191 if (workspaceThemeUrl == null) 192 { 193 // fallback to the default workspace 194 String workspaceName = RuntimeConfig.getInstance().getDefaultWorkspace(); 195 WorkspaceManager wm = WorkspaceManager.getInstance(); 196 if (wm.getWorkspaceNames().contains(workspaceName)) 197 { 198 workspaceThemeUrl = wm.getWorkspaces().get(workspaceName).getThemeURL(); 199 } 200 } 201 202 return workspaceThemeUrl; 203 } 204 205 /** 206 * Get the application context path. Can be empty if the application 207 * resides in the root context. Use it to create a link beginning with 208 * the application root. 209 * @param withWorkspaceURI true to add the workspace URI (recommended) 210 * @return The application context path with workspace URI 211 * @see Request#getContextPath() 212 */ 213 protected static String getUriPrefix(boolean withWorkspaceURI) 214 { 215 Request request = ContextHelper.getRequest(_context); 216 String workspaceURI = withWorkspaceURI ? getWorkspacePrefix() : ""; 217 218 return request.getContextPath() + workspaceURI; 219 } 220 221 /** 222 * Get the absolutized version of the context path. Use it to create an absolute 223 * link beginning with the application root, for instance when sending a mail 224 * linking to the application. 225 * @param withWorkspaceURI true to add the workspace URI (recommended) 226 * @return The absolute context path. 227 */ 228 protected static String getAbsoluteUriPrefix(boolean withWorkspaceURI) 229 { 230 Request request = ContextHelper.getRequest(_context); 231 232 String uriPrefix = getUriPrefix(withWorkspaceURI); 233 234 if (!uriPrefix.startsWith("http")) 235 { 236 uriPrefix = request.getScheme() + "://" + request.getServerName() + (request.getServerPort() != 80 ? ":" + request.getServerPort() : "") + uriPrefix; 237 } 238 239 return uriPrefix; 240 } 241 242 /** 243 * Return the current workspace name 244 * @return The workspace name. Cannot be empty. 245 */ 246 protected static String getWorkspaceName() 247 { 248 Request request = ContextHelper.getRequest(_context); 249 return (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_NAME); 250 } 251 252 /** 253 * Return the current workspace URI 254 * @return The workspace name. Can be empty for the default workspace. 255 */ 256 protected static String getWorkspacePrefix() 257 { 258 Request request = ContextHelper.getRequest(_context); 259 return (String) request.getAttribute(WorkspaceMatcher.WORKSPACE_URI); 260 } 261 262 /** 263 * Returns the configuration value associated with the given parameter. 264 * @param id the configuration parameter. 265 * @return the configuration value associated with the given parameter. 266 */ 267 public static Object config(String id) 268 { 269 if (Config.getInstance() != null) 270 { 271 return Config.getInstance().getValue(id); 272 } 273 else 274 { 275 return null; 276 } 277 } 278 279 /** 280 * Return the value of a request parameter. 281 * @param parameter the parameter name. 282 * @return the request parameter. 283 */ 284 public static String requestParameter(String parameter) 285 { 286 Request request = ContextHelper.getRequest(_context); 287 return request.getParameter(parameter); 288 } 289 290 /** 291 * Translate an i18n key using current user language. 292 * @param key The key to translate. Specify the catalog this way: "catalogue:KEY" 293 * @return The translation or null. 294 */ 295 public static String translate(String key) 296 { 297 return translate(key, null, null); 298 } 299 300 /** 301 * Translate an i18n key 302 * @param key The key to translate. Specify the catalog this way: "catalogue:KEY" 303 * @param lang The language. Can be null to use current user language. 304 * @return The translation or null. 305 */ 306 public static String translate(String key, String lang) 307 { 308 return translate(key, lang, null); 309 } 310 311 /** 312 * Translate an i18n key 313 * @param key The key to translate. Specify the catalog this way: "catalogue:KEY" 314 * @param lang The language. Can be null to use current user language. 315 * @param parameters The key parameters. Can be empty. 316 * @return The translation or null. 317 */ 318 public static String translate(String key, String lang, NodeList parameters) 319 { 320 List<String> i18nparams = new ArrayList<>(); 321 if (parameters != null && parameters.getLength() == 1) 322 { 323 NodeList childNodes = parameters.item(0).getChildNodes(); 324 for (int i = 0; i < childNodes.getLength(); i++) 325 { 326 i18nparams.add(childNodes.item(i).getTextContent()); 327 } 328 } 329 330 I18nizableText i18nKey = new I18nizableText(null, key, i18nparams); 331 return _i18nUtils.translate(i18nKey, lang); 332 } 333 334 /** 335 * Escape the given string to be used as JS variable. 336 * @param str the string to escape. 337 * @return the escaped String. 338 */ 339 public static String escapeJS(String str) 340 { 341 return StringEscapeUtils.escapeEcmaScript(str); 342 } 343 344 /** 345 * Split the text. 346 * @param textToSplit the text to split. 347 * @param tokenizers the tokenizer characters. 348 * @param startIndex the minimum number of characters of the result string 349 * @return the split text. 350 */ 351 public static String splitText(String textToSplit, String tokenizers, int startIndex) 352 { 353 String tokenizableText = textToSplit.substring(startIndex != 0 ? startIndex - 1 : 0, textToSplit.length()); 354 355 int tokenPlace = StringUtils.indexOfAny(tokenizableText, tokenizers); 356 357 if (tokenPlace == -1) 358 { 359 return textToSplit; 360 } 361 else 362 { 363 return textToSplit.substring(0, startIndex - 1 + tokenPlace); 364 } 365 } 366 367 /** 368 * Split the text. 369 * @param textToSplit the text to split. 370 * @param tokenizers the tokenizer characters. 371 * @param maxCharacters the maximum number of characters of the result string 372 * @param currentCharactersNumber the current character number. 373 * @return the split text. 374 */ 375 @Deprecated 376 public static String splitText(String textToSplit, String tokenizers, int maxCharacters, int currentCharactersNumber) 377 { 378 int tokenStartIndex = maxCharacters - currentCharactersNumber - 1; 379 String tokenizableText = textToSplit.substring(tokenStartIndex, textToSplit.length()); 380 381 int tokenPlace = StringUtils.indexOfAny(tokenizableText, tokenizers); 382 383 if (tokenPlace == -1) 384 { 385 return textToSplit; 386 } 387 else 388 { 389 return textToSplit.substring(0, tokenStartIndex + tokenPlace); 390 } 391 } 392 393 /** 394 * Get the versions of the application. 395 * Default VersionsHandler impl will return Ametys and Application versions 396 * @return The versions <Version><Version><Name>X</Name><Version>X</Version><Date>X</Date></Version></Versions> (empty tags are removed) 397 */ 398 public static Node versions() 399 { 400 Map<String, Object> versionsMap = new HashMap<>(); 401 402 List<Object> versionList = new ArrayList<>(); 403 404 for (Version version : _versionHandler.getVersions()) 405 { 406 Map<String, Object> versionMap = new HashMap<>(); 407 408 String componentName = version.getName(); 409 String componentVersion = version.getVersion(); 410 String componentDate = DateUtils.dateToString(version.getDate()); 411 412 if (StringUtils.isNotEmpty(componentName)) 413 { 414 versionMap.put("Name", componentName); 415 } 416 if (StringUtils.isNotEmpty(componentVersion)) 417 { 418 versionMap.put("Version", componentVersion); 419 } 420 if (StringUtils.isNotEmpty(componentDate)) 421 { 422 versionMap.put("Date", componentDate); 423 } 424 425 versionList.add(versionMap); 426 } 427 428 versionsMap.put("Component", versionList); 429 430 return new MapElement("Versions", versionsMap); 431 } 432 433 /** 434 * Get the current mode of the application for the current user. 435 * @return True if the application is in developer mode, false if in production mode. 436 */ 437 public static boolean isDeveloperMode() 438 { 439 Request request = ContextHelper.getRequest(_context); 440 441 DEVMODE developerMode = DevMode.getDeveloperMode(request); 442 return developerMode == DEVMODE.DEVELOPMENT 443 || developerMode == DEVMODE.SUPER_DEVELOPPMENT; 444 } 445 446 /** 447 * Return the user 448 * @return The current connected user object or null 449 * @throws SAXException if a problem occured while getting the user 450 */ 451 public static Node user() throws SAXException 452 { 453 UserIdentity userIdentity = _currentUserProvider.getUser(); 454 if (userIdentity != null) 455 { 456 return user(userIdentity.getLogin(), userIdentity.getPopulationId()); 457 } 458 459 return null; 460 } 461 462 /** 463 * Return the given user 464 * @param userIdentity the concerned user's login + population 465 * @return The informations about the given user 466 * @throws SAXException If an error occurred while saxing the user 467 */ 468 public static Node user(String userIdentity) throws SAXException 469 { 470 UserIdentity userIdentityObject = UserIdentity.stringToUserIdentity(userIdentity); 471 if (userIdentityObject == null) 472 { 473 return null; 474 } 475 else 476 { 477 return user(userIdentityObject.getLogin(), userIdentityObject.getPopulationId()); 478 } 479 } 480 481 /** 482 * Return the given user 483 * @param login the concerned user's login 484 * @param populationId the concerned user's population id 485 * @return The informations about the given user 486 * @throws SAXException If an error occurred while saxing the user 487 */ 488 public static Node user(String login, String populationId) throws SAXException 489 { 490 DOMBuilder domBuilder = new DOMBuilder(); 491 492 UserIdentity userIdentity = new UserIdentity(login, populationId); 493 _userHelper.saxUserIdentity(userIdentity, domBuilder); 494 495 return domBuilder.getDocument(); 496 } 497 498 /** 499 * Return the given user 500 * @param email the concerned user's email 501 * @param populationId the concerned user's population id 502 * @return The informations about the given user 503 * @throws SAXException If an error occurred while saxing the user 504 */ 505 public static Node userByMail(String email, String populationId) throws SAXException 506 { 507 try 508 { 509 User user = _userManager.getUserByEmail(populationId, email); 510 if (user != null) 511 { 512 return user(UserIdentity.userIdentityToString(user.getIdentity())); 513 } 514 } 515 catch (NotUniqueUserException e) 516 { 517 return null; 518 } 519 return null; 520 } 521 522 /** 523 * Returns the list of the current user's groups. 524 * @return the list of the current user's groups. Can be null if there is no connected user. 525 */ 526 public static NodeList groups() 527 { 528 UserIdentity userIdentity = _currentUserProvider.getUser(); 529 return userIdentity != null ? groups(userIdentity.getLogin(), userIdentity.getPopulationId()) : null; 530 } 531 532 /** 533 * Returns the of the given user's group. 534 * @param userIdentity the concerned user's login + population 535 * @return the of the given user's group. 536 */ 537 public static NodeList groups(String userIdentity) 538 { 539 UserIdentity userIdentityObject = UserIdentity.stringToUserIdentity(userIdentity); 540 if (userIdentityObject == null) 541 { 542 return null; 543 } 544 else 545 { 546 return groups(userIdentityObject.getLogin(), userIdentityObject.getPopulationId()); 547 } 548 } 549 550 /** 551 * Returns the of the given user's group. 552 * @param login the concerned user's login. 553 * @param populationId the concerned user's population. 554 * @return the of the given user's group. 555 */ 556 public static NodeList groups(String login, String populationId) 557 { 558 ArrayList<Node> groups = new ArrayList<>(); 559 560 Set<GroupIdentity> userGroups = _groupManager.getUserGroups(new UserIdentity(login, populationId)); 561 for (GroupIdentity groupId : userGroups) 562 { 563 Group group = _groupManager.getGroup(groupId); 564 if (group != null) 565 { 566 Map<String, String> attributes = new HashMap<>(); 567 attributes.put("name", groupId.getId()); 568 attributes.put("directory", groupId.getDirectoryId()); 569 groups.add(new StringElement("group", attributes, group.getLabel())); 570 } 571 } 572 573 return new AmetysNodeList(groups); 574 } 575 576 /** 577 * Parse a JSON string as a Map and return the value with the desired key 578 * @param jsonString the JSON representation of the object. 579 * @param key name of the value to return 580 * @return the value as a String, or empty string if an error occurred or the key was not found. 581 */ 582 public static String getValueFromJsonObject(String jsonString, String key) 583 { 584 try 585 { 586 Map<String, Object> jsonMap = _jsonUtils.convertJsonToMap(jsonString); 587 if (jsonMap.containsKey(key)) 588 { 589 Object value = jsonMap.get(key); 590 if (value instanceof Map || value instanceof Collection) 591 { 592 _LOGGER.warn("Unable to get string value for key '{}' from json object {}: the value can not be a map nor collection", key, jsonString); 593 } 594 else if (value instanceof Date) 595 { 596 return DateUtils.dateToString((Date) value); 597 } 598 else 599 { 600 return value.toString(); 601 } 602 } 603 } 604 catch (Exception e) 605 { 606 _LOGGER.warn("Unable to parse json object {}", jsonString, e); 607 } 608 609 return ""; 610 } 611 612 /** 613 * Determines if the current logged user has right on a String context 614 * @param rightId The id of right 615 * @param objectCtx the context. Can be null to search on any context. 616 * @return true if the current user is allowed, false otherwise 617 */ 618 public static boolean hasRight(String rightId, String objectCtx) 619 { 620 return _rightManager.currentUserHasRight(rightId, objectCtx) == RightResult.RIGHT_ALLOW; 621 } 622}