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