Scarab commit: svn commit: r10760 - trunk/src/java/org/tigris/scarab: actions actions/admin notification tools
Hussayn Dabbous <[email protected]>
| Newsgroups | gmane.comp.java.scarab.cvs |
|---|---|
| Message-ID | <[email protected]> |
Author: dabbous
Date: 2009-06-21 03:10:29-0700
New Revision: 10760
Removed:
trunk/src/java/org/tigris/scarab/notification/ScarabNewNotificationManager.java
trunk/src/java/org/tigris/scarab/notification/ScarabOldNotificationManager.java
Modified:
trunk/src/java/org/tigris/scarab/actions/ChangeNotificationStatus.java
trunk/src/java/org/tigris/scarab/actions/admin/AppConfigurationSettings.java
trunk/src/java/org/tigris/scarab/notification/NotificationManager.java
trunk/src/java/org/tigris/scarab/notification/ScarabNotificationManager.java
trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java
Log:
cleanup of NotificationManager code (for upcoming add on features), removed OldNotificationManager (this was planned since 2 years), removed unnecessary import statements, fixed a typo in the code documentation, etc...
Modified: trunk/src/java/org/tigris/scarab/actions/ChangeNotificationStatus.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/actions/ChangeNotificationStatus.java?view=diff&pathrev=10760&r1=10759&r2=10760
==============================================================================
--- trunk/src/java/org/tigris/scarab/actions/ChangeNotificationStatus.java (original)
+++ trunk/src/java/org/tigris/scarab/actions/ChangeNotificationStatus.java 2009-06-21 03:10:29-0700
@@ -1,35 +1,25 @@
package org.tigris.scarab.actions;
-import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.apache.torque.TorqueException;
-import org.apache.torque.om.NumberKey;
import org.apache.torque.om.ObjectKey;
-import org.apache.torque.util.Criteria;
import org.apache.turbine.RunData;
import org.apache.turbine.TemplateContext;
-import org.apache.turbine.tool.IntakeTool;
-import org.tigris.scarab.actions.base.RequireLoginFirstAction;
import org.tigris.scarab.actions.base.ScarabTemplateAction;
import org.tigris.scarab.notification.ActivityType;
import org.tigris.scarab.notification.NotificationManagerFactory;
-import org.tigris.scarab.notification.ScarabNewNotificationManager;
import org.tigris.scarab.om.Module;
import org.tigris.scarab.om.NotificationFilter;
import org.tigris.scarab.om.NotificationFilterManager;
import org.tigris.scarab.om.NotificationFilterPeer;
import org.tigris.scarab.om.NotificationStatus;
-import org.tigris.scarab.om.NotificationStatusManager;
import org.tigris.scarab.om.NotificationStatusPeer;
-import org.tigris.scarab.om.Query;
-import org.tigris.scarab.om.QueryManager;
import org.tigris.scarab.om.ScarabUser;
import org.tigris.scarab.tools.ScarabGlobalTool;
-import org.tigris.scarab.tools.ScarabLocalizationTool;
import org.tigris.scarab.tools.ScarabRequestTool;
import org.tigris.scarab.tools.localization.L10NKeySet;
import org.tigris.scarab.util.ScarabConstants;
Modified: trunk/src/java/org/tigris/scarab/actions/admin/AppConfigurationSettings.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/actions/admin/AppConfigurationSettings.java?view=diff&pathrev=10760&r1=10759&r2=10760
==============================================================================
--- trunk/src/java/org/tigris/scarab/actions/admin/AppConfigurationSettings.java (original)
+++ trunk/src/java/org/tigris/scarab/actions/admin/AppConfigurationSettings.java 2009-06-21 03:10:29-0700
@@ -60,7 +60,6 @@
import org.apache.turbine.TemplateContext;
import org.apache.turbine.Turbine;
import org.tigris.scarab.actions.base.RequireLoginFirstAction;
-import org.tigris.scarab.notification.ScarabNewNotificationManager;
import org.tigris.scarab.tools.localization.L10NKeySet;
import org.tigris.scarab.util.Log;
import org.tigris.scarab.util.comparators.StartsWithPredicate;
Modified: trunk/src/java/org/tigris/scarab/notification/NotificationManager.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/notification/NotificationManager.java?view=diff&pathrev=10760&r1=10759&r2=10760
==============================================================================
--- trunk/src/java/org/tigris/scarab/notification/NotificationManager.java (original)
+++ trunk/src/java/org/tigris/scarab/notification/NotificationManager.java 2009-06-21 03:10:29-0700
@@ -43,7 +43,7 @@
/**
* This method should add a notification to be processed. It must decide if
- * the activities are relevant to the recipients and filter acordingly.
+ * the activities are relevant to the recipients and filter accordingly.
*
* @see addActivityNotification(NotificationEvent, EmailContext,
* ActivitySet, Issue, Set, Set)
@@ -93,5 +93,5 @@
* @return
*/
public Integer getManagerId();
-
+
}
Removed: trunk/src/java/org/tigris/scarab/notification/ScarabNewNotificationManager.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/notification/ScarabNewNotificationManager.java?view=markup&pathrev=10759
Modified: trunk/src/java/org/tigris/scarab/notification/ScarabNotificationManager.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/notification/ScarabNotificationManager.java?view=diff&pathrev=10760&r1=10759&r2=10760
==============================================================================
--- trunk/src/java/org/tigris/scarab/notification/ScarabNotificationManager.java (original)
+++ trunk/src/java/org/tigris/scarab/notification/ScarabNotificationManager.java 2009-06-21 03:10:29-0700
@@ -46,27 +46,837 @@
* individuals on behalf of CollabNet.
*/
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.List;
+import javax.servlet.http.HttpServlet;
+
+import org.apache.log4j.Logger;
+import org.apache.torque.TorqueException;
+import org.apache.turbine.Turbine;
+import org.tigris.scarab.om.Activity;
+import org.tigris.scarab.om.ActivitySet;
+import org.tigris.scarab.notification.ActivityType;
+import org.tigris.scarab.om.Attribute;
+import org.tigris.scarab.om.AttributePeer;
+import org.tigris.scarab.om.AttributeValue;
+import org.tigris.scarab.om.GlobalParameterManager;
+import org.tigris.scarab.om.Issue;
+import org.tigris.scarab.om.IssueManager;
+import org.tigris.scarab.om.Module;
+import org.tigris.scarab.om.NotificationFilterManager;
+import org.tigris.scarab.om.NotificationStatus;
+import org.tigris.scarab.om.NotificationStatusPeer;
+import org.tigris.scarab.om.ScarabUser;
+import org.tigris.scarab.om.ScarabUserManager;
+import org.tigris.scarab.services.cache.ScarabCache;
+import org.tigris.scarab.tools.localization.L10NKey;
+import org.tigris.scarab.tools.localization.L10NKeySet;
+import org.tigris.scarab.tools.localization.LocalizationKey;
+import org.tigris.scarab.util.Email;
+import org.tigris.scarab.util.EmailContext;
+import org.tigris.scarab.util.Log;
+import org.tigris.scarab.util.ScarabLink;
+
/**
- * This class is for backward compatibility. It is only for delegating
- * functionality to the new scarab notification manager. The preferred
- * method of utilisation is via configuration. please see
- * property "scarab.notificationmanager.classname" for further
- * information.
+ * This class provides the default implementation for the Notification Manager.
+ * It will queue the notifications, and then process them consolidating by user and issue, so
+ * a user will only get ONE email for issue containing every activity relating this
+ * issue since the last notification. The time after which a notification is sent out
+ * to the user(s) is calculated from the time when the last modification has been applied
+ * plus the scarab.notificationmanager.issuequiettime (customizable via build/runtime properties)
* <br/>
*
- * @author jorgeuriarte
+ * @authors jorgeuriarte, hdab
*/
-public class ScarabNotificationManager extends ScarabNewNotificationManager
+public class ScarabNotificationManager extends HttpServlet implements NotificationManager
{
- /**
- * Important note:
- * If you want to utilise the old NotificationManager available
- * before release b21, please customize your properties as:
- *
- * scarab.notificationmanager.classname=tigris.scarab.notification.ScarabOldNotificationManager
- *
- * We recommend to NOT use the old notification manager implementation.
- * We only keep it for backward compatibility. The old implementation
- * may be removed in a later release.
- */
+
+ public static Logger log = Log.get(ScarabNotificationManager.class
+ .getName());
+
+ private static final Integer NOTIFICATION_MANAGER_ID = new Integer(1);
+
+ public Integer getManagerId()
+ {
+ return NOTIFICATION_MANAGER_ID;
+ }
+
+
+ /**
+ * Receives an activitySet from which to generate notification. Current
+ * implementation does only online email sending, with no aggregation or
+ * filtering.
+ */
+ public void addActivityNotification(ActivityType event,
+ ActivitySet activitySet, Issue issue, ScarabUser fromUser)
+ {
+ this.addActivityNotification(
+ event,
+ activitySet,
+ issue,
+ null,
+ null,
+ fromUser);
+ }
+
+ /**
+ * Long version of the addActivityNotification method, allowing to pass the sets of
+ * users involved as 'To' or 'CC'.
+ */
+ public void addActivityNotification(ActivityType event, ActivitySet activitySet, Issue issue,
+ Set toUsers, Set ccUsers, ScarabUser fromUser)
+ {
+ if (log.isDebugEnabled())
+ log.debug("addActivityNotification: " + issue.getIdPrefix()
+ + issue.getIssueId() + "-" + event.getCode());
+ this.queueNotifications(activitySet, issue, fromUser);
+ }
+
+ /**
+ * Queue the notifications for the passed activity set.
+ *
+ * @param activitySet
+ */
+ private void queueNotifications(ActivitySet activitySet, Issue issue, ScarabUser fromUser)
+ {
+ try
+ {
+ NotificationStatus notification = null;
+ for (Iterator it = activitySet.getActivityList().iterator(); it.hasNext(); )
+ {
+ Activity act = (Activity)it.next();
+ if (act.getIssue().equals(issue))
+ {
+ notification = new NotificationStatus(Email.getArchiveUser(), act);
+ NotificationStatusPeer.doInsert(notification);
+
+ Set users = issue.getAllUsersToEmail(AttributePeer.EMAIL_TO);
+ users.addAll(issue.getAllUsersToEmail(AttributePeer.CC_TO));
+ users.addAll(activitySet.getRemovedUsers(issue));
+
+ // FIXME: Should we still make difference between CC & TO? If so...
+ // ...do we need this info in the notification_status table??
+
+ // FIXME: SCB1439. does the user really have permissions
+ // to view this attribute?
+
+ Integer moduleId = issue.getModuleId();
+ String activityType = act.getActivityType();
+
+ for (Iterator itusers = users.iterator(); itusers.hasNext(); )
+ {
+ ScarabUser user = (ScarabUser)itusers.next();
+ Integer userId = user.getUserId();
+
+ boolean isSelf = userId.equals(fromUser.getUserId());
+ boolean wantsNotification = NotificationFilterManager.isNotificationEnabledFor(moduleId, userId, isSelf, activityType);
+
+ if(wantsNotification)
+ {
+ notification = new NotificationStatus(user, act);
+ NotificationStatusPeer.doInsert(notification);
+ }
+ }
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ log.error("queueNotifications(): ",e);
+ }
+ }
+
+
+ /**
+ * This method process the pending notifications and send them
+ * to the users.
+ * If the NotificationManager is not activated, this method
+ * will not do anything (the mail should have been sent already).
+ */
+ public void sendPendingNotifications()
+ {
+ ScarabCache.clear();
+ log.debug("sendPendingNotifications(): Collect pending notifications ...");
+ // It is now guaranteed, that the notifications arrive in order of CreationDate!
+ List pending = NotificationStatusPeer.getPendingNotifications();
+
+ if(pending == null)
+ {
+ log.warn("sendPendingNotifications(): ...Could not retrieve pending notifications from Database. Try again later.");
+ return;
+ }
+ log.debug("rearrange pending notifications per issue ...");
+ Map pendingIssueMap = getPendingIssueMap(pending);
+
+ Map issueActivities = new HashMap();
+ Map archiverActivities = new HashMap();
+ Set creators = new HashSet();
+
+ //Process each Issue ...
+ Iterator pendingIssuesIterator = pendingIssueMap.keySet().iterator();
+ int pendingIssueCount = pendingIssueMap.size();
+ int processedIssueCount = 0;
+ while( pendingIssuesIterator.hasNext())
+ {
+ Issue issue = (Issue)pendingIssuesIterator.next();
+
+ String issueId;
+ try
+ {
+ issueId = issue.getUniqueId();
+ }
+ catch (TorqueException te)
+ {
+ log.error("sendPendingNotifications(): No access to Issue [" + te + "]");
+ // Can not proceed with this issue !
+ continue;
+ }
+
+
+ // clear volatile data structures ...
+ issueActivities.clear();
+ archiverActivities.clear();
+ creators.clear();
+
+ NotificationStatus firstNotification = null;
+ NotificationStatus lastNotification = null;
+ NotificationStatus mostRelevantNotification = null;
+
+ List notificationList = (List)pendingIssueMap.get(issue);
+
+ //Process each Notification for current Issue ...
+ for (Iterator it = notificationList.iterator(); it.hasNext();)
+ {
+ NotificationStatus currentNotification = (NotificationStatus) it.next();
+ if(firstNotification == null)
+ {
+ firstNotification = currentNotification;
+ }
+ if(!it.hasNext())
+ {
+ lastNotification = currentNotification;
+ }
+
+ mostRelevantNotification = getMostRelevantNotification(currentNotification, mostRelevantNotification, issue);
+ try
+ {
+ Integer receiverId = currentNotification.getReceiverId();
+ ScarabUser receiver = null;
+ if(receiverId.equals(Email.getArchiveUser().getUserId()))
+ {
+ receiver = Email.getArchiveUser();
+ }
+ else
+ {
+ receiver = ScarabUserManager.getInstance(receiverId);
+ }
+ creators.add(currentNotification.getCreator());
+
+ Map userActivities = getActivitiesForUser(issueActivities, receiver);
+ addActivity(currentNotification, userActivities);
+ addActivity(currentNotification, archiverActivities);
+ }
+ catch (TorqueException te)
+ {
+ log.error("sendPendingNotifications(): No access to current Scarab User" + te);
+ // We can continue processing here. We just don't know how to process Emails
+ // for the current user.
+ }
+ }
+
+ /*
+ * Now we got all notifications for current issue sorted by receivers
+ * and collected in issueActivities. We now can iterate throug the
+ * issueActivities and send one E-Mail per receiver for this issue:
+ */
+
+ Long issueTime = lastNotification.getCreationDate().getTime();
+ if (isOldEnough(issueTime))
+ {
+ processedIssueCount += 1;
+
+ // ===========================================================
+ // Determine the changeKey (l10n) for the most relevant notification
+ // The resolved l10n key will appear in the subject line of the email!
+ // Note: If the key can not be resolved, the key itself will be
+ // used as replacement without further notification! (to be changed in the future)
+ // ===========================================================
+ LocalizationKey changeKey;
+ if (isStatusNotification(mostRelevantNotification, issue))
+ {
+ changeKey = getStatusKey(mostRelevantNotification, issue);
+ }
+ else
+ {
+ changeKey = getNotificationKey(mostRelevantNotification, issue);
+ }
+
+ log.debug("processing notifications for issue : ["+issueId+"]");
+ Iterator userIterator = getUsersToNotifyIterator(issueActivities);
+ while( userIterator.hasNext())
+ {
+ ScarabUser user = (ScarabUser) userIterator.next();
+
+ EmailContext ectx = new EmailContext();
+ ectx.setIssue(issue);
+ ectx.setLinkTool(new ScarabLink());
+ ectx.put("creators", creators);
+ ectx.put("firstNotification", firstNotification);
+ ectx.put("lastNotification", lastNotification);
+ ectx.put("changeKey",changeKey);
+
+ ectx.put("cr", "\n"); // for email template to get a reliable Carriage return
+
+ Map groupedActivities = (Map) issueActivities.get(user);
+ if(groupedActivities == null)
+ {
+ groupedActivities = archiverActivities;
+ }
+ addActivitiesToEmailContext(ectx, groupedActivities);
+
+ Exception exception = null;
+ try
+ {
+ this.sendEmail(ectx, issue, user);
+ }
+ catch (Exception e)
+ {
+ exception = e;
+ log.error("Failed to send email :" + e);
+ }
+
+ updateNotificationRepository(groupedActivities, exception);
+ }
+
+ }
+ else
+ {
+ log.debug("Issue " + issueId + ": Is not old enough.");
+ }
+ }
+ if(pendingIssueCount > 0)
+ {
+ log.info("sendPendingNotifications(): processed " + processedIssueCount + " of " + pendingIssueCount + " pending issues.");
+ }
+ else
+ {
+ log.debug("sendPendingNotifications(): nothing todo.");
+ }
+ }
+
+
+ /**
+ * Return the L10NKey associated to the Attribute change.
+ * @param notification
+ * @param issue
+ * @return
+ */
+ private LocalizationKey getNotificationKey(NotificationStatus notification, Issue issue)
+ {
+ LocalizationKey result = null;
+ ActivityType activityType = notification.getActivityType();
+ result = activityType.getNotificationKey();
+ return result;
+ }
+
+ /**
+ * Return the L10NKey representation of the status value.
+ * Note: This method strongly assumes, that the given notification
+ * contains an Activity of type ATTRIBUTE_CHANGED and the contained
+ * attribute is expected to be a status-attribute. This method throws
+ * an exception, if these constraints are not fulfilled!
+ * In case of success, the method returns an L10N key. This key is currently
+ * not expected to be backed by an l10n resource. Please consider this
+ * as a preparation for a future enhancement, where it will become possible
+ * to define localized attribute values. For now the key is a verbatim copy of the
+ * attribute value.
+ * @param notification
+ * @param issue
+ * @return
+ */
+ private LocalizationKey getStatusKey(NotificationStatus notification, Issue issue)
+ {
+ LocalizationKey result = null;
+ try
+ {
+ Attribute attribute = notification.getActivity().getAttribute();
+ if (attribute == null || !getIsStatusAttribute(attribute, issue))
+ {
+ throw new IllegalArgumentException("Expected a notification containing a status attribute.");
+ }
+
+ String name = attribute.getName();
+ AttributeValue av = issue.getAttributeValue(name);
+ if(av != null)
+ {
+ result = new L10NKey(av.getValue()); // interpret the value as a L10NKey (for future use!)
+ }
+ else
+ {
+ throw new IllegalArgumentException("Received a notification containing a status attribute without a given value.");
+ }
+ }
+ catch (TorqueException e)
+ {
+ Log.get().warn("Database acess error while retrieving status attribute value.(ignored)");
+ Log.get().warn("db layer reported: ["+e.getMessage()+"]");
+ }
+
+ return result;
+ }
+
+
+
+
+
+ private NotificationStatus getMostRelevantNotification(
+ NotificationStatus currentNotification,
+ NotificationStatus mostRelevantNotification,
+ Issue issue)
+ {
+ ActivityType currentActivityType = currentNotification.getActivityType();
+
+ // =====================================================================
+ // Check if the Issue status has changed. This is of highest relevance.
+ // =====================================================================
+ if(currentActivityType.equals(ActivityType.ATTRIBUTE_CHANGED))
+ {
+ try
+ {
+ Attribute attribute = currentNotification.getActivity().getAttribute();
+ if (getIsStatusAttribute(attribute, issue))
+ {
+ String name = attribute.getName();
+ AttributeValue av = issue.getAttributeValue(name);
+ if(av != null)
+ {
+ return currentNotification; // that is the most relevant notification!
+ }
+ }
+ }
+ catch (TorqueException e)
+ {
+ Log.get().warn("Database acess error while retrieving status attribute value.(ignored)");
+ Log.get().warn("db layer reported: ["+e.getMessage()+"]");
+ }
+ }
+
+ if(mostRelevantNotification == null)
+ {
+ mostRelevantNotification = currentNotification;
+ }
+ else
+ {
+ if( !isStatusNotification(mostRelevantNotification, issue))
+ {
+ ActivityType mostRelevantActivityType = mostRelevantNotification.getActivityType();
+ if ( currentActivityType.getPriority() >= mostRelevantActivityType.getPriority() )
+ {
+ mostRelevantNotification = currentNotification;
+ }
+ }
+ }
+ return mostRelevantNotification;
+ }
+
+ /**
+ * Tell if the notification contains a status-attribute.
+ * @param notification
+ * @param issue
+ * @return
+ */
+ private boolean isStatusNotification(NotificationStatus notification, Issue issue)
+ {
+ boolean result = false;
+ {
+ ActivityType activityType = notification.getActivityType();
+ if(activityType.equals(ActivityType.ATTRIBUTE_CHANGED))
+ {
+ try
+ {
+ Attribute attribute = notification.getActivity().getAttribute();
+ if (getIsStatusAttribute(attribute, issue))
+ {
+ String name = attribute.getName();
+ AttributeValue av = issue.getAttributeValue(name);
+ if(av != null)
+ {
+ result = true;
+ }
+ }
+ }
+ catch (TorqueException e)
+ {
+ Log.get().warn("Database acess error while retrieving status attribute value.(ignored)");
+ Log.get().warn("db layer reported: ["+e.getMessage()+"]");
+ }
+ }
+ }
+ return result;
+ }
+
+
+
+
+ /**
+ * This method returns true, if the attribute is identified as
+ * the "status_attribute" for the current module/issue_type combination.
+ *
+ * NOTE: The "status_attribute" id is searched in SCARAB_GLOBAL_ATTRIBUTE
+ * first, although it currently should not find any entry there. In a future
+ * release it is intended to allow a more sophisticated controll over what
+ * a status attribute is and how it should be rendered e.g. into email subject.
+ *
+ * @param attribute
+ * @param issue
+ * @return
+ * @throws TorqueException
+ */
+ private boolean getIsStatusAttribute(Attribute attribute, Issue issue)
+ throws TorqueException
+ {
+ boolean result=false;
+ Module module = issue.getModule();
+ String key = "status_attribute_"+attribute.getAttributeId();
+
+ String statusId = GlobalParameterManager.getString(key,module);
+ if(!statusId.equals(""))
+ {
+ result = true; // the attribute IS the status_attribute
+ }
+ else
+ {
+ String name = attribute.getName().toLowerCase();
+ String globalStatusAttributeName = GlobalParameterManager.getString("scarab.common.status.id").toLowerCase();
+ if(name.equals(globalStatusAttributeName))
+ {
+ result=true;
+ }
+ }
+ return result;
+ }
+
+
+
+ /**
+ * @param ectx
+ * @param groupedActivities
+ */
+ private void addActivitiesToEmailContext(EmailContext ectx, Map groupedActivities)
+ {
+ ectx.put("ActivityIssue", groupedActivities
+ .get(L10NKeySet.ActivityIssue));
+ ectx.put("ActivityAttributeChanges", groupedActivities
+ .get(L10NKeySet.ActivityAttributeChanges));
+ ectx.put("ActivityPersonnelChanges", groupedActivities
+ .get(L10NKeySet.ActivityPersonnelChanges));
+ ectx.put("ActivityComments", groupedActivities
+ .get(L10NKeySet.ActivityComments));
+ ectx.put("ActivityAssociatedInfo", groupedActivities
+ .get(L10NKeySet.ActivityAssociatedInfo));
+ ectx.put("ActivityDependencies", groupedActivities
+ .get(L10NKeySet.ActivityDependencies));
+ ectx.put("ActivityReasons", consolidateActivityReasons(groupedActivities));
+ }
+
+
+ /**
+ * @param issueActivities
+ * @return
+ */
+ private Iterator getUsersToNotifyIterator(Map issueActivities)
+ {
+ return issueActivities.keySet().iterator();
+ }
+
+ /**
+ * Return a list of strings with the reasons for the activities to be
+ * notified.
+ *
+ * @return
+ */
+ private List consolidateActivityReasons(Map activities)
+ {
+ Set set = new HashSet();
+ List list = new ArrayList();
+ for (Iterator it = activities.values().iterator(); it.hasNext(); )
+ {
+ List l = (List)it.next();
+ for (Iterator nots = l.iterator(); nots.hasNext(); )
+ {
+ NotificationStatus ns = (NotificationStatus)nots.next();
+ String comment = ns.getComment();
+ if (comment != null && !set.contains(comment))
+ {
+ set.add(comment);
+ list.add(comment);
+ }
+ }
+ }
+ return list;
+ }
+
+
+ /**
+ * Update the Notification status in the database. If exception is
+ * supplied, this method assumes, an error has occured and sets
+ * he status to DEFERRED. Otherwise the E-Mail is considered to be
+ * delivered with success and the Notification status is set to SENT.
+ * This is done for ALL activities beeing reported to this user in this
+ * issue.
+ * @param groupedActivities
+ * @param exception
+ */
+ private void updateNotificationRepository(Map groupedActivities, Exception exception)
+ {
+ /**
+ * Update the notifications' status with the result of the
+ * email sending
+ */
+ for (Iterator confirm = groupedActivities.values()
+ .iterator(); confirm.hasNext();)
+ {
+ List notifications = (List) confirm.next();
+ for (Iterator n = notifications.iterator(); n.hasNext();)
+ {
+ NotificationStatus notif = (NotificationStatus) n
+ .next();
+ if (exception == null)
+ {
+ //notif.setStatus(NotificationStatus.SENT);
+ try
+ {
+ NotificationStatusPeer.doDelete(notif);
+ }
+ catch(TorqueException te)
+ {
+ exception = te;
+ }
+ }
+
+ if(exception != null)
+ {
+ notif.setStatus(NotificationStatus.DEFERRED);
+ notif.setComment(exception.getMessage());
+ }
+ try
+ {
+ notif.save();
+ }
+ catch (Exception e)
+ {
+ log.error("sendPendingNotifications(): Updating: " + e, e);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * @param issueActivities
+ * @param user
+ * @return
+ */
+ private Map getActivitiesForUser(Map issueActivities, ScarabUser user)
+ {
+ Map userActivities = (Map) issueActivities.get(user);
+ if (null == userActivities)
+ {
+ userActivities = new HashMap();
+ issueActivities.put(user, userActivities);
+ }
+ return userActivities;
+ }
+
+
+ /**
+ * @param notification
+ * @param userActivities
+ */
+ private void addActivity(NotificationStatus notification, Map userActivities)
+ {
+ LocalizationKey activityGroup = getActivityGroup(notification.getActivityType());
+ List typeNotifications = (List) userActivities.get(activityGroup);
+ if (null == typeNotifications)
+ {
+ typeNotifications = new ArrayList();
+ userActivities.put(activityGroup, typeNotifications);
+ }
+
+ // We will only add this notification to the user's list if it's not
+ // already present.
+ boolean bAlreadyPresent = false;
+ for (Iterator it = typeNotifications.iterator(); it.hasNext() && !bAlreadyPresent; )
+ {
+ NotificationStatus not = (NotificationStatus)it.next();
+ bAlreadyPresent = (not.getActivityId().equals(notification.getActivityId()));
+ }
+ if (!bAlreadyPresent)
+ {
+ typeNotifications.add(notification);
+ }
+ }
+
+
+ /**
+ * @param pending
+ */
+ private Map getPendingIssueMap(List pending)
+ {
+ Map issueMap = new HashMap();
+ for (Iterator it = pending.iterator(); it.hasNext();)
+ {
+ NotificationStatus notification = (NotificationStatus) it.next();
+ Issue issue = null;
+ try
+ {
+ issue = IssueManager.getInstance(notification.getIssueId());
+
+ /**
+ * Only add the notification when it's related to THIS issue (needed
+ * for notification related to dependencies or moving, so we don't
+ * get duplicated descriptions)
+ */
+
+ if (notification.getActivity().getIssue().equals(issue))
+ {
+
+ List notificationList = (List)issueMap.get(issue);
+ if(notificationList == null)
+ {
+ notificationList = new ArrayList();
+ issueMap.put(issue,notificationList);
+ }
+ notificationList.add(notification);
+ }
+ }
+ catch (TorqueException te)
+ {
+ log.error("sendPendingNotifications(): " + te);
+ continue;
+ }
+ }
+ return issueMap;
+ }
+
+
+ /**
+ * Will return 'true' if the time since the passed timestamp is at least
+ * the minimal quiet time configured for issues.
+ *
+ * @param timestamp
+ * @return
+ */
+ private boolean isOldEnough(Long timestamp)
+ {
+ boolean bRdo = true;
+ long lTimestamp = timestamp.longValue();
+ long minimalAge = Turbine.getConfiguration().getLong("scarab.notificationmanager.issuequiettime", 0);
+ if ((new Date().getTime() - lTimestamp) < minimalAge)
+ bRdo = false;
+ return bRdo;
+ }
+
+ /**
+ * Sends email to the users associated with the issue. That is associated
+ * with this activitySet. If no subject and template specified, assume
+ * modify issue action. throws Exception
+ * Sends email to the user regarding issue's activity.
+ *
+ * @param context EmailContext preloaded with info about issue's activity
+ * @param issue Issue to be notified about
+ * @param user The user to be notified
+ *
+ */
+ private void sendEmail(EmailContext context, Issue issue, ScarabUser user)
+ throws Exception
+ {
+ context.setSubjectTemplate("notification/IssueActivitySubject.vm");
+ context.setDefaultTextKey(issue.getDefaultText());
+ Set toUsers = new HashSet();
+ toUsers.add(user);
+
+ String[] fromUser = getFromUser(issue, context);
+ String[] replyToUser = issue.getModule().getSystemEmail();
+
+ Email.sendEmail(
+ context,
+ issue.getModule(),
+ fromUser,
+ replyToUser,
+ toUsers,
+ null,
+ "notification/IssueActivity.vm");
+ }
+
+ private String[] getFromUser(Issue issue, EmailContext context) throws TorqueException
+ {
+ String[] replyToUser = null;
+
+ Set creators = (Set)context.get("creators");
+ if (creators.size()==1)
+ {
+ // exactly one contributor to this E-Mail
+ boolean exposeSender = Turbine.getConfiguration()
+ .getBoolean("scarab.email.replyto.sender",false);
+
+ if(exposeSender)
+ {
+ ScarabUser creator = (ScarabUser)creators.toArray()[0];
+ replyToUser = new String[] { creator.getName(), creator.getEmail() };
+ }
+ }
+
+ if(replyToUser == null)
+ {
+ replyToUser = issue.getModule().getSystemEmail();
+ }
+
+ return replyToUser;
+ }
+
+ private static Map typeDescriptions = new HashMap();
+
+ /*
+ * ActivityTypes are grouped for description purposes
+ */
+ static
+ {
+ typeDescriptions.put(ActivityType.ISSUE_CREATED.getCode(), L10NKeySet.ActivityIssue);
+ typeDescriptions.put(ActivityType.ISSUE_MOVED.getCode(), L10NKeySet.ActivityIssue);
+ typeDescriptions.put(ActivityType.ISSUE_COPIED.getCode(), L10NKeySet.ActivityIssue);
+ typeDescriptions.put(ActivityType.ATTRIBUTE_CHANGED.getCode(), L10NKeySet.ActivityAttributeChanges);
+ typeDescriptions.put(ActivityType.USER_ATTRIBUTE_CHANGED.getCode(), L10NKeySet.ActivityPersonnelChanges);
+ typeDescriptions.put(ActivityType.COMMENT_ADDED.getCode(), L10NKeySet.ActivityComments);
+ typeDescriptions.put(ActivityType.COMMENT_CHANGED.getCode(), L10NKeySet.ActivityComments);
+ typeDescriptions.put(ActivityType.URL_ADDED.getCode(), L10NKeySet.ActivityAssociatedInfo);
+ typeDescriptions.put(ActivityType.URL_CHANGED.getCode(), L10NKeySet.ActivityAssociatedInfo);
+ typeDescriptions.put(ActivityType.URL_DESC_CHANGED.getCode(), L10NKeySet.ActivityAssociatedInfo);
+ typeDescriptions.put(ActivityType.URL_DELETED.getCode(), L10NKeySet.ActivityAssociatedInfo);
+ typeDescriptions.put(ActivityType.ATTACHMENT_CREATED.getCode(), L10NKeySet.ActivityAssociatedInfo);
+ typeDescriptions.put(ActivityType.ATTACHMENT_REMOVED.getCode(), L10NKeySet.ActivityAssociatedInfo);
+ typeDescriptions.put(ActivityType.DEPENDENCY_CREATED.getCode(), L10NKeySet.ActivityDependencies);
+ typeDescriptions.put(ActivityType.DEPENDENCY_CHANGED.getCode(), L10NKeySet.ActivityDependencies);
+ typeDescriptions.put(ActivityType.DEPENDENCY_DELETED.getCode(), L10NKeySet.ActivityDependencies);
+ }
+
+
+ /**
+ * Returns the group to which the activity type belongs (for organizational purposes)
+ *
+ * @param type The type for which we want to get the corresponding group's name
+ * @return
+ */
+ private LocalizationKey getActivityGroup(ActivityType activityType)
+ {
+ L10NKey key = (L10NKey)typeDescriptions.get(activityType.getCode());
+ return key;
+ }
+
}
Removed: trunk/src/java/org/tigris/scarab/notification/ScarabOldNotificationManager.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/notification/ScarabOldNotificationManager.java?view=markup&pathrev=10759
Modified: trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java?view=diff&pathrev=10760&r1=10759&r2=10760
==============================================================================
--- trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java (original)
+++ trunk/src/java/org/tigris/scarab/tools/ScarabLocalizationTool.java 2009-06-21 03:10:29-0700
@@ -233,8 +233,7 @@
public String getIgnoreMissingResource(String key)
{
// [HD]: I plan to make this method private.
- // Currently it is only used from the OldNotificationManager,
- // which IMHO can be silently removed from the project.
+ // Currently it is still used from the velocity templates.
String value;
try
{
------------------------------------------------------
http://scarab.tigris.org/ds/viewMessage.do?dsForumId=3577&dsMessageId=2363927