001/* 002 * Copyright 2015 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.clientsideelement; 017 018import java.io.IOException; 019import java.io.UnsupportedEncodingException; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import org.apache.avalon.framework.service.ServiceException; 029import org.apache.avalon.framework.service.ServiceManager; 030import org.apache.commons.lang3.StringUtils; 031 032import org.ametys.core.group.Group; 033import org.ametys.core.group.GroupIdentity; 034import org.ametys.core.group.GroupManager; 035import org.ametys.core.ui.Callable; 036import org.ametys.core.ui.ClientSideElement; 037import org.ametys.core.ui.mail.StandardMailBodyHelper; 038import org.ametys.core.ui.mail.StandardMailBodyHelper.MailBodyBuilder; 039import org.ametys.core.user.User; 040import org.ametys.core.user.UserIdentity; 041import org.ametys.core.user.UserManager; 042import org.ametys.core.util.I18nUtils; 043import org.ametys.core.util.mail.SendMailHelper; 044import org.ametys.plugins.core.user.UserHelper; 045import org.ametys.runtime.i18n.I18nizableText; 046import org.ametys.runtime.i18n.I18nizableTextParameter; 047import org.ametys.web.repository.page.Page; 048import org.ametys.web.repository.site.Site; 049 050import jakarta.mail.MessagingException; 051import jakarta.mail.internet.AddressException; 052import jakarta.mail.internet.InternetAddress; 053 054/** 055 * This {@link ClientSideElement} creates a button that will be enabled if page and its hierarchy is valid. 056 * 057 */ 058public class LivePageClientSideElement extends AbstractPageClientSideElement 059{ 060 /** The {@link UserManager} */ 061 private UserManager _userManager; 062 063 /** The {@link UserManager} for front-end users */ 064 private UserManager _foUsersManager; 065 066 /** The {@link GroupManager} for front-end groups */ 067 private GroupManager _foGroupsManager; 068 069 /** The {@link I18nUtils} */ 070 private I18nUtils _i18nUtils; 071 072 private UserHelper _userHelper; 073 074 @Override 075 public void service(ServiceManager smanager) throws ServiceException 076 { 077 super.service(smanager); 078 _userManager = (UserManager) smanager.lookup(UserManager.ROLE); 079 _foUsersManager = (UserManager) smanager.lookup(UserManager.ROLE); 080 _foGroupsManager = (GroupManager) smanager.lookup(GroupManager.ROLE); 081 _i18nUtils = (I18nUtils) smanager.lookup(I18nUtils.ROLE); 082 _userHelper = (UserHelper) smanager.lookup(UserHelper.ROLE); 083 } 084 085 /** 086 * Send a notification for online pages. 087 * @param pageIds the ids of the pages 088 * @param users the users that will be notified 089 * @param groups the groups that will be notified 090 * @param comment the comment of the notifier 091 * @return the result 092 */ 093 @Callable (rights = Callable.CHECKED_BY_IMPLEMENTATION) 094 public Map<String, Object> sendOnlineNotification(List<String> pageIds, List<Map<String, String>> users, List<Map<String, String>> groups, String comment) 095 { 096 List<String> allRightIds = new ArrayList<>(); 097 List<Map<String, Object>> noRightPages = new ArrayList<>(); 098 099 UserIdentity userIdentity = _currentUserProvider.getUser(); 100 User notifier = _userManager.getUser(userIdentity); 101 102 List<UserIdentity> userIdentities = new ArrayList<>(); 103 for (Map<String, String> user : users) 104 { 105 UserIdentity ui = _userHelper.json2userIdentity(user); 106 if (ui != null) 107 { 108 userIdentities.add(ui); 109 } 110 } 111 List<GroupIdentity> groupIdentities = new ArrayList<>(); 112 for (Map<String, String> group : groups) 113 { 114 String id = group.get("groupId"); 115 String groupDirectory = group.get("groupDirectory"); 116 groupIdentities.add(new GroupIdentity(id, groupDirectory)); 117 } 118 119 Map<String, Set<User>> distinctUsersByLanguage = _getDistinctUsersByLanguage(userIdentities, groupIdentities); 120 121 for (String pageId : pageIds) 122 { 123 Page page = _resolver.resolveById(pageId); 124 125 if (!hasRight(page)) 126 { 127 noRightPages.add(Map.of("id", pageId, "title", page.getTitle())); 128 } 129 else 130 { 131 allRightIds.add(pageId); 132 133 String from = _getSender(notifier, page); 134 for (String language : distinctUsersByLanguage.keySet()) 135 { 136 String lang = StringUtils.defaultIfBlank(language, page.getSitemapName()); 137 String subject = _getSubject(notifier, page, lang); 138 139 for (User user : distinctUsersByLanguage.get(language)) 140 { 141 String email = user.getEmail(); 142 if (StringUtils.isNotEmpty(email)) 143 { 144 try 145 { 146 String body = _getBody(notifier, user, page, comment, lang); 147 148 SendMailHelper.newMail() 149 .withSubject(subject) 150 .withHTMLBody(body) 151 .withSender(from) 152 .withRecipient(email) 153 .withAsync(true) 154 .sendMail(); 155 } 156 catch (MessagingException | IOException e) 157 { 158 getLogger().error("Unable to send mail to user " + user.getEmail(), e); 159 } 160 } 161 } 162 } 163 164 } 165 } 166 167 return Map.of("allright-pages", allRightIds, "noright-pages", noRightPages); 168 } 169 170 /** 171 * Get the email sender 172 * @param sender The user responsible for the action 173 * @param page The published page 174 * @return The sender 175 */ 176 private String _getSender (User sender, Page page) 177 { 178 Site site = page.getSite(); 179 180 String displayName = sender.getFullName(); 181 String address = site.getValue("site-mail-from"); 182 183 try 184 { 185 // don't try to write an email parser to retrieve the display name and address 186 InternetAddress internetAddress = new InternetAddress(address); 187 String personal = internetAddress.getPersonal(); 188 if (StringUtils.isNotEmpty(personal)) 189 { 190 displayName = displayName + " (" + personal + ")"; 191 } 192 193 try 194 { 195 internetAddress.setPersonal(displayName); 196 } 197 catch (UnsupportedEncodingException e) 198 { 199 // Should never happens… 200 getLogger().error("Failed to encode the display name '{}' to use it as mail address. The default value from site configuration will be used.", displayName, e); 201 } 202 203 return internetAddress.toUnicodeString(); 204 } 205 catch (AddressException e) 206 { 207 // Should never happens 208 getLogger().error("The 'site-mail-from' value configured for site {} is not a valid email address. The default sender for the application will be used instead.", address); 209 return null; 210 } 211 } 212 213 /** 214 * Get the email subject 215 * @param sender The user responsible for the notification 216 * @param page The published page 217 * @return The email subject 218 */ 219 private String _getSubject (User sender, Page page, String language) 220 { 221 Site site = page.getSite(); 222 223 Map<String, I18nizableTextParameter> subjectI18nParameters = new HashMap<>(); 224 subjectI18nParameters.put("user", new I18nizableText(sender.getFullName())); 225 subjectI18nParameters.put("siteTitle", new I18nizableText(site.getTitle())); 226 subjectI18nParameters.put("pageTitle", new I18nizableText(page.getTitle())); 227 228 I18nizableText msg = new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_SUBJECT", subjectI18nParameters); 229 230 return _i18nUtils.translate(msg, language); 231 } 232 233 /** 234 * Get the email body 235 * @param sender The user responsible for the notification 236 * @param user The recipient of the notification 237 * @param page The published page 238 * @param comment The user's comment. Can be empty or null. 239 * @return The email body 240 * @throws IOException if faile dto build HTML mail body 241 */ 242 private String _getBody (User sender, User user, Page page, String comment, String language) throws IOException 243 { 244 Site site = page.getSite(); 245 String pageUrl = site.getUrl() + "/" + page.getSitemap().getName() + "/" + page.getPathInSitemap() + ".html"; 246 247 Map<String, I18nizableTextParameter> i18nParams = new HashMap<>(); 248 i18nParams.put("sender", new I18nizableText(sender.getFullName())); 249 i18nParams.put("user", new I18nizableText(user.getFullName())); 250 i18nParams.put("siteTitle", new I18nizableText(site.getTitle())); 251 i18nParams.put("siteUrl", new I18nizableText(site.getUrl())); 252 i18nParams.put("pageTitle", new I18nizableText(page.getTitle())); 253 i18nParams.put("pageUrl", new I18nizableText(pageUrl)); 254 if (StringUtils.isNotEmpty(comment)) 255 { 256 String htmlComment = comment.replaceAll("\r?\n", "<br/>"); 257 i18nParams.put("comment", new I18nizableText(htmlComment)); 258 } 259 260 MailBodyBuilder bodyBuilder = StandardMailBodyHelper.newHTMLBody() 261 .withTitle(new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY_TITLE", i18nParams)) 262 .addMessage(new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY", i18nParams)) 263 .addMessage(new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY_LINK_TEXT", i18nParams)) 264 .withLanguage(language) 265 .withLink(pageUrl, new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY_LINK_BUTTON")); 266 267 if (StringUtils.isNotEmpty(comment)) 268 { 269 bodyBuilder.addMessage(new I18nizableText("plugin.web", "PLUGINS_WEB_PAGE_NOTIFY_ONLINE_PUBLICATION_MAIL_BODY_COMMENT", i18nParams)); 270 } 271 272 return bodyBuilder.build(); 273 } 274 275 private Map<String, Set<User>> _getDistinctUsersByLanguage (List<UserIdentity> userIdentities, List<GroupIdentity> groupIdentities) 276 { 277 Set<User> users = new HashSet<>(); 278 279 for (UserIdentity userIdentity : userIdentities) 280 { 281 if (userIdentity != null && StringUtils.isNotEmpty(userIdentity.getLogin()) && StringUtils.isNotEmpty(userIdentity.getPopulationId())) 282 { 283 User user = _foUsersManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin()); 284 if (user != null) 285 { 286 users.add(user); 287 } 288 } 289 } 290 291 for (GroupIdentity groupIdentity : groupIdentities) 292 { 293 if (groupIdentity != null && StringUtils.isNotEmpty(groupIdentity.getId()) && StringUtils.isNotEmpty(groupIdentity.getDirectoryId())) 294 { 295 Group group = _foGroupsManager.getGroup(groupIdentity.getDirectoryId(), groupIdentity.getId()); 296 if (group != null) 297 { 298 Set<UserIdentity> groupUsers = group.getUsers(); 299 for (UserIdentity userIdentity : groupUsers) 300 { 301 User user = _foUsersManager.getUser(userIdentity.getPopulationId(), userIdentity.getLogin()); 302 if (user != null) 303 { 304 users.add(user); 305 } 306 } 307 } 308 } 309 } 310 311 return users.stream() 312 .collect(Collectors.groupingBy( 313 user -> { 314 // user.getLanguage() can be null and groupingBy never accept null keys 315 return StringUtils.defaultIfBlank(user.getLanguage(), StringUtils.EMPTY); 316 }, 317 Collectors.toSet() 318 ) 319 ); 320 } 321}