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 // get user to notify 061 if (MemberType.USER == newMember.getType()) 062 { 063 User user = _userManager.getUser(newMember.getUser()); 064 String email = user.getEmail(); 065 if (StringUtils.isNotEmpty(email) && _isUserMemberNewToProject(user.getIdentity(), project)) 066 { 067 return Collections.singletonMap(user.getLanguage(), List.of(email)); 068 } 069 } 070 else if (MemberType.GROUP == newMember.getType()) 071 { 072 GroupIdentity groupIdentity = newMember.getGroup(); 073 Group group = _groupManager.getGroup(groupIdentity); 074 if (group != null && project != null) 075 { 076 List<User> newUsersInProject = _projectMemberManager.getGroupUsersFromProject(group, project, (currentProject, identity) -> _isGroupMemberNewToTheProject(currentProject, identity, groupIdentity)); 077 078 return newUsersInProject.stream() 079 .map(user -> Pair.of(user, user.getEmail())) 080 .filter(p -> StringUtils.isNotEmpty(p.getRight())) 081 .collect(Collectors.groupingBy( 082 p -> { 083 return StringUtils.defaultString(p.getLeft().getLanguage()); 084 }, 085 Collectors.mapping( 086 Pair::getRight, 087 Collectors.toList() 088 ) 089 ) 090 ); 091 } 092 } 093 094 return Map.of(); 095 } 096 097 @Override 098 protected I18nizableText getI18nSubject(Event event, Project project) 099 { 100 return new I18nizableText("plugin." + _pluginName, "PROJECT_MAIL_NOTIFICATION_EVENT_SUBJECT_MEMBER_ADDED", List.of(project.getTitle())); 101 } 102 103 @Override 104 protected String getMailBodyURI(Event event, Project project) 105 { 106 return "cocoon://_plugins/workspaces/notification-mail-member-event"; 107 } 108 109 private boolean _isGroupMemberNewToTheProject(Project project, UserIdentity userIdentity, GroupIdentity newGroupToIgnore) 110 { 111 Set<ProjectMember> projectMembers = _projectMemberManager.getProjectMembers(project, false); 112 boolean isUserOfProject = projectMembers.stream() 113 .filter(member -> MemberType.USER == member.getType()) 114 .map(ProjectMember::getUser) 115 .map(User::getIdentity) 116 .filter(userIdentity::equals) 117 .findAny() 118 .isPresent(); 119 120 if (isUserOfProject) 121 { 122 // User from group was already part of the project, as a member of type "user" 123 return false; 124 } 125 126 return _isUserMemberInAGroupMember(userIdentity, newGroupToIgnore, projectMembers); 127 } 128 129 private boolean _isUserMemberNewToProject(UserIdentity userIdentity, Project project) 130 { 131 Set<ProjectMember> projectMembers = _projectMemberManager.getProjectMembers(project, false); 132 return _isUserMemberInAGroupMember(userIdentity, null, projectMembers); 133 } 134 135 private boolean _isUserMemberInAGroupMember(UserIdentity userIdentity, GroupIdentity newGroupToIgnore, Set<ProjectMember> projectMembers) 136 { 137 return projectMembers.stream() 138 .filter(member -> MemberType.GROUP == member.getType()) 139 .map(ProjectMember::getGroup) 140 .filter(group -> newGroupToIgnore == null || !newGroupToIgnore.equals(group.getIdentity())) 141 .filter(Objects::nonNull) 142 .map(Group::getUsers) 143 .flatMap(Set::stream) 144 .filter(userIdentity::equals) 145 .findAny() 146 .isEmpty(); // User was not found in any group of the project, apart from the ignored group 147 } 148}