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