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