/*
* ConcourseConnect
* Copyright 2009 Concursive Corporation
* http://www.concursive.com
*
* This file is part of ConcourseConnect, an open source social business
* software and community platform.
*
* Concursive ConcourseConnect is free software: you can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, version 3 of the License.
*
* Under the terms of the GNU Affero General Public License you must release the
* complete source code for any application that uses any part of ConcourseConnect
* (system header files and libraries used by the operating system are excluded).
* These terms must be included in any work that has ConcourseConnect components.
* If you are developing and distributing open source applications under the
* GNU Affero General Public License, then you are free to use ConcourseConnect
* under the GNU Affero General Public License.
*
* If you are deploying a web site in which users interact with any portion of
* ConcourseConnect over a network, the complete source code changes must be made
* available. For example, include a link to the source archive directly from
* your web site.
*
* For OEMs, ISVs, SIs and VARs who distribute ConcourseConnect with their
* products, and do not license and distribute their source code under the GNU
* Affero General Public License, Concursive provides a flexible commercial
* license.
*
* To anyone in doubt, we recommend the commercial license. Our commercial license
* is competitively priced and will eliminate any confusion about how
* ConcourseConnect can be used and distributed.
*
* ConcourseConnect is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with ConcourseConnect. If not, see <http://www.gnu.org/licenses/>.
*
* Attribution Notice: ConcourseConnect is an Original Work of software created
* by Concursive Corporation
*/
package com.concursive.connect.web.modules.calendar.workflow;
import com.concursive.commons.email.SMTPMessage;
import com.concursive.commons.email.SMTPMessageFactory;
import com.concursive.commons.i18n.LocalizationUtils;
import com.concursive.commons.workflow.ComponentContext;
import com.concursive.commons.workflow.ComponentInterface;
import com.concursive.commons.workflow.ObjectHookComponent;
import com.concursive.connect.config.ApplicationPrefs;
import com.concursive.connect.web.modules.calendar.dao.MeetingAttendee;
import com.concursive.connect.web.modules.calendar.utils.DimDimUtils;
import com.concursive.connect.web.modules.calendar.utils.MeetingInviteesBean;
import com.concursive.connect.web.modules.login.dao.User;
import com.concursive.connect.web.modules.login.utils.UserUtils;
import com.concursive.connect.web.modules.profile.dao.Project;
import freemarker.template.Configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.StringWriter;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
/**
* Sends mails for meeting to attendees
*
* @author Nanda Kumar
* @created June 11, 2009
*/
public class SendEventMeetingInvitation extends ObjectHookComponent implements ComponentInterface {
private static Log LOG = LogFactory.getLog(SendEventMeetingInvitation.class);
private Configuration freeMarkerConfiguration = null;
private MeetingInviteesBean meetingInviteesBean = null;
private freemarker.template.Template templateSubject = null;
private freemarker.template.Template templateBody = null;
private User hostUser = null;
private SMTPMessage message = null;
private Map<String, String> subjectMap = null;
private Map<String, Object> bodyMap = null;
public boolean execute(ComponentContext context) {
try {
//set freemarker configuration
freeMarkerConfiguration = (Configuration) context.getAttribute(ComponentContext.FREEMARKER_CONFIGURATION);
if (freeMarkerConfiguration == null) {
LOG.error("freeMarkerConfiguration is null");
return false;
}
//get the invitees object
meetingInviteesBean = (MeetingInviteesBean) context.getThisObject();
if (meetingInviteesBean == null) {
LOG.error("Cannot find meeting details.");
return false;
}
//check mail is to be send
if (meetingInviteesBean.getAction() != DimDimUtils.ACTION_MEETING_STATUS_CHANGE &&
meetingInviteesBean.getAction() != DimDimUtils.ACTION_MEETING_USER_JOIN &&
meetingInviteesBean.getAction() != DimDimUtils.ACTION_MEETING_APPROVE_JOIN &&
meetingInviteesBean.getMeetingChangeUsers().isEmpty() &&
meetingInviteesBean.getMembersFoundList().isEmpty() &&
meetingInviteesBean.getRejectedUsers().isEmpty() &&
meetingInviteesBean.getCancelledUsers().isEmpty()) {
return false;
}
//get meeting host
hostUser = UserUtils.loadUser(meetingInviteesBean.getMeeting().getOwner());
if (hostUser == null) {
LOG.error("Cannot find meeting host details.");
return false;
}
//mail settings
Map<String, String> prefs = context.getApplicationPrefs();
message = SMTPMessageFactory.createSMTPMessageInstance(prefs);
message.setFrom(prefs.get("EMAILADDRESS"));
message.setType("text/html");
//message map parameters
subjectMap = new HashMap<String, String>();
bodyMap = new HashMap<String, Object>();
//set mail parameter maps
bodyMap.put("meeting", meetingInviteesBean.getMeeting());
bodyMap.put("host", hostUser);
String url = context.getParameter("url");
bodyMap.put("url", url);
// This uses the host's startDate, but a user can override this when sent
bodyMap.put("startDate", formatUserTimestamp(meetingInviteesBean.getMeeting().getStartDate(), hostUser));
Project project = meetingInviteesBean.getProject();
bodyMap.put("eventUrl", url + "/show/" + project.getUniqueId() + "/calendar/" + generateCalendarURL(meetingInviteesBean.getMeeting().getStartDate(), LocalizationUtils.getLocale(prefs.get(ApplicationPrefs.LANGUAGE))));
switch (meetingInviteesBean.getAction()) {
case DimDimUtils.ACTION_MEETING_DIMDIM_EDIT:
if (!meetingInviteesBean.getMeetingChangeUsers().isEmpty() && meetingInviteesBean.getIsModifiedMeeting()) {
sendMeetingChangeMail();
}
if (!meetingInviteesBean.getMembersFoundList().isEmpty()) {
sendMeetingInvitationMail();
}
if (!meetingInviteesBean.getRejectedUsers().isEmpty()) {
sendMeetingInvitationRejectMail();
}
if (!meetingInviteesBean.getCancelledUsers().isEmpty()) {
sendMeetingCancellationMail();
}
break;
case DimDimUtils.ACTION_MEETING_DIMDIM_SCHEDULE:
sendMeetingInvitationMail();
break;
case DimDimUtils.ACTION_MEETING_STATUS_CHANGE:
sendInvitationStatusMail();
break;
case DimDimUtils.ACTION_MEETING_DIMDIM_CANCEL:
sendMeetingCancellationMail();
break;
case DimDimUtils.ACTION_MEETING_USER_JOIN:
sendUserJoinedMail();
break;
case DimDimUtils.ACTION_MEETING_APPROVE_JOIN:
sendUserJoinStatusMail();
break;
default:
LOG.error("Meeting action not known - " + meetingInviteesBean.getAction());
return false;
}
return true;
} catch (Exception e) {
LOG.error("Exception when trying to send mail");
e.printStackTrace(System.out);
return false;
}
}
/*
* Sends the meeting invitation rejected mail to attendees
*/
private boolean sendMeetingInvitationRejectMail() throws Exception {
LOG.debug("Trying to send invitation rejected mail");
//set mail templates
templateSubject = freeMarkerConfiguration.getTemplate("event_meeting_invitation_reject_email_subject-text.ftl");
templateBody = freeMarkerConfiguration.getTemplate("event_meeting_invitation_reject_email_body-html.ftl");
//set replyto mailid
message.setReplyTo(hostUser.getEmail(), hostUser.getNameFirstLast());
//send mails to all meeting invitees
for (User thisInvitee : meetingInviteesBean.getRejectedUsers()) {
//set additional mail parameter maps
bodyMap.put("invitee", thisInvitee);
// override the date formatting
if (thisInvitee.getLocale() != null) {
bodyMap.put("startDate", formatUserTimestamp(meetingInviteesBean.getMeeting().getStartDate(), thisInvitee));
}
//set to mailid
message.setTo(thisInvitee.getEmail());
//build the templates
StringWriter subjectTextWriter = new StringWriter();
templateSubject.process(subjectMap, subjectTextWriter);
StringWriter bodyTextWriter = new StringWriter();
templateBody.process(bodyMap, bodyTextWriter);
message.setSubject(subjectTextWriter.toString());
message.setBody(bodyTextWriter.toString());
LOG.debug(bodyTextWriter.toString());
//send mail
if (message.send() == 0) {
LOG.debug("invitation rejected email sent to " + thisInvitee.getNameFirstLast() + " - " + thisInvitee.getEmail());
} else {
LOG.debug("invitation rejected email not sent to " + thisInvitee.getNameFirstLast() + " - " + thisInvitee.getEmail());
}
}
return true;
}
/*
* Sends the meeting cancelled mail to attendees
*/
private boolean sendMeetingCancellationMail() throws Exception {
LOG.debug("Trying to send meeting cancellation mail");
//set mail templates
templateSubject = freeMarkerConfiguration.getTemplate("event_meeting_cancellation_email_subject-text.ftl");
templateBody = freeMarkerConfiguration.getTemplate("event_meeting_cancellation_email_body-html.ftl");
//set replyto mailid
message.setReplyTo(hostUser.getEmail(), hostUser.getNameFirstLast());
//send mails to all meeting invitees
for (User thisInvitee : meetingInviteesBean.getCancelledUsers()) {
//set additional mail parameter maps
bodyMap.put("invitee", thisInvitee);
// override the date formatting
if (thisInvitee.getLocale() != null) {
bodyMap.put("startDate", formatUserTimestamp(meetingInviteesBean.getMeeting().getStartDate(), thisInvitee));
}
//set to mailid
message.setTo(thisInvitee.getEmail());
//build the templates
StringWriter subjectTextWriter = new StringWriter();
templateSubject.process(subjectMap, subjectTextWriter);
StringWriter bodyTextWriter = new StringWriter();
templateBody.process(bodyMap, bodyTextWriter);
message.setSubject(subjectTextWriter.toString());
message.setBody(bodyTextWriter.toString());
LOG.debug(bodyTextWriter.toString());
//send mail
if (message.send() == 0) {
LOG.debug("meeting cancelled email sent to " + thisInvitee.getNameFirstLast() + " - " + thisInvitee.getEmail());
} else {
LOG.debug("meeting cancelled email not sent to " + thisInvitee.getNameFirstLast() + " - " + thisInvitee.getEmail());
}
}
return true;
}
/*
* Sends the meeting invitation status change mail of attendees to meeting host
*/
private boolean sendInvitationStatusMail() throws Exception {
LOG.debug("Trying to send meeting invitation status change mail");
//set mail templates
templateSubject = freeMarkerConfiguration.getTemplate("event_meeting_attendeestatus_email_subject-text.ftl");
templateBody = freeMarkerConfiguration.getTemplate("event_meeting_attendeestatus_email_body-html.ftl");
//set additional mail parameter maps
User inviteeUser = UserUtils.loadUser(meetingInviteesBean.getMeetingAttendee().getUserId());
bodyMap.put("invitee", inviteeUser);
// override the date formatting
if (inviteeUser.getLocale() != null) {
bodyMap.put("startDate", formatUserTimestamp(meetingInviteesBean.getMeeting().getStartDate(), inviteeUser));
}
bodyMap.put("status", "is tentative about");
if (meetingInviteesBean.getMeetingAttendee().getDimdimStatus() == MeetingAttendee.STATUS_DIMDIM_ACCEPTED) {
bodyMap.put("status", "has accepted");
}
if (meetingInviteesBean.getMeetingAttendee().getDimdimStatus() == MeetingAttendee.STATUS_DIMDIM_DECLINED) {
bodyMap.put("status", "has declined");
}
//build the templates
StringWriter subjectTextWriter = new StringWriter();
templateSubject.process(subjectMap, subjectTextWriter);
StringWriter bodyTextWriter = new StringWriter();
templateBody.process(bodyMap, bodyTextWriter);
message.setSubject(subjectTextWriter.toString());
message.setBody(bodyTextWriter.toString());
LOG.debug(bodyTextWriter.toString());
//set replyto and to mailids
message.setReplyTo(inviteeUser.getEmail(), inviteeUser.getNameFirstLast());
message.setTo(hostUser.getEmail());
//send mail
if (message.send() == 0) {
LOG.debug("invitiation status change email sent to " + hostUser.getNameFirstLast() + " - " + hostUser.getEmail());
return true;
}
LOG.debug("invitiation status change email not sent to " + hostUser.getNameFirstLast() + " - " + hostUser.getEmail());
return false;
}
/*
* Sends meeting change mail to attendees
*/
private boolean sendMeetingChangeMail() throws Exception {
LOG.debug("Trying to send meeting change mail");
//send edit mail only if the meeting record value was changed.
if (meetingInviteesBean.getIsModifiedMeeting()) {
//set mail templates
templateSubject = freeMarkerConfiguration.getTemplate("event_meeting_change_email_subject-text.ftl");
templateBody = freeMarkerConfiguration.getTemplate("event_meeting_change_email_body-html.ftl");
//set additional mail parameter maps
// bodyMap.put("previousStartDate", longDateToString(meetingInviteesBean.getPreviousMeetingStartDate()));
// bodyMap.put("previousTitle", meetingInviteesBean.getPreviousMeetingTitle());
for (User thisInvitee : meetingInviteesBean.getMeetingChangeUsers()) {
//set additional mail parameter maps
bodyMap.put("invitee", thisInvitee);
// override the date formatting
if (thisInvitee.getLocale() != null) {
bodyMap.put("startDate", formatUserTimestamp(meetingInviteesBean.getMeeting().getStartDate(), thisInvitee));
}
//set to mailid
message.setTo(thisInvitee.getEmail());
//build the templates
StringWriter subjectTextWriter = new StringWriter();
templateSubject.process(subjectMap, subjectTextWriter);
StringWriter bodyTextWriter = new StringWriter();
templateBody.process(bodyMap, bodyTextWriter);
message.setSubject(subjectTextWriter.toString());
message.setBody(bodyTextWriter.toString());
LOG.debug(bodyTextWriter.toString());
//send mail
if (message.send() == 0) {
LOG.debug("meeting change email sent to " + thisInvitee.getNameFirstLast() + " - " + thisInvitee.getEmail());
} else {
LOG.debug("meeting change email not sent to " + thisInvitee.getNameFirstLast() + " - " + thisInvitee.getEmail());
}
}
return true;
}
return false;
}
/*
* Sends meeting invitation mails
*/
private boolean sendMeetingInvitationMail() throws Exception {
LOG.debug("Trying to send meeting invitation mail");
//set mail templates
templateSubject = freeMarkerConfiguration.getTemplate("event_meeting_invitation_email_subject-text.ftl");
templateBody = freeMarkerConfiguration.getTemplate("event_meeting_invitation_email_body-html.ftl");
//set replyto mailid
message.setReplyTo(hostUser.getEmail(), hostUser.getNameFirstLast());
//send mails to all meeting invitees
for (User thisInvitee : meetingInviteesBean.getMembersFoundList().keySet()) {
//set additional mail parameter maps
bodyMap.put("invitee", thisInvitee);
// override the date formatting
if (thisInvitee.getLocale() != null) {
bodyMap.put("startDate", formatUserTimestamp(meetingInviteesBean.getMeeting().getStartDate(), thisInvitee));
}
//set to mailid
message.setTo(thisInvitee.getEmail());
//build the templates
StringWriter subjectTextWriter = new StringWriter();
templateSubject.process(subjectMap, subjectTextWriter);
StringWriter bodyTextWriter = new StringWriter();
templateBody.process(bodyMap, bodyTextWriter);
message.setSubject(subjectTextWriter.toString());
message.setBody(bodyTextWriter.toString());
LOG.debug(bodyTextWriter.toString());
//send mail
if (message.send() == 0) {
LOG.debug("meeting invitation email sent to " + thisInvitee.getNameFirstLast() + " - " + thisInvitee.getEmail());
} else {
LOG.debug("meeting invitation email not sent to " + thisInvitee.getNameFirstLast() + " - " + thisInvitee.getEmail());
}
}
return true;
}
/*
* Sends mail to meeting host regarding user adding himself to meeting
*/
private boolean sendUserJoinedMail() throws Exception {
LOG.debug("Trying to send user joined to meeting mail");
//set mail templates
templateSubject = freeMarkerConfiguration.getTemplate("event_meeting_userjoined_email_subject-text.ftl");
templateBody = freeMarkerConfiguration.getTemplate("event_meeting_userjoined_email_body-html.ftl");
//set additional mail parameter maps
User inviteeUser = UserUtils.loadUser(meetingInviteesBean.getMeetingAttendee().getUserId());
bodyMap.put("invitee", inviteeUser);
// override the date formatting
if (inviteeUser.getLocale() != null) {
bodyMap.put("startDate", formatUserTimestamp(meetingInviteesBean.getMeeting().getStartDate(), inviteeUser));
}
bodyMap.put("status", "but is tentative about");
if (meetingInviteesBean.getMeetingAttendee().getDimdimStatus() == MeetingAttendee.STATUS_DIMDIM_ACCEPTED ||
meetingInviteesBean.getMeetingAttendee().getDimdimStatus() == MeetingAttendee.STATUS_DIMDIM_APPROVE_YES) {
bodyMap.put("status", "and will be");
}
//build the templates
StringWriter subjectTextWriter = new StringWriter();
templateSubject.process(subjectMap, subjectTextWriter);
StringWriter bodyTextWriter = new StringWriter();
templateBody.process(bodyMap, bodyTextWriter);
message.setSubject(subjectTextWriter.toString());
message.setBody(bodyTextWriter.toString());
LOG.debug(bodyTextWriter.toString());
//set replyto and to mailids
message.setReplyTo(inviteeUser.getEmail(), inviteeUser.getNameFirstLast());
message.setTo(hostUser.getEmail());
//send mail
if (message.send() == 0) {
LOG.debug("user joined to meeting email sent to " + hostUser.getNameFirstLast() + " - " + hostUser.getEmail());
return true;
}
LOG.debug("user joined to meeting email not sent to " + hostUser.getNameFirstLast() + " - " + hostUser.getEmail());
return false;
}
/*
* Sends approval or rejection mail to user on adding himself to meeting
*/
private boolean sendUserJoinStatusMail() throws Exception {
LOG.debug("Trying to send approve or reject mail for user join");
//set mail templates
templateSubject = freeMarkerConfiguration.getTemplate("event_meeting_joinstatus_email_subject-text.ftl");
templateBody = freeMarkerConfiguration.getTemplate("event_meeting_joinstatus_email_body-html.ftl");
//set additional mail parameter maps
User inviteeUser = UserUtils.loadUser(meetingInviteesBean.getMeetingAttendee().getUserId());
bodyMap.put("invitee", inviteeUser);
// override the date formatting
if (inviteeUser.getLocale() != null) {
bodyMap.put("startDate", formatUserTimestamp(meetingInviteesBean.getMeeting().getStartDate(), inviteeUser));
}
bodyMap.put("status", "rejected");
if (meetingInviteesBean.getMeetingAttendee().getDimdimStatus() == MeetingAttendee.STATUS_DIMDIM_ACCEPTED) {
bodyMap.put("status", "approved");
}
//build the templates
StringWriter subjectTextWriter = new StringWriter();
templateSubject.process(subjectMap, subjectTextWriter);
StringWriter bodyTextWriter = new StringWriter();
templateBody.process(bodyMap, bodyTextWriter);
message.setSubject(subjectTextWriter.toString());
message.setBody(bodyTextWriter.toString());
LOG.debug(bodyTextWriter.toString());
//set replyto and to mailids
message.setReplyTo(hostUser.getEmail(), hostUser.getNameFirstLast());
message.setTo(inviteeUser.getEmail());
//send mail
if (message.send() == 0) {
LOG.debug("user joined status to meeting email sent to " + inviteeUser.getNameFirstLast() + " - " + inviteeUser.getEmail());
return true;
}
LOG.debug("user joined status to meeting email not sent to " + inviteeUser.getNameFirstLast() + " - " + inviteeUser.getEmail());
return false;
}
private String formatUserTimestamp(Timestamp timestamp, User user) {
// Use the user's locale
SimpleDateFormat formatter = (SimpleDateFormat) SimpleDateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.SHORT, user.getLocale());
// Adjust the date/time based on any timezone
if (user.getTimeZone() != null) {
TimeZone tz = TimeZone.getTimeZone(user.getTimeZone());
formatter.setTimeZone(tz);
}
return formatter.format(timestamp);
}
/**
* The calendar module has a specific format for the url and must match the application's locale
*
* @param date
* @param locale
* @return
*/
private String generateCalendarURL(Timestamp date, Locale locale) {
SimpleDateFormat dtFormater = new SimpleDateFormat("yyyy-MM-dd", locale);
return dtFormater.format(date);
}
public String getDescription() {
return "Sends event meeting mails to meeting attendees";
}
}