001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/announcement/tags/sakai_2-4-1/announcement-impl/impl/src/java/org/sakaiproject/announcement/impl/SiteEmailNotificationAnnc.java $
003: * $Id: SiteEmailNotificationAnnc.java 22949 2007-03-19 17:50:15Z josrodri@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.announcement.impl;
021:
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.ResourceBundle;
025: import java.util.Vector;
026:
027: import org.apache.commons.logging.Log;
028: import org.apache.commons.logging.LogFactory;
029: import org.sakaiproject.announcement.api.AnnouncementChannel;
030: import org.sakaiproject.announcement.api.AnnouncementMessage;
031: import org.sakaiproject.announcement.api.AnnouncementMessageEdit;
032: import org.sakaiproject.announcement.api.AnnouncementMessageHeader;
033: import org.sakaiproject.announcement.api.AnnouncementService;
034: import org.sakaiproject.api.app.scheduler.ScheduledInvocationManager;
035: import org.sakaiproject.api.app.scheduler.ScheduledInvocationCommand;
036: import org.sakaiproject.authz.api.SecurityAdvisor;
037: import org.sakaiproject.authz.cover.SecurityService;
038: import org.sakaiproject.component.cover.ServerConfigurationService;
039: import org.sakaiproject.event.api.NotificationService;
040: import org.sakaiproject.event.api.NotificationEdit;
041: import org.sakaiproject.entity.api.Reference;
042: import org.sakaiproject.entity.api.ResourceProperties;
043: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
044: import org.sakaiproject.entity.cover.EntityManager;
045: import org.sakaiproject.event.api.Event;
046: import org.sakaiproject.event.api.Notification;
047: import org.sakaiproject.event.api.NotificationAction;
048: import org.sakaiproject.event.cover.EventTrackingService;
049: import org.sakaiproject.exception.IdUnusedException;
050: import org.sakaiproject.exception.PermissionException;
051: import org.sakaiproject.site.api.Site;
052: import org.sakaiproject.site.cover.SiteService;
053: import org.sakaiproject.time.api.Time;
054: import org.sakaiproject.time.cover.TimeService;
055: import org.sakaiproject.util.SiteEmailNotification;
056: import org.sakaiproject.component.cover.ComponentManager;
057:
058: /**
059: * <p>
060: * SiteEmailNotificationAnnc fills the notification message and headers with details from the announcement message that triggered the notification event.
061: * </p>
062: */
063: public class SiteEmailNotificationAnnc extends SiteEmailNotification
064: implements ScheduledInvocationCommand {
065: private static ResourceBundle rb = ResourceBundle
066: .getBundle("siteemaanc");
067:
068: /** Our logger. */
069: private static Log M_log = LogFactory
070: .getLog(SiteEmailNotificationAnnc.class);
071:
072: private ScheduledInvocationManager scheduledInvocationManager;
073:
074: private ComponentManager componentManager;
075:
076: /**
077: * Construct.
078: */
079: public SiteEmailNotificationAnnc() {
080: }
081:
082: /**
083: * Construct.
084: */
085: public SiteEmailNotificationAnnc(String siteId) {
086: super (siteId);
087: }
088:
089: /**
090: * Inject ScheudledInvocationManager
091: */
092: public void setScheduledInvocationManager(
093: ScheduledInvocationManager service) {
094: scheduledInvocationManager = service;
095: }
096:
097: /**
098: * Inject ComponentManager
099: */
100: public void setComponentManager(ComponentManager componentManager) {
101: this .componentManager = componentManager;
102: }
103:
104: /**
105: * @inheritDoc
106: */
107: protected String getResourceAbility() {
108: return AnnouncementService.SECURE_ANNC_READ;
109: }
110:
111: /**
112: * @inheritDoc
113: */
114: public NotificationAction getClone() {
115: SiteEmailNotificationAnnc clone = new SiteEmailNotificationAnnc();
116: clone.set(this );
117:
118: return clone;
119: }
120:
121: /**
122: * @inheritDoc
123: */
124: public void notify(Notification notification, Event event) {
125: // get the message
126: Reference ref = EntityManager.newReference(event.getResource());
127: AnnouncementMessageEdit msg = (AnnouncementMessageEdit) ref
128: .getEntity();
129: AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg
130: .getAnnouncementHeader();
131:
132: // do not do notification for hidden (draft) messages
133: if (hdr.getDraft())
134: return;
135:
136: // Put here since if release date after now, do not notify
137: // since scheduled notification has been set.
138: Time now = TimeService.newTime();
139:
140: if (now.after(hdr.getDate())) {
141: super .notify(notification, event);
142: }
143: }
144:
145: /**
146: * @inheritDoc
147: */
148: protected String getMessage(Event event) {
149: StringBuffer buf = new StringBuffer();
150: String newline = "<br />\n";
151:
152: // get the message
153: Reference ref = EntityManager.newReference(event.getResource());
154: AnnouncementMessage msg = (AnnouncementMessage) ref.getEntity();
155: AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg
156: .getAnnouncementHeader();
157:
158: // use either the configured site, or if not configured, the site (context) of the resource
159: String siteId = (getSite() != null) ? getSite() : ref
160: .getContext();
161:
162: // get a site title
163: String title = siteId;
164: try {
165: Site site = SiteService.getSite(siteId);
166: title = site.getTitle();
167: } catch (Exception ignore) {
168: }
169:
170: // Now build up the message text.
171: buf.append(rb.getString("An_announcement_has_been"));
172: if (AnnouncementService.SECURE_ANNC_ADD
173: .equals(event.getEvent())) {
174: buf.append(" " + rb.getString("added"));
175: } else {
176: buf.append(" " + rb.getString("updated"));
177: }
178: buf.append(" " + rb.getString("in_the") + " \"");
179: buf.append(title);
180: buf.append("\" " + rb.getString("site_at"));
181: buf.append(" "
182: + ServerConfigurationService.getString("ui.service",
183: "Sakai"));
184: buf.append(" (<a href=\"");
185: buf.append(ServerConfigurationService.getPortalUrl());
186: buf.append("\">");
187: buf.append(ServerConfigurationService.getPortalUrl());
188: buf.append("</a>)");
189: buf.append(newline);
190: buf.append(newline);
191: buf.append(newline);
192: buf.append(rb.getString("Subject") + ": ");
193: buf.append(hdr.getSubject());
194: buf.append(newline);
195: buf.append(newline);
196: buf.append(rb.getString("From") + ": ");
197: buf.append(hdr.getFrom().getDisplayName());
198: buf.append(newline);
199: buf.append(newline);
200: buf.append(rb.getString("Date") + ": ");
201: buf.append(hdr.getDate().toStringLocalFull());
202: buf.append(newline);
203: buf.append(newline);
204: buf.append(rb.getString("Message") + ": ");
205: buf.append(newline);
206: buf.append(newline);
207: buf.append(msg.getBody());
208: buf.append(newline);
209: buf.append(newline);
210:
211: // add any attachments
212: List attachments = hdr.getAttachments();
213: if (attachments.size() > 0) {
214: buf.append(newline + rb.getString("Attachments") + newline);
215: for (Iterator iAttachments = attachments.iterator(); iAttachments
216: .hasNext();) {
217: Reference attachment = (Reference) iAttachments.next();
218: String attachmentTitle = attachment.getProperties()
219: .getPropertyFormatted(
220: ResourceProperties.PROP_DISPLAY_NAME);
221: buf.append("<a href=\"" + attachment.getUrl() + "\">"
222: + attachmentTitle + "</a>" + newline);
223: }
224: }
225:
226: return buf.toString();
227: }
228:
229: /**
230: * @inheritDoc
231: */
232: protected List getHeaders(Event event) {
233: List rv = new Vector();
234:
235: // Set the content type of the message body to HTML
236: rv.add("Content-Type: text/html");
237:
238: // set the subject
239: rv.add("Subject: " + getSubject(event));
240:
241: // from
242: rv.add(getFrom(event));
243:
244: // to
245: rv.add(getTo(event));
246:
247: return rv;
248: }
249:
250: /**
251: * @inheritDoc
252: */
253: protected String getTag(String newline, String title) {
254: // tag the message - HTML version
255: String rv = newline
256: + rb.getString("separator")
257: + newline
258: + rb.getString("this")
259: + " "
260: + ServerConfigurationService.getString("ui.service",
261: "Sakai") + " (<a href=\""
262: + ServerConfigurationService.getPortalUrl() + "\">"
263: + ServerConfigurationService.getPortalUrl() + "</a>) "
264: + rb.getString("forthe") + " " + title + " "
265: + rb.getString("site") + newline
266: + rb.getString("youcan") + newline;
267: return rv;
268: }
269:
270: /**
271: * @inheritDoc
272: */
273: protected boolean isBodyHTML(Event e) {
274: return true;
275: }
276:
277: /**
278: * Format the announcement notification subject line.
279: *
280: * @param event
281: * The event that matched criteria to cause the notification.
282: * @return the announcement notification subject line.
283: */
284: protected String getSubject(Event event) {
285: // get the message
286: Reference ref = EntityManager.newReference(event.getResource());
287: AnnouncementMessage msg = (AnnouncementMessage) ref.getEntity();
288: AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg
289: .getAnnouncementHeader();
290:
291: // use either the configured site, or if not configured, the site (context) of the resource
292: String siteId = (getSite() != null) ? getSite() : ref
293: .getContext();
294:
295: // get a site title
296: String title = siteId;
297: try {
298: Site site = SiteService.getSite(siteId);
299: title = site.getTitle();
300: } catch (Exception ignore) {
301: }
302:
303: // use the message's subject
304: return "[ " + title + " - " + rb.getString("Announcement")
305: + " ] " + hdr.getSubject();
306: }
307:
308: /**
309: * Add to the user list any other users who should be notified about this ref's change.
310: *
311: * @param users
312: * The user list, already populated based on site visit and resource ability.
313: * @param ref
314: * The entity reference.
315: */
316: protected void addSpecialRecipients(List users, Reference ref) {
317: // include any users who have AnnouncementService.SECURE_ALL_GROUPS and getResourceAbility() in the context
318: String contextRef = SiteService.siteReference(ref.getContext());
319:
320: // get the list of users who have SECURE_ALL_GROUPS
321: List allGroupUsers = SecurityService.unlockUsers(
322: AnnouncementService.SECURE_ANNC_ALL_GROUPS, contextRef);
323:
324: // filter down by the permission
325: if (getResourceAbility() != null) {
326: List allGroupUsers2 = SecurityService.unlockUsers(
327: getResourceAbility(), contextRef);
328: allGroupUsers.retainAll(allGroupUsers2);
329: }
330:
331: // remove any in the list already
332: allGroupUsers.removeAll(users);
333:
334: // combine
335: users.addAll(allGroupUsers);
336: }
337:
338: /**
339: * Implementation of command pattern. Will be called by ScheduledInvocationManager
340: * for delayed announcement notifications
341: *
342: * @param opaqueContext
343: * reference (context) for message
344: */
345: public void execute(String opaqueContext) {
346: // get the message
347: final Reference ref = EntityManager.newReference(opaqueContext);
348:
349: // needed to access the message
350: enableSecurityAdvisor();
351:
352: final AnnouncementMessage msg = (AnnouncementMessage) ref
353: .getEntity();
354: final AnnouncementMessageHeader hdr = (AnnouncementMessageHeader) msg
355: .getAnnouncementHeader();
356:
357: // read the notification options
358: final String notification = msg.getProperties().getProperty(
359: "notificationLevel");
360:
361: int noti = NotificationService.NOTI_OPTIONAL;
362: if ("r".equals(notification)) {
363: noti = NotificationService.NOTI_REQUIRED;
364: } else if ("n".equals(notification)) {
365: noti = NotificationService.NOTI_NONE;
366: }
367:
368: final Event event = EventTrackingService.newEvent(
369: "annc.schInv.notify", msg.getReference(), true, noti);
370: EventTrackingService.post(event);
371:
372: final NotificationService notificationService = (NotificationService) ComponentManager
373: .get(org.sakaiproject.event.api.NotificationService.class);
374: NotificationEdit notify = notificationService
375: .addTransientNotification();
376:
377: super .notify(notify, event);
378:
379: // since we build the notification by accessing the
380: // message within the super class, can't remove the
381: // SecurityAdvisor until this point
382: // done with access, need to remove from stack
383: SecurityService.clearAdvisors();
384: }
385:
386: /**
387: * Establish a security advisor to allow the "embedded" azg work to occur
388: * with no need for additional security permissions.
389: */
390: protected void enableSecurityAdvisor() {
391: // put in a security advisor so we can do our podcast work without need
392: // of further permissions
393: SecurityService.pushAdvisor(new SecurityAdvisor() {
394: public SecurityAdvice isAllowed(String userId,
395: String function, String reference) {
396: return SecurityAdvice.ALLOWED;
397: }
398: });
399: }
400:
401: }
|