001/* 002 * Copyright 2017 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 */ 016package org.ametys.plugins.workspaces.project.notification; 017 018import java.util.Collections; 019import java.util.List; 020import java.util.Map; 021import java.util.Objects; 022import java.util.Set; 023import java.util.stream.Collectors; 024 025import org.apache.commons.lang3.StringUtils; 026import org.apache.commons.lang3.tuple.Pair; 027 028import org.ametys.core.group.Group; 029import org.ametys.core.group.GroupIdentity; 030import org.ametys.core.observation.Event; 031import org.ametys.core.user.User; 032import org.ametys.core.user.UserIdentity; 033import org.ametys.plugins.workspaces.ObservationConstants; 034import org.ametys.plugins.workspaces.members.JCRProjectMember; 035import org.ametys.plugins.workspaces.members.JCRProjectMember.MemberType; 036import org.ametys.plugins.workspaces.members.ProjectMemberManager.ProjectMember; 037import org.ametys.plugins.workspaces.project.objects.Project; 038import org.ametys.runtime.config.Config; 039import org.ametys.runtime.i18n.I18nizableText; 040 041/** 042 * Notifier to send mail to a newly added member of a workspace. 043 */ 044public class WelcomeMemberMailNotifierObserver extends AbstractMemberMailNotifierObserver 045{ 046 @Override 047 public boolean supports(Event event) 048 { 049 return event.getId().equals(ObservationConstants.EVENT_MEMBER_ADDED) 050 && Config.getInstance().getValue("workspaces.member.added.send.notification", true, false); // Check feature enabled 051 } 052 053 @Override 054 protected Map<String, List<String>> getUserToNotifyByLanguage(Event event, Project project) 055 { 056 // async observer, we need to resolve the member 057 String memberId = (String) event.getArguments().get(ObservationConstants.ARGS_MEMBER_ID); 058 JCRProjectMember newMember = _resolver.resolveById(memberId); 059 060 String defaultLanguage = _userLanguagesManager.getDefaultLanguage(); 061 062 // get user to notify 063 if (MemberType.USER == newMember.getType()) 064 { 065 User user = _userManager.getUser(newMember.getUser()); 066 String email = user.getEmail(); 067 if (StringUtils.isNotEmpty(email) && _isUserMemberNewToProject(user.getIdentity(), project)) 068 { 069 return Collections.singletonMap(StringUtils.defaultIfBlank(user.getLanguage(), defaultLanguage), List.of(email)); 070 } 071 } 072 else if (MemberType.GROUP == newMember.getType()) 073 { 074 GroupIdentity groupIdentity = newMember.getGroup(); 075 Group group = _groupManager.getGroup(groupIdentity); 076 if (group != null && project != null) 077 { 078 List<User> newUsersInProject = _projectMemberManager.getGroupUsersFromProject(group, project, (currentProject, identity) -> _isGroupMemberNewToTheProject(currentProject, identity, groupIdentity)); 079 080 return newUsersInProject.stream() 081 .map(user -> Pair.of(user, user.getEmail())) 082 .filter(p -> StringUtils.isNotEmpty(p.getRight())) 083 .collect(Collectors.groupingBy( 084 p -> { 085 return StringUtils.defaultIfBlank(p.getLeft().getLanguage(), defaultLanguage); 086 }, 087 Collectors.mapping( 088 Pair::getRight, 089 Collectors.toList() 090 ) 091 ) 092 ); 093 } 094 } 095 096 return Map.of(); 097 } 098 099 @Override 100 protected I18nizableText getI18nSubject(Event event, Project project) 101 { 102 return new I18nizableText("plugin." + _pluginName, "PROJECT_MAIL_NOTIFICATION_EVENT_SUBJECT_MEMBER_ADDED", List.of(project.getTitle())); 103 } 104 105 @Override 106 protected String getMailBodyURI(Event event, Project project) 107 { 108 return "cocoon://_plugins/workspaces/notification-mail-member-event"; 109 } 110 111 private boolean _isGroupMemberNewToTheProject(Project project, UserIdentity userIdentity, GroupIdentity newGroupToIgnore) 112 { 113 Set<ProjectMember> projectMembers = _projectMemberManager.getProjectMembers(project, false); 114 boolean isUserOfProject = projectMembers.stream() 115 .filter(member -> MemberType.USER == member.getType()) 116 .map(ProjectMember::getUser) 117 .map(User::getIdentity) 118 .filter(userIdentity::equals) 119 .findAny() 120 .isPresent(); 121 122 if (isUserOfProject) 123 { 124 // User from group was already part of the project, as a member of type "user" 125 return false; 126 } 127 128 return _isUserMemberInAGroupMember(userIdentity, newGroupToIgnore, projectMembers); 129 } 130 131 private boolean _isUserMemberNewToProject(UserIdentity userIdentity, Project project) 132 { 133 Set<ProjectMember> projectMembers = _projectMemberManager.getProjectMembers(project, false); 134 return _isUserMemberInAGroupMember(userIdentity, null, projectMembers); 135 } 136 137 private boolean _isUserMemberInAGroupMember(UserIdentity userIdentity, GroupIdentity newGroupToIgnore, Set<ProjectMember> projectMembers) 138 { 139 return projectMembers.stream() 140 .filter(member -> MemberType.GROUP == member.getType()) 141 .map(ProjectMember::getGroup) 142 .filter(group -> newGroupToIgnore == null || !newGroupToIgnore.equals(group.getIdentity())) 143 .filter(Objects::nonNull) 144 .map(Group::getUsers) 145 .flatMap(Set::stream) 146 .filter(userIdentity::equals) 147 .findAny() 148 .isEmpty(); // User was not found in any group of the project, apart from the ignored group 149 } 150}