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