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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.