/**********************************************************************************
* $URL: https://source.sakaiproject.org/svn/announcement/trunk/announcement-impl/impl/src/java/org/sakaiproject/announcement/impl/SiteEmailNotificationAnnc.java $
* $Id: SiteEmailNotificationAnnc.java 129207 2013-08-29 17:39:46Z ottenhoff@longsight.com $
***********************************************************************************
*
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009 The Sakai Foundation
*
* Licensed under the Educational Community License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.opensource.org/licenses/ECL-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**********************************************************************************/
package org.sakaiproject.announcement.impl;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.announcement.api.AnnouncementChannel;
import org.sakaiproject.announcement.api.AnnouncementMessage;
import org.sakaiproject.announcement.api.AnnouncementMessageEdit;
import org.sakaiproject.announcement.api.AnnouncementMessageHeader;
import org.sakaiproject.announcement.api.AnnouncementService;
import org.sakaiproject.api.app.scheduler.ScheduledInvocationCommand;
import org.sakaiproject.api.app.scheduler.ScheduledInvocationManager;
import org.sakaiproject.authz.api.SecurityAdvisor;
import org.sakaiproject.authz.cover.SecurityService;
import org.sakaiproject.component.cover.ComponentManager;
import org.sakaiproject.component.cover.ServerConfigurationService;
import org.sakaiproject.entity.api.Reference;
import org.sakaiproject.entity.api.ResourceProperties;
import org.sakaiproject.entity.cover.EntityManager;
import org.sakaiproject.event.api.Event;
import org.sakaiproject.event.api.Notification;
import org.sakaiproject.event.api.NotificationEdit;
import org.sakaiproject.event.api.NotificationService;
import org.sakaiproject.content.api.ContentHostingService;
import org.sakaiproject.event.cover.EventTrackingService;
import org.sakaiproject.exception.IdUnusedException;
import org.sakaiproject.exception.PermissionException;
import org.sakaiproject.message.api.MessageHeader;
import org.sakaiproject.site.api.Group;
import org.sakaiproject.site.api.Site;
import org.sakaiproject.site.api.ToolConfiguration;
import org.sakaiproject.site.cover.SiteService;
import org.sakaiproject.time.api.Time;
import org.sakaiproject.time.cover.TimeService;
import org.sakaiproject.user.api.User;
import org.sakaiproject.user.api.UserNotDefinedException;
import org.sakaiproject.user.cover.UserDirectoryService;
import org.sakaiproject.util.EmailNotification;
import org.sakaiproject.util.FormattedText;
import org.sakaiproject.util.MergedList;
import org.sakaiproject.util.ResourceLoader;
import org.sakaiproject.util.SiteEmailNotification;
/**
* <p>
* SiteEmailNotificationAnnc fills the notification message and headers with details from the announcement message that triggered the notification event.
* </p>
*/
public class SiteEmailNotificationAnnc extends SiteEmailNotification
implements ScheduledInvocationCommand
{
private static ResourceLoader rb = new ResourceLoader("siteemaanc");
private static final String PORTLET_CONFIG_PARM_MERGED_CHANNELS = "mergedAnnouncementChannels";
/** Our logger. */
private static Log M_log = LogFactory.getLog(SiteEmailNotificationAnnc.class);
private ScheduledInvocationManager scheduledInvocationManager;
/**
* Construct.
*/
public SiteEmailNotificationAnnc()
{
}
/**
* Construct.
*/
public SiteEmailNotificationAnnc(String siteId)
{
super(siteId);
}
/**
* Inject ScheudledInvocationManager
*/
public void setScheduledInvocationManager(
ScheduledInvocationManager service)
{
scheduledInvocationManager = service;
}
/**
* @inheritDoc
*/
protected String getResourceAbility()
{
return AnnouncementService.SECURE_ANNC_READ;
}
/**
* @inheritDoc
*/
public void notify(Notification notification, Event event)
{
// get the message
Reference ref = EntityManager.newReference(event.getResource());
AnnouncementMessageEdit msg = (AnnouncementMessageEdit) ref.getEntity();
AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg.getAnnouncementHeader();
// do not do notification for hidden (draft) messages
if (hdr.getDraft()) return;
// Put here since if release date after now, do not notify
// since scheduled notification has been set.
Time now = TimeService.newTime();
if (now.after(hdr.getDate()))
{
super.notify(notification, event);
}
}
/**
* @inheritDoc
*/
protected String htmlContent(Event event)
{
StringBuilder buf = new StringBuilder();
String newline = "<br />\n";
// get the message
Reference ref = EntityManager.newReference(event.getResource());
AnnouncementMessage msg = (AnnouncementMessage) ref.getEntity();
AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg.getAnnouncementHeader();
// use either the configured site, or if not configured, the site (context) of the resource
String siteId = (getSite() != null) ? getSite() : ref.getContext();
// get a site title
String title = siteId;
String url = ServerConfigurationService.getPortalUrl()+ "/site/"+ siteId;
try
{
Site site = SiteService.getSite(siteId);
title = site.getTitle();
url = site.getUrl(); // Might have a better URL.
}
catch (Exception ignore)
{
M_log.warn("Failed to load site: "+ siteId+ " for: "+ event.getResource());
}
// Now build up the message text.
if (AnnouncementService.SECURE_ANNC_ADD.equals(event.getEvent()))
{
buf.append(rb.getFormattedMessage("noti.header.add", new Object[]{title, url}));
}
else
{
buf.append(rb.getFormattedMessage("noti.header.update", new Object[]{title, url}));
}
buf.append(" " + rb.getString("at_date") + " ");
buf.append(hdr.getDate().toStringLocalFull());
buf.append(newline);
// add any attachments
List<Reference> attachments = hdr.getAttachments();
if (attachments.size() > 0)
{
buf.append(newline + rb.getString("Attachments") + newline);
for (Iterator<Reference> iAttachments = attachments.iterator(); iAttachments.hasNext();)
{
Reference attachment = (Reference) iAttachments.next();
String attachmentTitle = attachment.getProperties().getPropertyFormatted(ResourceProperties.PROP_DISPLAY_NAME);
buf.append("<a href=\"" + attachment.getUrl() + "\">");
buf.append(attachmentTitle);
buf.append("</a>" + newline);
}
}
return buf.toString();
}
/**
* get announcement group information
*/
private static String getAnnouncementGroup(AnnouncementMessage a)
{
if (a.getProperties().getProperty(ResourceProperties.PROP_PUBVIEW) != null
&& a.getProperties().getProperty(ResourceProperties.PROP_PUBVIEW).equals(Boolean.TRUE.toString()))
{
return rb.getString("Public");
}
else if (a.getAnnouncementHeader().getAccess().equals(MessageHeader.MessageAccess.CHANNEL))
{
return rb.getString("Allgroups");
}
else
{
int count = 0;
String allGroupString = "";
try
{
Site site = SiteService.getSite(EntityManager.newReference(a.getReference()).getContext());
for (Iterator i = a.getAnnouncementHeader().getGroups().iterator(); i.hasNext();)
{
Group aGroup = site.getGroup((String) i.next());
if (aGroup != null)
{
count++;
if (count > 1)
{
allGroupString = allGroupString.concat(", ").concat(aGroup.getTitle());
}
else
{
allGroupString = aGroup.getTitle();
}
}
}
}
catch (IdUnusedException e)
{
// No site available.
}
return allGroupString;
}
}
/**
* @inheritDoc
*/
protected List getHeaders(Event event)
{
List rv = super.getHeaders(event);
// Set the content type of the message body to HTML
// rv.add("Content-Type: text/html");
// set the subject
rv.add("Subject: " + getSubject(event));
// from
rv.add(getFromAddress(event));
// to
rv.add(getTo(event));
return rv;
}
/**
* @inheritDoc
*/
protected String getTag(String title, boolean shouldUseHtml)
{
if (shouldUseHtml) {
return rb.getFormattedMessage("noti.tag.html", new Object[]{ServerConfigurationService.getString("ui.service", "Sakai"), ServerConfigurationService.getPortalUrl(), title});
} else {
return rb.getFormattedMessage("noti.tag", new Object[]{ServerConfigurationService.getString("ui.service", "Sakai"), ServerConfigurationService.getPortalUrl(), title});
}
}
/**
* Format the announcement notification subject line.
*
* @param event
* The event that matched criteria to cause the notification.
* @return the announcement notification subject line.
*/
protected String getSubject(Event event)
{
// get the message
Reference ref = EntityManager.newReference(event.getResource());
AnnouncementMessage msg = (AnnouncementMessage) ref.getEntity();
AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg.getAnnouncementHeader();
// use either the configured site, or if not configured, the site (context) of the resource
String siteId = (getSite() != null) ? getSite() : ref.getContext();
// get a site title
String title = siteId;
try
{
Site site = SiteService.getSite(siteId);
title = site.getTitle();
}
catch (Exception ignore)
{
}
// use the message's subject
return rb.getFormattedMessage("noti.subj", new Object[]{title, hdr.getSubject()});
}
/**
* Format the announcement notification from address.
*
* @param event
* The event that matched criteria to cause the notification.
* @return the announcement notification from address.
*/
protected String getFromAddress(Event event)
{
Reference ref = EntityManager.newReference(event.getResource());
//SAK-14831, yorkadam, make site title reflected in 'From:' name instead of default ServerConfigurationService.getString("ui.service", "Sakai");
String siteId = (getSite() != null) ? getSite() : ref.getContext();
String title = "";
try
{
Site site = SiteService.getSite(siteId);
title = site.getTitle();
}
catch(Exception ignore)
{}
String userEmail = "no-reply@" + ServerConfigurationService.getServerName();
String userDisplay = ServerConfigurationService.getString("ui.service", "Sakai");
//String no_reply = "From: \"" + userDisplay + "\" <" + userEmail + ">";
//String no_reply_withTitle = "From: \"" + title + "\" <" + userEmail + ">";
String from = "From: Sakai"; // fallback value
if (title!=null && !title.equals("")){
from = "From: \"" + title + "\" <" + userEmail + ">";
} else {
String fromVal = getFrom(event); // should not return null but better safe than sorry
if (fromVal != null) {
from = fromVal;
}
}
// get the message
AnnouncementMessage msg = (AnnouncementMessage) ref.getEntity();
String userId = msg.getAnnouncementHeader().getFrom().getId();
//checks if "from" email id has to be included? and whether the notification is a delayed notification?. SAK-13512
// SAK-20988 - emailFromReplyable@org.sakaiproject.event.api.NotificationService is deprecated
boolean notificationEmailFromReplyable = ServerConfigurationService.getBoolean("notify.email.from.replyable", false);
if (notificationEmailFromReplyable
&& from.contains("no-reply@")
&& userId != null)
{
try
{
User u = UserDirectoryService.getUser(userId);
userDisplay = u.getDisplayName();
userEmail = u.getEmail();
if ((userEmail != null) && (userEmail.trim().length()) == 0) userEmail = null;
} catch (UserNotDefinedException e) {
M_log.warn("Failed to load user from announcement header: " + userId + ". Will send from no-reply@" + ServerConfigurationService.getServerName() + " instead.");
}
// some fallback positions
if (userEmail == null) userEmail = "no-reply@" + ServerConfigurationService.getServerName();
if (userDisplay == null) userDisplay = ServerConfigurationService.getString("ui.service", "Sakai");
from="From: \"" + userDisplay + "\" <" + userEmail + ">";
}
return from;
}
/**
* Add to the user list any other users who should be notified about this ref's change.
*
* @param users
* The user list, already populated based on site visit and resource ability.
* @param ref
* The entity reference.
*/
protected void addSpecialRecipients(List users, Reference ref)
{
//Reverting the faulty logic of SAK-21798 and SAK-18433.
}
/**
* Implementation of command pattern. Will be called by ScheduledInvocationManager
* for delayed announcement notifications
*
* @param opaqueContext
* reference (context) for message
*/
public void execute(String opaqueContext)
{
// get the message
final Reference ref = EntityManager.newReference(opaqueContext);
// needed to access the message
enableSecurityAdvisorToGetAnnouncement();
final AnnouncementMessage msg = (AnnouncementMessage) ref.getEntity();
final AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg.getAnnouncementHeader();
// read the notification options
final String notification = msg.getProperties().getProperty("notificationLevel");
int noti = NotificationService.NOTI_OPTIONAL;
if ("r".equals(notification))
{
noti = NotificationService.NOTI_REQUIRED;
}
else if ("n".equals(notification))
{
noti = NotificationService.NOTI_NONE;
}
final Event delayedNotificationEvent = EventTrackingService.newEvent("annc.schInv.notify", msg.getReference(), true, noti);
// EventTrackingService.post(event);
final NotificationService notificationService = (NotificationService) ComponentManager.get(org.sakaiproject.event.api.NotificationService.class);
NotificationEdit notify = notificationService.addTransientNotification();
super.notify(notify, delayedNotificationEvent);
// since we build the notification by accessing the
// message within the super class, can't remove the
// SecurityAdvisor until this point
// done with access, need to remove from stack
disableSecurityAdvisor();
}
/**
* Establish a security advisor to allow the "embedded" azg work to occur
* with no need for additional security permissions.
*/
protected void enableSecurityAdvisorToGetAnnouncement() {
// put in a security advisor so we can do our podcast work without need
// of further permissions
SecurityService.pushAdvisor(new SecurityAdvisor() {
public SecurityAdvice isAllowed(String userId, String function,
String reference) {
if (function.equals(AnnouncementService.SECURE_ANNC_READ) || function.equals(ContentHostingService.AUTH_RESOURCE_READ)) // SAK-23300
return SecurityAdvice.ALLOWED;
else
return SecurityAdvice.PASS;
}
});
}
/**
* remove recent add SecurityAdvisor from stack
*/
protected void disableSecurityAdvisor() {
SecurityService.popAdvisor();
}
@Override
protected EmailNotification makeEmailNotification() {
return new SiteEmailNotificationAnnc();
}
@Override
protected String plainTextContent(Event event) {
StringBuilder buf = new StringBuilder();
String newline = "\n\r";
// get the message
Reference ref = EntityManager.newReference(event.getResource());
AnnouncementMessage msg = (AnnouncementMessage) ref.getEntity();
AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg.getAnnouncementHeader();
// use either the configured site, or if not configured, the site (context) of the resource
String siteId = (getSite() != null) ? getSite() : ref.getContext();
// get a site title
String title = siteId;
try
{
Site site = SiteService.getSite(siteId);
title = site.getTitle();
}
catch (Exception ignore)
{
}
// Now build up the message text.
if (AnnouncementService.SECURE_ANNC_ADD.equals(event.getEvent()))
{
buf.append(FormattedText.convertFormattedTextToPlaintext(rb.getFormattedMessage("noti.header.add", new Object[]{title,ServerConfigurationService.getString("ui.service", "Sakai"),ServerConfigurationService.getPortalUrl(), siteId})));
}
else
{
buf.append(FormattedText.convertFormattedTextToPlaintext(rb.getFormattedMessage("noti.header.update", new Object[]{title,ServerConfigurationService.getString("ui.service", "Sakai"),ServerConfigurationService.getPortalUrl(), siteId})));
}
buf.append(newline);
buf.append(newline);
buf.append(newline);
buf.append(rb.getString("Subject"));
buf.append(hdr.getSubject());
//buf.append(rb.getString("Subject") + ": "); buf.append(hdr.getSubject());
buf.append(newline);
buf.append(newline);
buf.append(rb.getString("Group"));
buf.append(getAnnouncementGroup(msg));
buf.append(newline);
buf.append(newline);
buf.append(rb.getString("Message"));
buf.append(newline);
buf.append(newline);
buf.append(FormattedText.convertFormattedTextToPlaintext(msg.getBody()));
buf.append(newline);
buf.append(newline);
// add any attachments
List attachments = hdr.getAttachments();
if (attachments.size() > 0)
{
buf.append(newline + rb.getString("Attachments") + newline);
for (Iterator iAttachments = attachments.iterator(); iAttachments.hasNext();)
{
Reference attachment = (Reference) iAttachments.next();
String attachmentTitle = attachment.getProperties().getPropertyFormatted(ResourceProperties.PROP_DISPLAY_NAME);
buf.append(attachmentTitle + ": " +attachment.getUrl() + newline);
}
}
return buf.toString();
}
}