001/* 002 * Copyright 2016 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.plugins.workspaces.project.helper; 018 019import java.time.ZonedDateTime; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.Iterator; 025import java.util.List; 026import java.util.Map; 027import java.util.Set; 028 029import org.apache.avalon.framework.context.Context; 030import org.apache.avalon.framework.context.ContextException; 031import org.apache.avalon.framework.context.Contextualizable; 032import org.apache.avalon.framework.logger.LogEnabled; 033import org.apache.avalon.framework.logger.Logger; 034import org.apache.avalon.framework.service.ServiceException; 035import org.apache.avalon.framework.service.ServiceManager; 036import org.apache.avalon.framework.service.Serviceable; 037import org.apache.cocoon.components.ContextHelper; 038import org.apache.cocoon.environment.Request; 039import org.apache.commons.lang3.StringUtils; 040import org.w3c.dom.Node; 041import org.w3c.dom.NodeList; 042 043import org.ametys.cms.languages.LanguagesManager; 044import org.ametys.cms.tag.Tag; 045import org.ametys.core.right.RightManager; 046import org.ametys.core.right.RightManager.RightResult; 047import org.ametys.core.user.CurrentUserProvider; 048import org.ametys.core.user.UserIdentity; 049import org.ametys.core.util.DateUtils; 050import org.ametys.core.util.dom.AmetysNodeList; 051import org.ametys.core.util.dom.MapElement; 052import org.ametys.plugins.explorer.resources.ModifiableResourceCollection; 053import org.ametys.plugins.repository.RepositoryConstants; 054import org.ametys.plugins.repository.data.holder.ModelAwareDataHolder; 055import org.ametys.plugins.repository.provider.RequestAttributeWorkspaceSelector; 056import org.ametys.plugins.workspaces.ObservationConstants; 057import org.ametys.plugins.workspaces.alert.AlertWorkspaceModule; 058import org.ametys.plugins.workspaces.categories.Category; 059import org.ametys.plugins.workspaces.categories.CategoryHelper; 060import org.ametys.plugins.workspaces.categories.CategoryProviderExtensionPoint; 061import org.ametys.plugins.workspaces.events.activitystream.ActivityStreamClientInteraction; 062import org.ametys.plugins.workspaces.keywords.KeywordsDAO; 063import org.ametys.plugins.workspaces.news.NewsWorkspaceModule; 064import org.ametys.plugins.workspaces.project.ProjectManager; 065import org.ametys.plugins.workspaces.project.modules.WorkspaceModule; 066import org.ametys.plugins.workspaces.project.modules.WorkspaceModuleExtensionPoint; 067import org.ametys.plugins.workspaces.project.objects.Project; 068import org.ametys.runtime.i18n.I18nizableText; 069import org.ametys.web.WebConstants; 070import org.ametys.web.cocoon.I18nUtils; 071import org.ametys.web.repository.page.Page; 072import org.ametys.web.repository.page.ZoneItem; 073import org.ametys.web.repository.site.Site; 074import org.ametys.web.repository.site.SiteManager; 075import org.ametys.web.transformation.xslt.AmetysXSLTHelper; 076 077/** 078 * Helper component to be used from XSL stylesheets to get info related to projects. 079 */ 080public class ProjectXsltHelper implements Serviceable, Contextualizable, LogEnabled 081{ 082 private static Logger _logger; 083 private static Context _context; 084 private static SiteManager _siteManager; 085 private static ProjectManager _projectManager; 086 private static CategoryProviderExtensionPoint _categoryProviderEP; 087 private static CategoryHelper _categoryHelper; 088 private static I18nUtils _i18nUtils; 089 private static ActivityStreamClientInteraction _activityStream; 090 private static LanguagesManager _languagesManager; 091 private static CurrentUserProvider _currentUserProvider; 092 private static WorkspaceModuleExtensionPoint _workspaceModuleEP; 093 private static RightManager _rightManager; 094 private static KeywordsDAO _keywordsDAO; 095 096 @Override 097 public void contextualize(Context context) throws ContextException 098 { 099 _context = context; 100 } 101 102 @Override 103 public void enableLogging(Logger logger) 104 { 105 _logger = logger; 106 } 107 108 @Override 109 public void service(ServiceManager manager) throws ServiceException 110 { 111 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 112 _projectManager = (ProjectManager) manager.lookup(ProjectManager.ROLE); 113 _categoryProviderEP = (CategoryProviderExtensionPoint) manager.lookup(CategoryProviderExtensionPoint.ROLE); 114 _categoryHelper = (CategoryHelper) manager.lookup(CategoryHelper.ROLE); 115 _i18nUtils = (I18nUtils) manager.lookup(org.ametys.core.util.I18nUtils.ROLE); 116 _activityStream = (ActivityStreamClientInteraction) manager.lookup(ActivityStreamClientInteraction.ROLE); 117 _languagesManager = (LanguagesManager) manager.lookup(LanguagesManager.ROLE); 118 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 119 _workspaceModuleEP = (WorkspaceModuleExtensionPoint) manager.lookup(WorkspaceModuleExtensionPoint.ROLE); 120 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 121 _keywordsDAO = (KeywordsDAO) manager.lookup(KeywordsDAO.ROLE); 122 } 123 124 /** 125 * Returns the current project 126 * @return the current project 127 */ 128 public static String project() 129 { 130 Project project = null; 131 132 Request request = ContextHelper.getRequest(_context); 133 String siteName = (String) request.getAttribute("site"); 134 135 if (StringUtils.isNotEmpty(siteName) && _siteManager.hasSite(siteName)) 136 { 137 Site site = _siteManager.getSite(siteName); 138 List<Project> projects = _projectManager.getProjectsForSite(site); 139 140 project = !projects.isEmpty() ? projects.get(0) : null; 141 } 142 143 if (project == null && _logger.isWarnEnabled()) 144 { 145 String warnMsg = String.format("No project was found for site '%s'.", siteName); 146 _logger.warn(warnMsg); 147 } 148 149 return project != null ? project.getName() : null; 150 } 151 152 /** 153 * Returns the information of current project 154 * @return the information of current project 155 */ 156 public static MapElement projectInfo() 157 { 158 return projectInfo(project()); 159 } 160 161 /** 162 * Returns the project information 163 * @param projectName The project name 164 * @return the project information 165 */ 166 public static MapElement projectInfo(String projectName) 167 { 168 Map<String, Object> projectInfo = new HashMap<>(); 169 170 Project project = _projectManager.getProject(projectName); 171 if (project != null) 172 { 173 projectInfo.put("name", project.getName()); 174 projectInfo.put("id", project.getId()); 175 projectInfo.put("title", project.getTitle()); 176 projectInfo.put("description", project.getDescription()); 177 projectInfo.put("creationDate", _projectCreationDate(project)); 178 projectInfo.put("lastActivityDate", projectLastActivityDate(project.getName())); 179 projectInfo.put("categoryLabel", _projectCategoryLabel(project)); 180 projectInfo.put("categoryColor", _projectCategoryColor(project)); 181 projectInfo.put("keyword", _projectKeywords(project)); 182 } 183 184 return new MapElement("project", projectInfo); 185 } 186 187 /** 188 * Returns the title of the current project 189 * @return the title of the current project 190 */ 191 public static String projectTitle() 192 { 193 return projectTitle(project()); 194 } 195 196 /** 197 * Returns the title of the given project 198 * @param projectName The project to consider 199 * @return the title of the given project or empty otherwise 200 */ 201 public static String projectTitle(String projectName) 202 { 203 String title = ""; 204 205 Project project = _projectManager.getProject(projectName); 206 if (project != null) 207 { 208 title = project.getTitle(); 209 } 210 211 return title; 212 } 213 214 /** 215 * Returns the description of the current project 216 * @return the description of the current project 217 */ 218 public static String projectDescription() 219 { 220 return projectDescription(project()); 221 } 222 223 /** 224 * Returns the description of the given project 225 * @param projectName The project to consider 226 * @return the description of the given project or empty otherwise 227 */ 228 public static String projectDescription(String projectName) 229 { 230 String title = ""; 231 232 Project project = _projectManager.getProject(projectName); 233 if (project != null) 234 { 235 title = project.getDescription(); 236 } 237 238 return title; 239 } 240 241 /** 242 * Returns the keywords of the current project 243 * @return keywords of the current project 244 */ 245 public static NodeList projectKeywords() 246 { 247 return projectKeywords(project()); 248 } 249 250 /** 251 * Returns the project keywords 252 * @param projectName the project name 253 * @return the project keywords 254 */ 255 public static NodeList projectKeywords(String projectName) 256 { 257 List<MapElement> keywordsElmts = new ArrayList<>(); 258 259 Project project = _projectManager.getProject(projectName); 260 if (project != null) 261 { 262 List<Map<String, String>> keywords = _projectKeywords(project); 263 for (Map<String, String> keyword : keywords) 264 { 265 keywordsElmts.add(new MapElement("keyword", keyword)); 266 } 267 } 268 269 return new AmetysNodeList(keywordsElmts); 270 } 271 272 private static List<Map<String, String>> _projectKeywords(Project project) 273 { 274 List<Map<String, String>> keywords = new ArrayList<>(); 275 276 for (String keyword : project.getKeywords()) 277 { 278 Tag tag = _keywordsDAO.getTag(keyword, Collections.emptyMap()); 279 if (tag != null) 280 { 281 String title = _i18nUtils.translate(tag.getTitle(), AmetysXSLTHelper.lang()); 282 keywords.add(Map.of("title", title, "name", keyword)); 283 } 284 } 285 286 return keywords; 287 } 288 289 /** 290 * Returns the creation date of the current project 291 * @return the creation date of the current project at the ISO format or empty otherwise 292 */ 293 public static String projectCreationDate() 294 { 295 return projectCreationDate(project()); 296 } 297 298 /** 299 * Returns the creation date of the given project 300 * @param projectName The project to consider 301 * @return the creation date of the given project at the ISO format or empty otherwise 302 */ 303 public static String projectCreationDate(String projectName) 304 { 305 String creationDate = ""; 306 307 Project project = _projectManager.getProject(projectName); 308 if (project != null) 309 { 310 creationDate = _projectCreationDate(project); 311 } 312 313 return creationDate; 314 } 315 316 private static String _projectCreationDate(Project project) 317 { 318 ZonedDateTime date = project.getCreationDate(); 319 return date.format(DateUtils.getISODateTimeFormatter()); 320 } 321 322 /** 323 * Return the color associated to the first category associated to the current project 324 * @return the hexa code color or the default color if no color is associated to the first category or if not category is associated. 325 * <color> 326 * <main>#FFFFFF</main> 327 * <text>#000000</text> 328 * </color> 329 */ 330 public static MapElement projectCategoryColor() 331 { 332 return projectCategoryColor(project()); 333 } 334 335 /** 336 * Return the color associated to the first category associated to the given project 337 * @param projectName The project to consider 338 * @return the hexa code color or the default color if no color is associated to the first category or if not category is associated 339 * <color> 340 * <main>#FFFFFF</main> 341 * <text>#000000</text> 342 * </color> 343 */ 344 public static MapElement projectCategoryColor(String projectName) 345 { 346 Project project = _projectManager.getProject(projectName); 347 Map<String, String> color = _projectCategoryColor(project); 348 return new MapElement("color", color); 349 } 350 351 private static Map<String, String> _projectCategoryColor(Project project) 352 { 353 Category category = _getFirstCategory(project); 354 return _categoryColor(category); 355 } 356 357 /** 358 * Return the color associated to the category 359 * @param categoryId category id 360 * @return the hexa code color or the default color if no color is associated to the category or if null 361 * <color> 362 * <main>#FFFFFF</main> 363 * <text>#000000</text> 364 * </color> 365 */ 366 public static MapElement categoryColor(String categoryId) 367 { 368 Category category = _categoryProviderEP.getTag(categoryId, null); 369 return categoryColor(category); 370 } 371 372 /** 373 * Return the color associated to the category 374 * @param category category 375 * @return the hexa code color or the default color if no color is associated to the category or if null 376 * <color> 377 * <main>#FFFFFF</main> 378 * <text>#000000</text> 379 * </color> 380 */ 381 public static MapElement categoryColor(Category category) 382 { 383 Map<String, String> color = _categoryColor(category); 384 return new MapElement("color", color); 385 } 386 387 private static Map<String, String> _categoryColor(Category category) 388 { 389 return _categoryHelper.getCategoryColor(category); 390 } 391 392 private static Category _getFirstCategory(Project project) 393 { 394 if (project != null) 395 { 396 for (String categoryId : project.getCategories()) 397 { 398 Category category = _categoryProviderEP.getTag(categoryId, null); 399 if (category != null) 400 { 401 return category; 402 } 403 } 404 } 405 return null; 406 } 407 408 409 /** 410 * Return the color associated to the first category associated to the current project 411 * @return the hexa code color or the default color if no color is associated to the first category or if not category is associated 412 */ 413 public static String projectCategoryLabel() 414 { 415 return projectCategoryLabel(project()); 416 } 417 418 /** 419 * Return the color associated to the first category associated to the given project 420 * @param projectName The project to consider 421 * @return the label or empty if no category 422 */ 423 public static String projectCategoryLabel(String projectName) 424 { 425 Project project = _projectManager.getProject(projectName); 426 if (project != null) 427 { 428 return _projectCategoryLabel(project); 429 } 430 return null; 431 } 432 433 private static String _projectCategoryLabel(Project project) 434 { 435 Category category = _getFirstCategory(project); 436 if (category != null) 437 { 438 return _i18nUtils.translate(category.getTitle(), AmetysXSLTHelper.lang()); 439 } 440 return null; 441 } 442 443 /** 444 * Get the date of last activity for current project 445 * @return the formatted date of last activity or creation date if there is no activity yet 446 */ 447 public static String projectLastActivityDate() 448 { 449 return projectLastActivityDate(project()); 450 } 451 452 /** 453 * Get the date of last activity for a given project 454 * @param projectName the project's name 455 * @return the formatted date of last activity or creation date if there is no activity yet 456 */ 457 public static String projectLastActivityDate(String projectName) 458 { 459 ZonedDateTime lastDate = _activityStream.getDateOfLastEvent(projectName, Arrays.asList(ObservationConstants.EVENT_MEMBER_ADDED, ObservationConstants.EVENT_MEMBER_DELETED)); 460 return lastDate != null ? lastDate.format(DateUtils.getISODateTimeFormatter()) : projectCreationDate(); 461 } 462 463 /** 464 * True if the resource comes from workspaces 465 * @param resourceId the resource id 466 * @return true if the resource comes from workspaces 467 */ 468 public static boolean isResourceFromWorkspace(String resourceId) 469 { 470 return _projectManager.getParentProject(resourceId) != null; 471 } 472 473 /** 474 * Get the site of a project's resource 475 * @param projectResourceId The resource id 476 * @return The site <site id="site://xxx" name="siteName"><title>Site's titleX</title><url>http://...</url>/site> 477 */ 478 public static Node resourceSite(String projectResourceId) 479 { 480 Project project = _projectManager.getParentProject(projectResourceId); 481 if (project != null) 482 { 483 Site site = project.getSites().iterator().next(); 484 485 Map<String, String> attributes = new HashMap<>(); 486 attributes.put("name", site.getName()); 487 attributes.put("id", site.getId()); 488 489 Map<String, String> values = new HashMap<>(); 490 values.put("title", site.getTitle()); 491 values.put("url", site.getUrl()); 492 493 return new MapElement("site", attributes, values); 494 } 495 496 return null; 497 } 498 499 /** 500 * Get the id of news root's page 501 * @return the id of news root's page or null if not exists 502 */ 503 public static String getNewsRootPageId() 504 { 505 Request request = ContextHelper.getRequest(_context); 506 507 // Retrieve the current workspace. 508 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 509 510 try 511 { 512 // Force default workspace (news root's page can not be publish yet as it is a node page) 513 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE); 514 515 return getModulePage(NewsWorkspaceModule.NEWS_MODULE_ID); 516 } 517 finally 518 { 519 // Restore workspace 520 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 521 } 522 } 523 524 /** 525 * Get the id of alerts root's page 526 * @return the id of alerts root's page or null if not exists 527 */ 528 public static String getAlertsRootPageId() 529 { 530 Request request = ContextHelper.getRequest(_context); 531 532 // Retrieve the current workspace. 533 String currentWsp = RequestAttributeWorkspaceSelector.getForcedWorkspace(request); 534 535 try 536 { 537 // Force default workspace (news root's page can not be publish yet as it is a node page) 538 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, RepositoryConstants.DEFAULT_WORKSPACE); 539 540 return getModulePage(AlertWorkspaceModule.ALERT_MODULE_ID); 541 } 542 finally 543 { 544 // Restore workspace 545 RequestAttributeWorkspaceSelector.setForcedWorkspace(request, currentWsp); 546 } 547 } 548 549 /** 550 * Get the module page. 551 * Be careful, the read access is not check ! 552 * @param moduleId the module id 553 * @return the module page 554 */ 555 public static String getModulePage(String moduleId) 556 { 557 String projectName = project(); 558 Project project = _projectManager.getProject(projectName); 559 560 Set<Page> newsRootPages = _projectManager.getModulePages(project, moduleId); 561 Iterator<Page> it = newsRootPages.iterator(); 562 if (it.hasNext()) 563 { 564 return it.next().getId(); 565 } 566 567 return null; 568 } 569 570 /** 571 * Get the translated label associated to the code 572 * @param code The language code 573 * @param language The language in which translate the label 574 * @return The label associated to the code 575 */ 576 public static String getLanguageLabel(String code, String language) 577 { 578 I18nizableText label = _languagesManager.getLanguage(code).getLabel(); 579 return _i18nUtils.translate(label, language); 580 } 581 582 /** 583 * Get the language that will be the default value of the available values. 584 * First choice will be the page language, 'en' otherwise, first available language otherwise. 585 * @param language The current page language 586 * @return A language code 587 */ 588 public static String computeDefaultLanguage(String language) 589 { 590 Request request = ContextHelper.getRequest(_context); 591 ZoneItem zoneItem = (ZoneItem) request.getAttribute(WebConstants.REQUEST_ATTR_ZONEITEM); 592 ModelAwareDataHolder dataHolder = zoneItem.getServiceParameters(); 593 594 String[] availableLanguages = dataHolder.getValue("availableLanguages"); 595 if (Arrays.stream(availableLanguages).anyMatch(l -> l.equals(language))) 596 { 597 return language; 598 } 599 else if (Arrays.stream(availableLanguages).anyMatch(l -> l.equals("en"))) 600 { 601 return "en"; 602 } 603 else 604 { 605 return availableLanguages[0]; 606 } 607 } 608 609 /** 610 * Determines if the current user is a manager of current project 611 * @return true if the current user is manager 612 */ 613 public static boolean isManager() 614 { 615 UserIdentity user = _currentUserProvider.getUser(); 616 String projectName = project(); 617 return _projectManager.isManager(projectName, user); 618 } 619 620 /** 621 * Determines if a user is a manager 622 * @param login the user's login 623 * @param populationId the user's population 624 * @return true if the user is manager 625 */ 626 public static boolean isManager(String login, String populationId) 627 { 628 String projectName = project(); 629 UserIdentity user = new UserIdentity(login, populationId); 630 return _projectManager.isManager(projectName, user); 631 } 632 633 /** 634 * Determines if the current user is a manager for the project 635 * @param projectName the project's name 636 * @return true if the current user is manager 637 */ 638 public static boolean isManager(String projectName) 639 { 640 UserIdentity user = _currentUserProvider.getUser(); 641 return _projectManager.isManager(projectName, user); 642 } 643 644 /** 645 * Determines if the given user is a manager for the project 646 * @param projectName the project's name 647 * @param login the user's login 648 * @param populationId the user's population 649 * @return true if the user is manager 650 */ 651 public static boolean isManager(String projectName, String login, String populationId) 652 { 653 UserIdentity user = new UserIdentity(login, populationId); 654 return _projectManager.isManager(projectName, user); 655 } 656 657 658 /** 659 * Returns true if the current user has the specified right on the specified workspace module 660 * @param rightId Right Id 661 * @param moduleId Module Id 662 * @return true if the current user has the specified right on the specified module 663 */ 664 public static boolean hasRightOnModule(String rightId, String moduleId) 665 { 666 String projectName = project(); 667 WorkspaceModule module = _workspaceModuleEP.getModule(moduleId); 668 if (module != null && projectName != null) 669 { 670 Project project = _projectManager.getProject(projectName); 671 if (project != null) 672 { 673 ModifiableResourceCollection moduleRoot = module.getModuleRoot(project, false); 674 if (moduleRoot != null) 675 { 676 return _rightManager.currentUserHasRight(rightId, moduleRoot) == RightResult.RIGHT_ALLOW; 677 } 678 } 679 } 680 681 return false; 682 } 683 684 /** 685 * Returns true if current user can access to the back-office 686 * @return true if current user can access to the back-office 687 */ 688 public static boolean canAccessBO() 689 { 690 String projectName = project(); 691 Project project = _projectManager.getProject(projectName); 692 return project != null ? _projectManager.canAccessBO(project) : false; 693 } 694}