View Javadoc
1   /*
2    * Copyright (C) 2003-2019 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.calendar.service.impl;
18  
19  import java.io.ByteArrayInputStream;
20  import java.io.OutputStream;
21  import java.text.DateFormat;
22  import java.text.SimpleDateFormat;
23  import java.util.*;
24  import java.util.Map.Entry;
25  import java.util.concurrent.atomic.AtomicBoolean;
26  
27  import org.exoplatform.calendar.service.Attachment;
28  import org.exoplatform.calendar.service.CalendarEvent;
29  import org.exoplatform.calendar.service.CalendarService;
30  import org.exoplatform.calendar.service.CalendarSetting;
31  import org.exoplatform.calendar.service.Utils;
32  import org.exoplatform.calendar.util.CalendarUtils;
33  import org.exoplatform.commons.utils.DateUtils;
34  import org.exoplatform.container.ExoContainerContext;
35  import org.exoplatform.container.PortalContainer;
36  import org.exoplatform.portal.Constants;
37  import org.exoplatform.services.log.ExoLogger;
38  import org.exoplatform.services.log.Log;
39  import org.exoplatform.services.mail.MailService;
40  import org.exoplatform.services.organization.OrganizationService;
41  import org.exoplatform.services.organization.User;
42  import org.exoplatform.services.organization.UserProfile;
43  import org.exoplatform.services.resources.LocaleContextInfo;
44  import org.exoplatform.services.resources.ResourceBundleService;
45  import org.exoplatform.services.security.ConversationState;
46  
47  public class MailNotification {
48  
49    private ResourceBundle        ressourceBundle;
50  
51    private MailService           mailService;
52  
53    private OrganizationService   organizationService;
54  
55    private CalendarService       calendarService;
56  
57    public static final   String FIELD_MESSAGE = "messageName" ;
58    public static final   String FIELD_EVENT = "eventName";
59    public static final   String FIELD_DESCRIPTION = "description";
60    public static final   String FIELD_ATTACHMENTS = "attachments";
61    public static final   String FIELD_FROM = "from";
62    public static final   String FIELD_TO = "to";
63    public static final   String FIELD_PLACE = "place";
64    public static final   String FIELD_MEETING = "participant";
65  
66  
67    
68    private static final  AtomicBoolean                 isRBLoaded           = new AtomicBoolean();
69  
70    private static final Log      LOG = ExoLogger.getExoLogger(MailNotification.class);
71    
72    public MailNotification(MailService mailService,
73                            OrganizationService organizationService,
74                            CalendarService calendarService) {
75      this.mailService = mailService;
76      this.organizationService = organizationService;
77      this.calendarService = calendarService;
78    }
79  
80    public void sendEmail(CalendarEvent event, String username) throws Exception {
81      User invitor = (User) ConversationState.getCurrent().getAttribute("UserProfile");
82      if (invitor == null)
83        return;
84      List<Attachment> atts = event.getAttachment();
85      Map<String, String> eXoIdMap = new HashMap<>();   
86  
87      StringBuilder toDisplayName = new StringBuilder("");
88      StringBuilder sbAddress = new StringBuilder("");
89      for (String s : event.getParticipant()) {
90        User user = organizationService.getUserHandler().findUserByName(s);
91        if (user == null)  {
92          continue;
93        }
94        
95        eXoIdMap.put(user.getEmail(), s);
96        if (toDisplayName.length() > 0) {
97          toDisplayName.append(",");
98        }
99        toDisplayName.append(user.getDisplayName());
100       if (sbAddress.length() > 0)
101         sbAddress.append(",");
102       sbAddress.append(user.getEmail());
103     }
104 
105     User user = organizationService.getUserHandler().findUserByName(username);
106     byte[] icsFile;
107     try (OutputStream out = calendarService.getCalendarImportExports(calendarService.ICALENDAR)
108                                            .exportEventCalendar(username,
109                                                                 event.getCalendarId(),
110                                                                 event.getCalType(),
111                                                                 event.getId())) {
112       icsFile = out.toString().getBytes("UTF-8");
113     }
114 
115     String emailList = sbAddress.toString();
116     String userId;
117     for (String userEmail : emailList.split(CalendarUtils.COMMA)) {
118       if (CalendarUtils.isEmpty(userEmail)) continue;
119 
120       userId = eXoIdMap.get(userEmail);
121       ResourceBundle res = null;
122 
123       CalendarSetting calendarSetting = calendarService.getCalendarSetting(userId);
124       UserProfile userProfile = organizationService.getUserProfileHandler().findUserProfileByName(userId);
125       String lang = userProfile == null ? null : userProfile.getUserInfoMap().get(Constants.USER_LANGUAGE);
126       ResourceBundleService ressourceBundleService = ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(ResourceBundleService.class);
127       if (lang != null && !lang.isEmpty()) {
128         res = ressourceBundleService.getResourceBundle(Utils.RESOURCEBUNDLE_NAME, LocaleContextInfo.getLocale(lang));
129       }
130 
131       if (res == null) {
132         res = getResourceBundle(ressourceBundleService);
133       }
134 
135       DateFormat df = new SimpleDateFormat(calendarSetting.getDateFormat() + " " + calendarSetting.getTimeFormat());
136       df.setTimeZone(DateUtils.getTimeZone(calendarSetting.getTimeZone()));
137 
138       org.exoplatform.services.mail.Message message = new org.exoplatform.services.mail.Message();
139       message.setSubject(buildMailSubject(event, df, res));
140       message.setBody(getBodyMail(buildMailBody(invitor,
141                                                 event,
142                                                 toDisplayName.toString(),
143                                                 df,
144                                                 CalendarUtils.generateTimeZoneLabel(calendarSetting.getTimeZone()),
145                                                 res),
146                                   eXoIdMap,
147                                   userEmail,
148                                   invitor,
149                                   event,
150                                   res));
151       message.setTo(userEmail);
152       message.setMimeType(Utils.MIMETYPE_TEXTHTML);
153       message.setFrom(user.getDisplayName() + "<" + System.getProperty("exo.email.smtp.from") + ">");
154       message.setReplyTo(user.getEmail());
155 
156       if (icsFile != null) {
157         try (ByteArrayInputStream is = new ByteArrayInputStream(icsFile)) {
158           org.exoplatform.services.mail.Attachment attachmentCal = new org.exoplatform.services.mail.Attachment();
159           attachmentCal.setInputStream(is);
160           attachmentCal.setName("icalendar.ics");
161           attachmentCal.setMimeType("text/calendar");
162           message.addAttachment(attachmentCal);
163         }
164       }
165 
166       if (!atts.isEmpty()) {
167         for (Attachment att : atts) {
168           org.exoplatform.services.mail.Attachment attachment = new org.exoplatform.services.mail.Attachment();
169           attachment.setInputStream(att.getInputStream());
170           attachment.setMimeType(att.getMimeType());
171           attachment.setName(att.getName());
172           message.addAttachment(attachment);
173         }
174       }
175       mailService.sendMessage(message);
176     }
177   }
178 
179   private String getBodyMail(Object sbBody,
180                              Map<String, String> eXoIdMap,
181                              String userEmail,
182                              User invitor,
183                              CalendarEvent event,
184                              ResourceBundle res) throws Exception {
185     StringBuilder body = new StringBuilder(sbBody.toString());
186     String eXoId = CalendarUtils.isEmpty(eXoIdMap.get(userEmail)) ? "null":eXoIdMap.get(userEmail);
187     body.append("<tr>");
188     body.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">");
189     body.append(" </td><td> <a href=\""
190         + getReplyInvitationLink(org.exoplatform.calendar.service.Utils.ACCEPT, invitor, userEmail, eXoId, event) + "\" >"
191         + getLabel(res, "yes") + "</a>" + " - " + "<a href=\""
192         + getReplyInvitationLink(org.exoplatform.calendar.service.Utils.NOTSURE, invitor, userEmail, eXoId, event) + "\" >"
193         + getLabel(res, "notSure") + "</a>" + " - " + "<a href=\""
194         + getReplyInvitationLink(org.exoplatform.calendar.service.Utils.DENY, invitor, userEmail, eXoId, event) + "\" >"
195         + getLabel(res, "no") + "</a>");
196     body.append("</td></tr>");
197     body.append("<tr>");
198     body.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">");
199     body.append(getLabel(res, "seeMoreDetails") + " </td><td><a href=\""
200         + getReplyInvitationLink(org.exoplatform.calendar.service.Utils.ACCEPT_IMPORT, invitor, userEmail, eXoId, event) + "\" >"
201         + getLabel(res, "importToExoCalendar") + "</a> " + getLabel(res, "or") + " <a href=\""
202         + getReplyInvitationLink(org.exoplatform.calendar.service.Utils.JUMP_TO_CALENDAR, invitor, userEmail, eXoId, event)
203         + "\" >" + getLabel(res, "jumpToExoCalendar") + "</a>");
204     body.append("</td></tr>");
205     body.append("</tbody>");
206     body.append("</table>");
207     body.append("</div>");
208     return body.toString();
209   }
210 
211   private static String getReplyInvitationLink(int answer,
212                                         User invitor,
213                                         String invitee,
214                                         String eXoId,
215                                         CalendarEvent event) throws Exception {
216     String portalURL = CalendarUtils.getServerBaseUrl() + "/" + PortalContainer.getCurrentPortalContainerName();
217     String restURL = portalURL + "/" + PortalContainer.getCurrentRestContextName();
218     String calendarURL = portalURL + "/intranet/calendar";
219 
220     if (answer == org.exoplatform.calendar.service.Utils.ACCEPT || answer == org.exoplatform.calendar.service.Utils.DENY
221         || answer == org.exoplatform.calendar.service.Utils.NOTSURE) {
222       return (restURL + "/cs/calendar" + CalendarUtils.INVITATION_URL + event.getCalendarId() + "/" + event.getCalType() + "/"
223           + event.getId() + "/" + invitor.getUserName() + "/" + invitee + "/" + eXoId + "/" + answer);
224     }
225     if (answer == org.exoplatform.calendar.service.Utils.ACCEPT_IMPORT) {
226       return (calendarURL + CalendarUtils.INVITATION_IMPORT_URL + invitor.getUserName() + "/" + event.getId() + "/" + event.getCalType());
227     }
228     if (answer == org.exoplatform.calendar.service.Utils.JUMP_TO_CALENDAR) {
229       return (calendarURL + CalendarUtils.INVITATION_DETAIL_URL + invitor.getUserName() + "/" + event.getId() + "/" + event.getCalType());
230     }
231     return "";
232   }
233 
234   private Object buildMailBody(User invitor,
235                                CalendarEvent event,
236                                String toDisplayName,
237                                DateFormat df,
238                                String timezone,
239                                ResourceBundle res) throws Exception {
240     List<Attachment> atts = event.getAttachment();
241 
242     StringBuilder sbBody = new StringBuilder();
243     sbBody.append("<div style=\"margin: 20px auto; padding: 8px; background: rgb(224, 236, 255) none repeat scroll 0%; -moz-background-clip: -moz-initial; -moz-background-origin: -moz-initial; -moz-background-inline-policy: -moz-initial; width: 500px;\">");
244     sbBody.append("<table style=\"margin: 0px; padding: 0px; border-collapse: collapse; border-spacing: 0px; width: 100%; line-height: 16px;\">");
245     sbBody.append("<tbody>");
246     sbBody.append("<tr>");
247     sbBody.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap; \">"
248         + getLabel(res, "fromWho") + ":</td>");
249     sbBody.append("<td style=\"padding: 4px;\"> " + invitor.getDisplayName() + " (" + invitor.getEmail() + ")" + " </td>");
250     sbBody.append("</tr>");
251 
252     sbBody.append("<tr>");
253     sbBody.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">"
254         + getLabel(res, FIELD_MESSAGE) + ":</td>");
255     sbBody.append("<td style=\"padding: 4px;\">" + event.getMessage() + "</td>");
256     sbBody.append("</tr>");
257 
258     sbBody.append("<tr>");
259     sbBody.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">"
260         + getLabel(res, FIELD_EVENT) + ":</td>");
261     sbBody.append("<td style=\"padding: 4px;\">" + event.getSummary() + "</td>");
262     sbBody.append("</tr>");
263     sbBody.append("<tr>");
264     sbBody.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">"
265         + getLabel(res, FIELD_DESCRIPTION) + ":</td>");
266     sbBody.append("<td style=\"padding: 4px;\">"
267         + (event.getDescription() != null && event.getDescription().trim().length() > 0 ? event.getDescription() : " ")
268         + "</td>");
269     sbBody.append("</tr>");
270     sbBody.append("<tr>");
271     sbBody.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">"
272         + getLabel(res, "when") + ":</td>");
273     sbBody.append("<td style=\"padding: 4px;\"> <div>" + getLabel(res, FIELD_FROM) + ": "
274         + df.format(event.getFromDateTime()) + " " + timezone + "</div>");
275     sbBody.append("<div>" + getLabel(res, FIELD_TO) + ": " + df.format(event.getToDateTime()) + " " + timezone
276         + "</div></td>");
277     sbBody.append("</tr>");
278     sbBody.append("<tr>");
279     sbBody.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">"
280         + getLabel(res, FIELD_PLACE) + ":</td>");
281     sbBody.append("<td style=\"padding: 4px;\">"
282         + (event.getLocation() != null && event.getLocation().trim().length() > 0 ? event.getLocation() : " ") + "</td>");
283     sbBody.append("</tr>");
284     sbBody.append("<tr>");
285     sbBody.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">"
286         + getLabel(res, FIELD_MEETING) + "</td>");
287     sbBody.append("<td style=\"padding: 4px;\">" + toDisplayName + "</td>");
288     sbBody.append("</tr>");
289     if (!atts.isEmpty()) {
290       sbBody.append("<tr>");
291       sbBody.append("<td style=\"padding: 4px;  text-align: right; vertical-align: top; white-space:nowrap;\">"
292           + getLabel(res, FIELD_ATTACHMENTS) + ":</td>");
293       StringBuilder sbf = new StringBuilder();
294       for (Attachment att : atts) {
295         if (sbf.length() > 0)
296           sbf.append(",");
297         sbf.append(att.getName());
298       }
299       sbBody.append("<td style=\"padding: 4px;\"> (" + atts.size() + ") " + sbf.toString() + " </td>");
300       sbBody.append("</tr>");
301     }
302 
303     return sbBody.toString();
304   }
305 
306   private String buildMailSubject(CalendarEvent event, DateFormat df, ResourceBundle res) {
307     StringBuilder sbSubject = new StringBuilder("[" + getLabel(res, "invitation") + "] ");
308     sbSubject.append(event.getSummary());
309     Date fromDateTime = event.getFromDateTime();
310     if(fromDateTime != null) {
311       sbSubject.append(" ");
312       sbSubject.append(df.format(fromDateTime));
313     }
314 
315     return sbSubject.toString();
316   }
317 
318   public String getLabel(ResourceBundle res, String label) {
319     if(res != null) {
320       String resKey = ".label." + label;
321       try {
322         return res.getString(resKey);
323       } catch (MissingResourceException e) {
324         return label;
325       }
326     } else {
327       return label;
328     }
329   }
330   
331   public ResourceBundle getResourceBundle(ResourceBundleService ressourceBundleService) throws Exception {
332     if (!isRBLoaded.get()) {
333       synchronized (isRBLoaded) {
334         if (!isRBLoaded.get()) {
335           try {
336             ressourceBundle = ressourceBundleService.getResourceBundle(Utils.RESOURCEBUNDLE_NAME, Locale.getDefault());
337           } catch (MissingResourceException e) {
338             ressourceBundle = null;
339           }
340           isRBLoaded.set(true);
341         }
342       }
343     } 
344     return ressourceBundle;
345   }
346 }