001/* 002 * Copyright 2013 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.web.repository.page.actions; 017 018import java.io.IOException; 019import java.util.HashMap; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Map; 023import java.util.Set; 024import java.util.stream.Collectors; 025 026import org.apache.avalon.framework.parameters.Parameters; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.cocoon.acting.ServiceableAction; 030import org.apache.cocoon.environment.ObjectModelHelper; 031import org.apache.cocoon.environment.Redirector; 032import org.apache.cocoon.environment.SourceResolver; 033import org.apache.commons.lang3.StringUtils; 034 035import org.ametys.core.group.Group; 036import org.ametys.core.group.GroupIdentity; 037import org.ametys.core.group.GroupManager; 038import org.ametys.core.ui.mail.StandardMailBodyHelper; 039import org.ametys.core.user.CurrentUserProvider; 040import org.ametys.core.user.User; 041import org.ametys.core.user.UserIdentity; 042import org.ametys.core.user.UserManager; 043import org.ametys.core.util.I18nUtils; 044import org.ametys.core.util.language.UserLanguagesManager; 045import org.ametys.core.util.mail.SendMailHelper; 046import org.ametys.plugins.core.user.UserHelper; 047import org.ametys.plugins.repository.AmetysObjectResolver; 048import org.ametys.runtime.config.Config; 049import org.ametys.runtime.i18n.I18nizableText; 050import org.ametys.runtime.i18n.I18nizableTextParameter; 051import org.ametys.web.repository.page.Page; 052import org.ametys.web.repository.site.Site; 053 054import jakarta.mail.MessagingException; 055 056/** 057 * This actions send a email to notify the publication of a page. 058 * 059 */ 060public class SendOnlineNotificationAction extends ServiceableAction 061{ 062 private UserManager _userManager; 063 private GroupManager _groupManager; 064 private AmetysObjectResolver _resolver; 065 private I18nUtils _i18nUtils; 066 private CurrentUserProvider _userProvider; 067 private UserHelper _userHelper; 068 private UserLanguagesManager _userLanguagesManager; 069 070 @Override 071 public void service(ServiceManager smanager) throws ServiceException 072 { 073 super.service(smanager); 074 _userManager = (UserManager) smanager.lookup(UserManager.ROLE); 075 _userProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE); 076 _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE); 077 _groupManager = (GroupManager) smanager.lookup(GroupManager.ROLE); 078 _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE); 079 _userHelper = (UserHelper) smanager.lookup(UserHelper.ROLE); 080 _userLanguagesManager = (UserLanguagesManager) smanager.lookup(UserLanguagesManager.ROLE); 081 } 082 083 @Override 084 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 085 { 086 @SuppressWarnings("unchecked") 087 Map<String, Object> jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 088 089 String pageId = (String) jsParameters.get("id"); 090 Page page = _resolver.resolveById(pageId); 091 092 UserIdentity userIdentity = _userProvider.getUser(); 093 User notifier = _userManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin()); 094 095 String from = _getSender(notifier, page); 096 097 @SuppressWarnings("unchecked") 098 List<Map<String, String>> jsUsers = (List<Map<String, String>>) jsParameters.get("users"); 099 List<UserIdentity> userIdentities = jsUsers.stream() 100 .map(_userHelper::json2userIdentity) 101 .collect(Collectors.toList()); 102 103 @SuppressWarnings("unchecked") 104 List<Map<String, String>> jsGroups = (List<Map<String, String>>) jsParameters.get("groups"); 105 List<GroupIdentity> groupIdentities = jsGroups.stream() 106 .map(jsGroup -> new GroupIdentity(jsGroup.get("id"), jsGroup.get("groupDirectory"))) 107 .collect(Collectors.toList()); 108 109 Map<String, Set<User>> distinctUsersByLanguage = _getDistinctUsersByLanguage(userIdentities, groupIdentities); 110 for (String language : distinctUsersByLanguage.keySet()) 111 { 112 String subject = _getSubject (notifier, page, language); 113 114 for (User user : distinctUsersByLanguage.get(language)) 115 { 116 String email = user.getEmail(); 117 if (StringUtils.isNotEmpty(email)) 118 { 119 try 120 { 121 String body = _getBody(notifier, user, page, language); 122 123 SendMailHelper.newMail() 124 .withSubject(subject) 125 .withHTMLBody(body) 126 .withSender(from) 127 .withRecipient(email) 128 .sendMail(); 129 } 130 catch (MessagingException | IOException e) 131 { 132 getLogger().error("Unable to send mail to user " + user.getEmail(), e); 133 } 134 } 135 } 136 } 137 138 return EMPTY_MAP; 139 } 140 141 /** 142 * Get the email sender 143 * @param sender The user responsible for the action 144 * @param page The published page 145 * @return The sender 146 */ 147 protected String _getSender (User sender, Page page) 148 { 149 if (sender != null && sender.getEmail() != null) 150 { 151 return sender.getFullName() + " <" + sender.getEmail() + ">"; 152 } 153 154 Site site = page.getSite(); 155 String defaultFromValue = Config.getInstance().getValue("smtp.mail.from"); 156 return site.getValue("site-mail-from", false, defaultFromValue); 157 } 158 159 /** 160 * Get the email subject 161 * @param sender The user responsible for the notification 162 * @param page The published page 163 * @param language The language 164 * @return The email subject 165 */ 166 protected String _getSubject (User sender, Page page, String language) 167 { 168 Site site = page.getSite(); 169 170 Map<String, I18nizableTextParameter> subjectI18nParameters = new HashMap<>(); 171 subjectI18nParameters.put("user", new I18nizableText(sender.getFullName())); 172 subjectI18nParameters.put("siteTitle", new I18nizableText(site.getTitle())); 173 subjectI18nParameters.put("pageTitle", new I18nizableText(page.getTitle())); 174 175 I18nizableText msg = new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_SUBJECT", subjectI18nParameters); 176 177 return _i18nUtils.translate(msg, language); 178 } 179 180 /** 181 * Get the email body 182 * @param sender The user responsible for the notification 183 * @param user The recipient of the notification 184 * @param page The published page 185 * @param language The language 186 * @return The email body 187 * @throws IOException if failed to build HTML body 188 */ 189 protected String _getBody (User sender, User user, Page page, String language) throws IOException 190 { 191 Site site = page.getSite(); 192 String pageUrl = site.getUrl() + "/" + page.getSitemap().getName() + "/" + page.getPathInSitemap() + ".html"; 193 194 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 195 i18nParams.put("sender", new I18nizableText(sender.getFullName())); 196 i18nParams.put("user", new I18nizableText(user.getFullName())); 197 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 198 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 199 i18nParams.put("pageTitle", new I18nizableText(page.getTitle())); 200 i18nParams.put("pageUrl", new I18nizableText(pageUrl)); 201 202 return StandardMailBodyHelper.newHTMLBody() 203 .withTitle(new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY_TITLE")) 204 .addMessage(new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY", i18nParams)) 205 .addMessage(new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY_LINK_TEXT", i18nParams)) 206 .withLink(pageUrl, new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY_LINK_BUTTON")) 207 .withLanguage(language) 208 .build(); 209 } 210 211 private Map<String, Set<User>> _getDistinctUsersByLanguage (List<UserIdentity> userIdentities, List<GroupIdentity> groupIdentities) 212 { 213 Set<User> users = new HashSet<>(); 214 215 for (UserIdentity userIdentity : userIdentities) 216 { 217 User user = _userManager.getUser(userIdentity); 218 if (user != null) 219 { 220 users.add(user); 221 } 222 } 223 224 for (GroupIdentity groupIdentity : groupIdentities) 225 { 226 Group group = _groupManager.getGroup(groupIdentity); 227 if (group != null) 228 { 229 Set<UserIdentity> userGroupIdentities = group.getUsers(); 230 for (UserIdentity userGroupIdentity : userGroupIdentities) 231 { 232 User user = _userManager.getUser(userGroupIdentity); 233 if (user != null) 234 { 235 users.add(user); 236 } 237 } 238 } 239 } 240 241 String defaultLanguage = _userLanguagesManager.getDefaultLanguage(); 242 243 return users.stream() 244 .collect(Collectors.groupingBy( 245 user -> { 246 // user.getLanguage() can be null and groupingBy never accept null keys 247 return StringUtils.defaultIfBlank(user.getLanguage(), defaultLanguage); 248 }, 249 Collectors.toSet() 250 ) 251 ); 252 } 253}