1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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 }