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.util.I18nUtils; 039import org.ametys.core.util.mail.SendMailHelper; 040import org.ametys.plugins.repository.AmetysObjectResolver; 041import org.ametys.plugins.workspaces.ObservationConstants; 042import org.ametys.plugins.workspaces.members.JCRProjectMember; 043import org.ametys.plugins.workspaces.members.ProjectMemberManager; 044import org.ametys.plugins.workspaces.members.JCRProjectMember.MemberType; 045import org.ametys.plugins.workspaces.project.objects.Project; 046import org.ametys.runtime.config.Config; 047import org.ametys.runtime.i18n.I18nizableText; 048import org.ametys.runtime.plugin.component.AbstractLogEnabled; 049import org.ametys.runtime.plugin.component.PluginAware; 050import org.ametys.web.population.PopulationContextHelper; 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 if (!Config.getInstance().getValueAsBoolean("workspaces.member.added.send.notification")) 106 { 107 return; 108 } 109 110 Map<String, Object> args = event.getArguments(); 111 112 // Retrieve newly added member 113 String jcrMemberId = (String) args.get(ObservationConstants.ARGS_MEMBER_ID); 114 JCRProjectMember jcrMember = _getMember(jcrMemberId); 115 116 // Retrieve project 117 String projectId = (String) args.get(ObservationConstants.ARGS_PROJECT_ID); 118 Project project = _resolver.resolveById(projectId); 119 120 // Compute subject and body 121 String subject = _i18nUtils.translate(_getSubjectI18nizableText(project, jcrMember)); 122 String textBody = _i18nUtils.translate(_getBodyI18nizableText(project, jcrMember)); 123 124 List<UserIdentity> usersIdentities = new ArrayList<>(); 125 if (MemberType.USER.toString().equals(jcrMember.getType())) 126 { 127 usersIdentities.add(jcrMember.getUser()); 128 } 129 else if (MemberType.GROUP.toString().equals(jcrMember.getType())) 130 { 131 Group group = _groupManager.getGroup(jcrMember.getGroup()); 132 if (group != null && project != null) 133 { 134 // only get users from group that are in the project populations 135 Set<String> projectPopulations = project.getSites() 136 .stream() 137 .map(site -> _populationContextHelper.getUserPopulationsOnContext("/sites/" + site.getName(), false)) 138 .flatMap(Set::stream) 139 .collect(Collectors.toSet()); 140 141 // filter the group users that were already in the project 142 Set<UserIdentity> projectUsers = _projectMemberManager.getProjectMembers(project) 143 .stream() 144 .filter(member -> MemberType.USER.toString().equals(member.getType())) 145 .map(member -> member.getUser()) 146 .collect(Collectors.toSet()); 147 148 usersIdentities.addAll(group.getUsers() 149 .stream() 150 .filter(identity -> projectPopulations.contains(identity.getPopulationId())) 151 .filter(identity -> !projectUsers.contains(identity)) 152 .collect(Collectors.toSet())); 153 } 154 } 155 156 List<String> memberMails = new ArrayList<>(); 157 for (UserIdentity memberIdentity : usersIdentities) 158 { 159 User member = _userManager.getUser(memberIdentity); 160 if (member == null) 161 { 162 getLogger().error("Unable to send a notification e-mail to member '{}', as User Manager was unable to get the user.", memberIdentity); 163 return; 164 } 165 String memberMail = member.getEmail(); 166 if (memberMail != null) 167 { 168 memberMails.add(memberMail); 169 } 170 } 171 172 // Retrieve mail sender 173 String sender = Config.getInstance().getValueAsString("smtp.mail.from"); 174 175 for (String memberMail : memberMails) 176 { 177 try 178 { 179 SendMailHelper.sendMail(subject, null, textBody, memberMail, sender); 180 } 181 catch (MessagingException e) 182 { 183 getLogger().warn("Could not send a notification e-mail to " + memberMail, e); 184 } 185 } 186 } 187 188 private JCRProjectMember _getMember(String memberId) 189 { 190 return _resolver.resolveById(memberId); 191 } 192 193 /** 194 * Gets the {@link I18nizableText} for subject of the mail 195 * @param project the project 196 * @param member the member 197 * @return the subject 198 */ 199 protected I18nizableText _getSubjectI18nizableText(Project project, JCRProjectMember member) 200 { 201 return new I18nizableText("plugin." + _pluginName, _getSubjectI18nKey(), _getSubjectParams(project, member)); 202 } 203 204 /** 205 * Gets the i18n subject key 206 * @return the i18n subject key 207 */ 208 protected String _getSubjectI18nKey() 209 { 210 return "PROJECT_MAIL_NOTIFICATION_SUBJECT_MEMBER_ADDED"; 211 } 212 213 /** 214 * Gets the i18n parameters for subject key 215 * @param project the project 216 * @param member the member 217 * @return the i18n parameters 218 */ 219 protected List<String> _getSubjectParams(Project project, JCRProjectMember member) 220 { 221 List<String> i18nParams = new ArrayList<>(); 222 i18nParams.add(StringUtils.defaultString(project.getTitle())); // {0} 223 return i18nParams; 224 } 225 226 /** 227 * Gets the {@link I18nizableText} for body of the mail 228 * @param project the project 229 * @param member the member 230 * @return the body 231 */ 232 protected I18nizableText _getBodyI18nizableText(Project project, JCRProjectMember member) 233 { 234 return new I18nizableText("plugin." + _pluginName, _getBodyI18nKey(), _getBodyParams(project, member)); 235 } 236 237 /** 238 * Gets the i18n body key 239 * @return the i18n body key 240 */ 241 protected String _getBodyI18nKey() 242 { 243 return "PROJECT_MAIL_NOTIFICATION_BODY_MEMBER_ADDED"; 244 } 245 246 /** 247 * Gets the i18n parameters for body key 248 * @param project the project 249 * @param jcrMember the member 250 * @return the i18n parameters 251 */ 252 protected List<String> _getBodyParams(Project project, JCRProjectMember jcrMember) 253 { 254 List<String> i18nParams = new ArrayList<>(); 255 i18nParams.add(StringUtils.defaultString(project.getTitle())); // {0} 256 i18nParams.add(StringUtils.defaultString(_getUrl(project))); // {1} 257 return i18nParams; 258 } 259 260 /** 261 * Gets the URL of the project 262 * @param project The project 263 * @return the URL of the project 264 */ 265 protected String _getUrl(Project project) 266 { 267// return project.getURL(); 268 return project.getSites().iterator().next().getUrl(); 269 } 270}