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.ArrayList; 019import java.util.List; 020import java.util.Map; 021import java.util.Set; 022import java.util.stream.Collectors; 023 024import javax.mail.MessagingException; 025 026import org.apache.avalon.framework.service.ServiceException; 027import org.apache.avalon.framework.service.ServiceManager; 028import org.apache.avalon.framework.service.Serviceable; 029import org.apache.commons.lang.StringUtils; 030 031import org.ametys.core.group.Group; 032import org.ametys.core.group.GroupManager; 033import org.ametys.core.observation.AsyncObserver; 034import org.ametys.core.observation.Event; 035import org.ametys.core.user.User; 036import org.ametys.core.user.UserIdentity; 037import org.ametys.core.user.UserManager; 038import org.ametys.core.user.population.PopulationContextHelper; 039import org.ametys.core.util.I18nUtils; 040import org.ametys.core.util.mail.SendMailHelper; 041import org.ametys.plugins.repository.AmetysObjectResolver; 042import org.ametys.plugins.workspaces.ObservationConstants; 043import org.ametys.plugins.workspaces.members.JCRProjectMember; 044import org.ametys.plugins.workspaces.members.JCRProjectMember.MemberType; 045import org.ametys.plugins.workspaces.members.ProjectMemberManager; 046import org.ametys.plugins.workspaces.project.objects.Project; 047import org.ametys.runtime.config.Config; 048import org.ametys.runtime.i18n.I18nizableText; 049import org.ametys.runtime.plugin.component.AbstractLogEnabled; 050import org.ametys.runtime.plugin.component.PluginAware; 051 052/** 053 * Notifier to send mail to a newly added member of a workspace. 054 */ 055public class AddMemberMailNotifierObserver extends AbstractLogEnabled implements AsyncObserver, PluginAware, Serviceable 056{ 057 /** The name of current plugin */ 058 protected String _pluginName; 059 /** The Ametys Object resolver */ 060 protected AmetysObjectResolver _resolver; 061 /** The i18n utils */ 062 protected I18nUtils _i18nUtils; 063 /** The user manager */ 064 protected UserManager _userManager; 065 /** The group manager */ 066 protected GroupManager _groupManager; 067 /** The population context helper */ 068 protected PopulationContextHelper _populationContextHelper; 069 /** The project member manager */ 070 protected ProjectMemberManager _projectMemberManager; 071 072 @Override 073 public void setPluginInfo(String pluginName, String featureName, String id) 074 { 075 _pluginName = pluginName; 076 } 077 078 @Override 079 public void service(ServiceManager manager) throws ServiceException 080 { 081 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 082 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 083 _userManager = (UserManager) manager.lookup(UserManager.ROLE); 084 _groupManager = (GroupManager) manager.lookup(GroupManager.ROLE); 085 _populationContextHelper = (PopulationContextHelper) manager.lookup(PopulationContextHelper.ROLE); 086 _projectMemberManager = (ProjectMemberManager) manager.lookup(ProjectMemberManager.ROLE); 087 } 088 089 @Override 090 public boolean supports(Event event) 091 { 092 return event.getId().equals(ObservationConstants.EVENT_MEMBER_ADDED); 093 } 094 095 @Override 096 public int getPriority(Event event) 097 { 098 return MIN_PRIORITY; 099 } 100 101 @Override 102 public void observe(Event event, Map<String, Object> transientVars) throws Exception 103 { 104 // Check feature enabled 105 boolean notification = Config.getInstance().getValue("workspaces.member.added.send.notification"); 106 if (!notification) 107 { 108 return; 109 } 110 111 Map<String, Object> args = event.getArguments(); 112 113 // Retrieve newly added member 114 String jcrMemberId = (String) args.get(ObservationConstants.ARGS_MEMBER_ID); 115 JCRProjectMember jcrMember = _getMember(jcrMemberId); 116 117 // Retrieve project 118 String projectId = (String) args.get(ObservationConstants.ARGS_PROJECT_ID); 119 Project project = _resolver.resolveById(projectId); 120 121 // Compute subject and body 122 String subject = _i18nUtils.translate(_getSubjectI18nizableText(project, jcrMember)); 123 String textBody = _i18nUtils.translate(_getBodyI18nizableText(project, jcrMember)); 124 125 List<UserIdentity> usersIdentities = new ArrayList<>(); 126 if (MemberType.USER.toString().equals(jcrMember.getType())) 127 { 128 usersIdentities.add(jcrMember.getUser()); 129 } 130 else if (MemberType.GROUP.toString().equals(jcrMember.getType())) 131 { 132 Group group = _groupManager.getGroup(jcrMember.getGroup()); 133 if (group != null && project != null) 134 { 135 // only get users from group that are in the project populations 136 Set<String> projectPopulations = project.getSites() 137 .stream() 138 .map(site -> _populationContextHelper.getUserPopulationsOnContext("/sites/" + site.getName(), false)) 139 .flatMap(Set::stream) 140 .collect(Collectors.toSet()); 141 142 // filter the group users that were already in the project 143 Set<UserIdentity> projectUsers = _projectMemberManager.getProjectMembers(project) 144 .stream() 145 .filter(member -> MemberType.USER.toString().equals(member.getType())) 146 .map(member -> member.getUser()) 147 .collect(Collectors.toSet()); 148 149 usersIdentities.addAll(group.getUsers() 150 .stream() 151 .filter(identity -> projectPopulations.contains(identity.getPopulationId())) 152 .filter(identity -> !projectUsers.contains(identity)) 153 .collect(Collectors.toSet())); 154 } 155 } 156 157 List<String> memberMails = new ArrayList<>(); 158 for (UserIdentity memberIdentity : usersIdentities) 159 { 160 User member = _userManager.getUser(memberIdentity); 161 if (member == null) 162 { 163 getLogger().error("Unable to send a notification e-mail to member '{}', as User Manager was unable to get the user.", memberIdentity); 164 return; 165 } 166 String memberMail = member.getEmail(); 167 if (memberMail != null) 168 { 169 memberMails.add(memberMail); 170 } 171 } 172 173 // Retrieve mail sender 174 String sender = Config.getInstance().getValue("smtp.mail.from"); 175 176 for (String memberMail : memberMails) 177 { 178 try 179 { 180 SendMailHelper.sendMail(subject, null, textBody, memberMail, sender); 181 } 182 catch (MessagingException e) 183 { 184 getLogger().warn("Could not send a notification e-mail to " + memberMail, e); 185 } 186 } 187 } 188 189 private JCRProjectMember _getMember(String memberId) 190 { 191 return _resolver.resolveById(memberId); 192 } 193 194 /** 195 * Gets the {@link I18nizableText} for subject of the mail 196 * @param project the project 197 * @param member the member 198 * @return the subject 199 */ 200 protected I18nizableText _getSubjectI18nizableText(Project project, JCRProjectMember member) 201 { 202 return new I18nizableText("plugin." + _pluginName, _getSubjectI18nKey(), _getSubjectParams(project, member)); 203 } 204 205 /** 206 * Gets the i18n subject key 207 * @return the i18n subject key 208 */ 209 protected String _getSubjectI18nKey() 210 { 211 return "PROJECT_MAIL_NOTIFICATION_SUBJECT_MEMBER_ADDED"; 212 } 213 214 /** 215 * Gets the i18n parameters for subject key 216 * @param project the project 217 * @param member the member 218 * @return the i18n parameters 219 */ 220 protected List<String> _getSubjectParams(Project project, JCRProjectMember member) 221 { 222 List<String> i18nParams = new ArrayList<>(); 223 i18nParams.add(StringUtils.defaultString(project.getTitle())); // {0} 224 return i18nParams; 225 } 226 227 /** 228 * Gets the {@link I18nizableText} for body of the mail 229 * @param project the project 230 * @param member the member 231 * @return the body 232 */ 233 protected I18nizableText _getBodyI18nizableText(Project project, JCRProjectMember member) 234 { 235 return new I18nizableText("plugin." + _pluginName, _getBodyI18nKey(), _getBodyParams(project, member)); 236 } 237 238 /** 239 * Gets the i18n body key 240 * @return the i18n body key 241 */ 242 protected String _getBodyI18nKey() 243 { 244 return "PROJECT_MAIL_NOTIFICATION_BODY_MEMBER_ADDED"; 245 } 246 247 /** 248 * Gets the i18n parameters for body key 249 * @param project the project 250 * @param jcrMember the member 251 * @return the i18n parameters 252 */ 253 protected List<String> _getBodyParams(Project project, JCRProjectMember jcrMember) 254 { 255 List<String> i18nParams = new ArrayList<>(); 256 i18nParams.add(StringUtils.defaultString(project.getTitle())); // {0} 257 i18nParams.add(StringUtils.defaultString(_getUrl(project))); // {1} 258 return i18nParams; 259 } 260 261 /** 262 * Gets the URL of the project 263 * @param project The project 264 * @return the URL of the project 265 */ 266 protected String _getUrl(Project project) 267 { 268 return project.getSites().iterator().next().getUrl(); 269 } 270}