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