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