001: package org.tigris.scarab.notification;
002:
003: /* ================================================================
004: * Copyright (c) 2000-2005 CollabNet. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are
008: * met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in the
015: * documentation and/or other materials provided with the distribution.
016: *
017: * 3. The end-user documentation included with the redistribution, if
018: * any, must include the following acknowlegement: "This product includes
019: * software developed by CollabNet <http://www.Collab.Net/>."
020: * Alternately, this acknowlegement may appear in the software itself, if
021: * and wherever such third-party acknowlegements normally appear.
022: *
023: * 4. The hosted project names must not be used to endorse or promote
024: * products derived from this software without prior written
025: * permission. For written permission, please contact info@collab.net.
026: *
027: * 5. Products derived from this software may not use the "Tigris" or
028: * "Scarab" names nor may "Tigris" or "Scarab" appear in their names without
029: * prior written permission of CollabNet.
030: *
031: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
032: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
033: * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
034: * IN NO EVENT SHALL COLLAB.NET OR ITS CONTRIBUTORS BE LIABLE FOR ANY
035: * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
036: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
037: * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
038: * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
039: * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
040: * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
041: * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
042: *
043: * ====================================================================
044: *
045: * This software consists of voluntary contributions made by many
046: * individuals on behalf of CollabNet.
047: */
048:
049: import java.util.ArrayList;
050: import java.util.Date;
051: import java.util.HashMap;
052: import java.util.HashSet;
053: import java.util.Iterator;
054: import java.util.Map;
055: import java.util.Set;
056: import java.util.List;
057: import javax.servlet.http.HttpServlet;
058:
059: import org.apache.log4j.Logger;
060: import org.apache.torque.TorqueException;
061: import org.apache.turbine.Turbine;
062: import org.tigris.scarab.om.Activity;
063: import org.tigris.scarab.om.ActivitySet;
064: import org.tigris.scarab.notification.ActivityType;
065: import org.tigris.scarab.om.Attribute;
066: import org.tigris.scarab.om.AttributePeer;
067: import org.tigris.scarab.om.AttributeValue;
068: import org.tigris.scarab.om.GlobalParameterManager;
069: import org.tigris.scarab.om.Issue;
070: import org.tigris.scarab.om.IssueManager;
071: import org.tigris.scarab.om.Module;
072: import org.tigris.scarab.om.NotificationFilterManager;
073: import org.tigris.scarab.om.NotificationStatus;
074: import org.tigris.scarab.om.NotificationStatusPeer;
075: import org.tigris.scarab.om.ScarabUser;
076: import org.tigris.scarab.om.ScarabUserManager;
077: import org.tigris.scarab.tools.localization.L10NKey;
078: import org.tigris.scarab.tools.localization.L10NKeySet;
079: import org.tigris.scarab.tools.localization.L10NMessage;
080: import org.tigris.scarab.tools.localization.LocalizationKey;
081: import org.tigris.scarab.util.Email;
082: import org.tigris.scarab.util.EmailContext;
083: import org.tigris.scarab.util.Log;
084: import org.tigris.scarab.util.ScarabLink;
085: import org.tigris.scarab.util.ScarabRuntimeException;
086:
087: /**
088: * This class provides the new implementation for the Notification Manager.
089: * It will queue the notifications, and then process them consolidating by user and issue, so
090: * a user will only get ONE email for issue containing every activity relating this
091: * issue since the last notification.
092: * <br/>
093: *
094: * @author jorgeuriarte
095: */
096: public class ScarabNewNotificationManager extends HttpServlet implements
097: NotificationManager {
098:
099: public static Logger log = Log
100: .get(ScarabNewNotificationManager.class.getName());
101:
102: private static final Integer NOTIFICATION_MANAGER_ID = new Integer(
103: 1);
104:
105: public Integer getManagerId() {
106: return NOTIFICATION_MANAGER_ID;
107: }
108:
109: /**
110: * Receives an activitySet from which to generate notification. Current
111: * implementation does only online email sending, with no aggregation or
112: * filtering.
113: */
114: public void addActivityNotification(ActivityType event,
115: ActivitySet activitySet, Issue issue, ScarabUser fromUser) {
116: this .addActivityNotification(event, activitySet, issue, null,
117: null, fromUser);
118: }
119:
120: /**
121: * Long version of the addActivityNotification method, allowing to pass the sets of
122: * users involved as 'To' or 'CC'.
123: */
124: public void addActivityNotification(ActivityType event,
125: ActivitySet activitySet, Issue issue, Set toUsers,
126: Set ccUsers, ScarabUser fromUser) {
127: if (log.isDebugEnabled())
128: log.debug("addActivityNotification: " + issue.getIdPrefix()
129: + issue.getIssueId() + "-" + event.getCode());
130: this .queueNotifications(activitySet, issue, fromUser);
131: }
132:
133: /**
134: * Queue the notifications for the passed activity set.
135: *
136: * @param activitySet
137: */
138: private void queueNotifications(ActivitySet activitySet,
139: Issue issue, ScarabUser fromUser) {
140: try {
141: NotificationStatus notification = null;
142: for (Iterator it = activitySet.getActivityList().iterator(); it
143: .hasNext();) {
144: Activity act = (Activity) it.next();
145: if (act.getIssue().equals(issue)) {
146: notification = new NotificationStatus(Email
147: .getArchiveUser(), act);
148: NotificationStatusPeer.doInsert(notification);
149:
150: Set users = issue
151: .getAllUsersToEmail(AttributePeer.EMAIL_TO);
152: users.addAll(issue
153: .getAllUsersToEmail(AttributePeer.CC_TO));
154: users.addAll(activitySet.getRemovedUsers(issue));
155:
156: // FIXME: Should we still make difference between CC & TO? If so...
157: // ...do we need this info in the notification_status table??
158:
159: // FIXME: SCB1439. does the user really have permissions
160: // to view this attribute?
161:
162: Integer moduleId = issue.getModuleId();
163: String activityType = act.getActivityType();
164:
165: for (Iterator itusers = users.iterator(); itusers
166: .hasNext();) {
167: ScarabUser user = (ScarabUser) itusers.next();
168: Integer userId = user.getUserId();
169:
170: boolean isSelf = userId.equals(fromUser
171: .getUserId());
172: boolean wantsNotification = NotificationFilterManager
173: .isNotificationEnabledFor(moduleId,
174: userId, isSelf, activityType);
175:
176: if (wantsNotification) {
177: notification = new NotificationStatus(user,
178: act);
179: NotificationStatusPeer
180: .doInsert(notification);
181: }
182: }
183: }
184: }
185: } catch (Exception e) {
186: log.error("queueNotifications(): ", e);
187: }
188: }
189:
190: /**
191: * This method process the pending notifications and send them
192: * to the users.
193: * If the NotificationManager is not activated, this method
194: * will not do anything (the mail should have been sent already).
195: */
196: public void sendPendingNotifications() {
197: log
198: .debug("sendPendingNotifications(): Collect pending notifications ...");
199: List pending = NotificationStatusPeer.getPendingNotifications();
200:
201: if (pending == null) {
202: log
203: .warn("sendPendingNotifications(): ...Could not retrieve pending notifications from Database. Try again later.");
204: return;
205: }
206: log.debug("rearrange pending notifications per issue ...");
207: Map pendingIssueMap = getPendingIssueMap(pending);
208:
209: Map issueActivities = new HashMap();
210: Map archiverActivities = new HashMap();
211: Set creators = new HashSet();
212: NotificationStatus firstNotification;
213: NotificationStatus lastNotification;
214:
215: //Process each Issue ...
216: Iterator pendingIssuesIterator = pendingIssueMap.keySet()
217: .iterator();
218: while (pendingIssuesIterator.hasNext()) {
219: Issue issue = (Issue) pendingIssuesIterator.next();
220: String issueId = "???";
221: // clear volatile data structures ...
222: issueActivities.clear();
223: archiverActivities.clear();
224: creators.clear();
225: firstNotification = null;
226: lastNotification = null;
227: Long issueTime = null;
228: String changedStatusAttributeValue = "";
229:
230: Set notificationSet = (Set) pendingIssueMap.get(issue);
231:
232: //Process each Notification for current Issue ...
233: for (Iterator it = notificationSet.iterator(); it.hasNext();) {
234: NotificationStatus currentNotification = (NotificationStatus) it
235: .next();
236:
237: ActivityType activityType = currentNotification
238: .getActivityType();
239: if (changedStatusAttributeValue.length() == 0
240: && activityType
241: .equals(ActivityType.ATTRIBUTE_CHANGED)) {
242: try {
243: Attribute attribute = currentNotification
244: .getActivity().getAttribute();
245: if (getIsStatusAttribute(attribute, issue)) {
246: String name = attribute.getName();
247: AttributeValue av = issue
248: .getAttributeValue(name);
249: if (av != null) {
250: changedStatusAttributeValue = av
251: .getValue();
252: }
253: }
254: } catch (TorqueException e) {
255: Log
256: .get()
257: .warn(
258: "Database acess error while retrieving status attribute value.(ignored)");
259: Log.get().warn(
260: "db layer reported: [" + e.getMessage()
261: + "]");
262: }
263: }
264:
265: firstNotification = getOldestNotification(
266: currentNotification, firstNotification);
267: lastNotification = getYoungestNotification(
268: currentNotification, lastNotification);
269: try {
270: issueId = issue.getUniqueId();
271: Integer receiverId = currentNotification
272: .getReceiverId();
273: ScarabUser receiver = null;
274: if (receiverId.equals(Email.getArchiveUser()
275: .getUserId())) {
276: receiver = Email.getArchiveUser();
277: } else {
278: receiver = ScarabUserManager
279: .getInstance(receiverId);
280: }
281: creators.add(currentNotification.getCreator());
282:
283: Map userActivities = getActivitiesForUser(
284: issueActivities, receiver);
285: addActivity(currentNotification, userActivities);
286: addActivity(currentNotification, archiverActivities);
287: issueTime = adjustTimeToNewer(issueTime,
288: currentNotification);
289: } catch (TorqueException te) {
290: log.error("sendPendingNotifications(): " + te);
291: }
292: }
293:
294: /*
295: * Now we got all notifications for current issue sorted by receivers
296: * and collected in issueActivities. We now can iterate throug the
297: * issueActivities and send one E-Mail per receiver for this issue:
298: */
299:
300: if (isOldEnough(issueTime)) {
301: log.debug("processing notifications for issue : ["
302: + issueId + "]");
303: Iterator userIterator = getUsersToNotifyIterator(issueActivities);
304: while (userIterator.hasNext()) {
305: ScarabUser user = (ScarabUser) userIterator.next();
306:
307: EmailContext ectx = new EmailContext();
308: ectx.put("issue", issue);
309: ectx.put("link", new ScarabLink());
310: ectx.put("creators", creators);
311: ectx.put("firstNotification", firstNotification);
312: ectx.put("lastNotification", lastNotification);
313: ectx.put("changedStatus",
314: changedStatusAttributeValue);
315: Map groupedActivities = (Map) issueActivities
316: .get(user);
317: if (groupedActivities == null) {
318: groupedActivities = archiverActivities;
319: }
320: addActivitiesToEmailContext(ectx, groupedActivities);
321:
322: Exception exception = null;
323: try {
324: this .sendEmail(ectx, issue, user);
325: } catch (Exception e) {
326: exception = e;
327: }
328:
329: updateNotificationRepository(groupedActivities,
330: exception);
331: }
332:
333: } else {
334: log.debug("Issue " + issueId + ": Is not old enough.");
335: }
336: }
337: log.debug("sendPendingNotifications(): ...finished!");
338: }
339:
340: /**
341: * This method returns true, if the attribute is identified as
342: * the "status_attribute" for the current module/issue_type combination.
343: *
344: * NOTE: The "status_attribute" id is searched in SCARAB_GLOBAL_ATTRIBUTE
345: * first, although it currently should not find any entry there. In a future
346: * release it is intended to allow a more sophisticated controll over what
347: * a status attribute is and how it should be rendered e.g. into email subject.
348: *
349: * @param attribute
350: * @param issue
351: * @return
352: * @throws TorqueException
353: */
354: private boolean getIsStatusAttribute(Attribute attribute,
355: Issue issue) throws TorqueException {
356: boolean result = false;
357: Module module = issue.getModule();
358: String key = "status_attribute_" + attribute.getAttributeId();
359:
360: String statusId = GlobalParameterManager.getString(key, module);
361: if (!statusId.equals("")) {
362: result = true; // the attribute IS the status_attribute
363: } else {
364: String name = attribute.getName().toLowerCase();
365: String globalStatusAttributeName = GlobalParameterManager
366: .getString("scarab.common.status.id").toLowerCase();
367: if (name.equals(globalStatusAttributeName)) {
368: result = true;
369: }
370: }
371: return result;
372: }
373:
374: /**
375: * @param ectx
376: * @param groupedActivities
377: */
378: private void addActivitiesToEmailContext(EmailContext ectx,
379: Map groupedActivities) {
380: ectx.put("ActivityIssue", groupedActivities
381: .get(L10NKeySet.ActivityIssue));
382: ectx.put("ActivityAttributeChanges", groupedActivities
383: .get(L10NKeySet.ActivityAttributeChanges));
384: ectx.put("ActivityPersonnelChanges", groupedActivities
385: .get(L10NKeySet.ActivityPersonnelChanges));
386: ectx.put("ActivityComments", groupedActivities
387: .get(L10NKeySet.ActivityComments));
388: ectx.put("ActivityAssociatedInfo", groupedActivities
389: .get(L10NKeySet.ActivityAssociatedInfo));
390: ectx.put("ActivityDependencies", groupedActivities
391: .get(L10NKeySet.ActivityDependencies));
392: ectx.put("ActivityReasons",
393: consolidateActivityReasons(groupedActivities));
394: }
395:
396: /**
397: * @param issueActivities
398: * @return
399: */
400: private Iterator getUsersToNotifyIterator(Map issueActivities) {
401: return issueActivities.keySet().iterator();
402: }
403:
404: /**
405: * Return the Notification which is the youngest of n1,n2
406: * Note: If one of the notificaitons is null, return the other.
407: * If both notificaitons are null, return null
408: * @param n1
409: * @param n2
410: * @return
411: */
412: private NotificationStatus getYoungestNotification(
413: NotificationStatus n1, NotificationStatus n2) {
414: if (n1 == null)
415: return n2;
416: if (n2 == null)
417: return n1;
418: int compare = compareCreationDates(n1, n2);
419: NotificationStatus result = (compare > 0) ? n1 : n2;
420: return result;
421: }
422:
423: /**
424: * Return a list of strings with the reasons for the activities to be
425: * notified.
426: *
427: * @return
428: */
429: private List consolidateActivityReasons(Map activities) {
430: Set set = new HashSet();
431: List list = new ArrayList();
432: for (Iterator it = activities.values().iterator(); it.hasNext();) {
433: List l = (List) it.next();
434: for (Iterator nots = l.iterator(); nots.hasNext();) {
435: NotificationStatus ns = (NotificationStatus) nots
436: .next();
437: String comment = ns.getComment();
438: if (comment != null && !set.contains(comment)) {
439: set.add(comment);
440: list.add(comment);
441: }
442: }
443: }
444: return list;
445: }
446:
447: /**
448: * Return the Notification which is the oldest of n1, n2
449: * Note: If one of the notificaitons is null, return the other.
450: * If both notificaitons are null, return null
451: * @param n1
452: * @param n2
453: * @return
454: */
455: private NotificationStatus getOldestNotification(
456: NotificationStatus n1, NotificationStatus n2) {
457: if (n1 == null)
458: return n2;
459: if (n2 == null)
460: return n1;
461: int compare = compareCreationDates(n1, n2);
462: NotificationStatus result = (compare < 0) ? n1 : n2;
463: return result;
464: }
465:
466: /*
467: * Compares the creation dates of two notifications.
468: * returns:
469: * -1 : n1.date < n2.date
470: * 0 : n1.date == n2.date
471: * +1 : n1.date > n2.date
472: *
473: * If both entries are null, they are reported as equal (0)
474: * If one of the entries is null, its creation date is
475: * assumed to be "older than everything else".
476: * Thrws a ScarabRuntimeException when one of the entries
477: * has no CreationDate.
478: */
479: private int compareCreationDates(NotificationStatus n1,
480: NotificationStatus n2) {
481: // handle null entries:
482: if (n1 == n2)
483: return 0;
484: if (n1 == null)
485: return -1;
486: if (n2 == null)
487: return +1;
488:
489: int result;
490: try {
491: long n1d = n1.getCreationDate().getTime();
492: long n2d = n2.getCreationDate().getTime();
493:
494: if (n1d == n2d) {
495: result = 0;
496: } else {
497: result = (n1d > n2d) ? 1 : -1;
498: }
499: } catch (NullPointerException npe) {
500: L10NMessage msg = new L10NMessage(
501: L10NKeySet.NotificationStatusNoCreationDate);
502: log.warn(msg);
503: throw new ScarabRuntimeException(msg, npe);
504: }
505:
506: return result;
507: }
508:
509: /**
510: * Update the Notification status in the database. If exception is
511: * supplied, this method assumes, an error has occured and sets
512: * he status to DEFERRED. Otherwise the E-Mail is considered to be
513: * delivered with success and the Notification status is set to SENT.
514: * This is done for ALL activities beeing reported to this user in this
515: * issue.
516: * @param groupedActivities
517: * @param exception
518: */
519: private void updateNotificationRepository(Map groupedActivities,
520: Exception exception) {
521: /**
522: * Update the notifications' status with the result of the
523: * email sending
524: */
525: for (Iterator confirm = groupedActivities.values().iterator(); confirm
526: .hasNext();) {
527: List notifications = (List) confirm.next();
528: for (Iterator n = notifications.iterator(); n.hasNext();) {
529: NotificationStatus notif = (NotificationStatus) n
530: .next();
531: if (exception == null) {
532: //notif.setStatus(NotificationStatus.SENT);
533: try {
534: NotificationStatusPeer.doDelete(notif);
535: } catch (TorqueException te) {
536: exception = te;
537: }
538: }
539:
540: if (exception != null) {
541: notif.setStatus(NotificationStatus.DEFERRED);
542: notif.setComment(exception.getMessage());
543: }
544: try {
545: notif.save();
546: } catch (Exception e) {
547: log.error("sendPendingNotifications(): Updating: "
548: + e);
549: }
550: }
551: }
552: }
553:
554: /**
555: * @param notificationTime
556: * @param notification
557: */
558: private Long adjustTimeToNewer(Long notificationTime,
559: NotificationStatus notification) {
560: /**
561: * Keep the time of the younger notification for every issue
562: */
563: long newTime = notification.getCreationDate().getTime();
564: if (notificationTime == null) {
565: notificationTime = new Long(newTime);
566: } else {
567: if (notificationTime.longValue() < newTime) {
568: notificationTime = new Long(newTime);
569: } else {
570: // keep current notificationTime;
571: }
572: }
573: return notificationTime;
574: }
575:
576: /**
577: * @param issueActivities
578: * @param user
579: * @return
580: */
581: private Map getActivitiesForUser(Map issueActivities,
582: ScarabUser user) {
583: Map userActivities = (Map) issueActivities.get(user);
584: if (null == userActivities) {
585: userActivities = new HashMap();
586: issueActivities.put(user, userActivities);
587: }
588: return userActivities;
589: }
590:
591: /**
592: * @param notification
593: * @param userActivities
594: */
595: private void addActivity(NotificationStatus notification,
596: Map userActivities) {
597: LocalizationKey activityGroup = getActivityGroup(notification
598: .getActivityType());
599: List typeNotifications = (List) userActivities
600: .get(activityGroup);
601: if (null == typeNotifications) {
602: typeNotifications = new ArrayList();
603: userActivities.put(activityGroup, typeNotifications);
604: }
605:
606: // We will only add this notification to the user's list if it's not
607: // already present.
608: boolean bAlreadyPresent = false;
609: for (Iterator it = typeNotifications.iterator(); it.hasNext()
610: && !bAlreadyPresent;) {
611: NotificationStatus not = (NotificationStatus) it.next();
612: bAlreadyPresent = (not.getActivityId().equals(notification
613: .getActivityId()));
614: }
615: if (!bAlreadyPresent) {
616: typeNotifications.add(notification);
617: }
618: }
619:
620: /**
621: * @param pending
622: */
623: private Map getPendingIssueMap(List pending) {
624: Map issueMap = new HashMap();
625: for (Iterator it = pending.iterator(); it.hasNext();) {
626: NotificationStatus notification = (NotificationStatus) it
627: .next();
628: Issue issue = null;
629: try {
630: issue = IssueManager.getInstance(notification
631: .getIssueId());
632:
633: /**
634: * Only add the notification when it's related to THIS issue (needed
635: * for notification related to dependencies or moving, so we don't
636: * get duplicated descriptions)
637: */
638:
639: if (notification.getActivity().getIssue().equals(issue)) {
640:
641: Set notificationSet = (Set) issueMap.get(issue);
642: if (notificationSet == null) {
643: notificationSet = new HashSet();
644: issueMap.put(issue, notificationSet);
645: }
646: notificationSet.add(notification);
647: }
648: } catch (TorqueException te) {
649: log.error("sendPendingNotifications(): " + te);
650: continue;
651: }
652: }
653: return issueMap;
654: }
655:
656: /**
657: * Will return 'true' if the time since the passed timestamp is at least
658: * the minimal quiet time configured for issues.
659: *
660: * @param timestamp
661: * @return
662: */
663: private boolean isOldEnough(Long timestamp) {
664: boolean bRdo = true;
665: long lTimestamp = timestamp.longValue();
666: long minimalAge = Turbine.getConfiguration().getLong(
667: "scarab.notificationmanager.issuequiettime", 0);
668: if ((new Date().getTime() - lTimestamp) < minimalAge)
669: bRdo = false;
670: return bRdo;
671: }
672:
673: /**
674: * Sends email to the users associated with the issue. That is associated
675: * with this activitySet. If no subject and template specified, assume
676: * modify issue action. throws Exception
677: * Sends email to the user regarding issue's activity.
678: *
679: * @param context EmailContext preloaded with info about issue's activity
680: * @param issue Issue to be notified about
681: * @param user The user to be notified
682: *
683: */
684: private void sendEmail(EmailContext context, Issue issue,
685: ScarabUser user) throws Exception {
686: context
687: .setSubjectTemplate("notification/IssueActivitySubject.vm");
688: Set toUsers = new HashSet();
689: toUsers.add(user);
690:
691: String[] fromUser = getFromUser(issue, context);
692: String[] replyToUser = issue.getModule().getSystemEmail();
693:
694: Email.sendEmail(context, issue.getModule(), fromUser,
695: replyToUser, toUsers, null,
696: "notification/IssueActivity.vm");
697: }
698:
699: private String[] getFromUser(Issue issue, EmailContext context)
700: throws TorqueException {
701: String[] replyToUser = null;
702:
703: Set creators = (Set) context.get("creators");
704: if (creators.size() == 1) {
705: // exactly one contributor to this E-Mail
706: boolean exposeSender = Turbine.getConfiguration()
707: .getBoolean("scarab.email.replyto.sender", false);
708:
709: if (exposeSender) {
710: ScarabUser creator = (ScarabUser) creators.toArray()[0];
711: replyToUser = new String[] { creator.getName(),
712: creator.getEmail() };
713: }
714: }
715:
716: if (replyToUser == null) {
717: replyToUser = issue.getModule().getSystemEmail();
718: }
719:
720: return replyToUser;
721: }
722:
723: private static Map typeDescriptions = new HashMap();
724:
725: /*
726: * ActivityTypes are grouped for description purposes
727: */
728: static {
729: typeDescriptions.put(ActivityType.ISSUE_CREATED.getCode(),
730: L10NKeySet.ActivityIssue);
731: typeDescriptions.put(ActivityType.ISSUE_MOVED.getCode(),
732: L10NKeySet.ActivityIssue);
733: typeDescriptions.put(ActivityType.ISSUE_COPIED.getCode(),
734: L10NKeySet.ActivityIssue);
735: typeDescriptions.put(ActivityType.ATTRIBUTE_CHANGED.getCode(),
736: L10NKeySet.ActivityAttributeChanges);
737: typeDescriptions.put(ActivityType.USER_ATTRIBUTE_CHANGED
738: .getCode(), L10NKeySet.ActivityPersonnelChanges);
739: typeDescriptions.put(ActivityType.COMMENT_ADDED.getCode(),
740: L10NKeySet.ActivityComments);
741: typeDescriptions.put(ActivityType.COMMENT_CHANGED.getCode(),
742: L10NKeySet.ActivityComments);
743: typeDescriptions.put(ActivityType.URL_ADDED.getCode(),
744: L10NKeySet.ActivityAssociatedInfo);
745: typeDescriptions.put(ActivityType.URL_CHANGED.getCode(),
746: L10NKeySet.ActivityAssociatedInfo);
747: typeDescriptions.put(ActivityType.URL_DESC_CHANGED.getCode(),
748: L10NKeySet.ActivityAssociatedInfo);
749: typeDescriptions.put(ActivityType.URL_DELETED.getCode(),
750: L10NKeySet.ActivityAssociatedInfo);
751: typeDescriptions.put(ActivityType.ATTACHMENT_CREATED.getCode(),
752: L10NKeySet.ActivityAssociatedInfo);
753: typeDescriptions.put(ActivityType.ATTACHMENT_REMOVED.getCode(),
754: L10NKeySet.ActivityAssociatedInfo);
755: typeDescriptions.put(ActivityType.DEPENDENCY_CREATED.getCode(),
756: L10NKeySet.ActivityDependencies);
757: typeDescriptions.put(ActivityType.DEPENDENCY_CHANGED.getCode(),
758: L10NKeySet.ActivityDependencies);
759: typeDescriptions.put(ActivityType.DEPENDENCY_DELETED.getCode(),
760: L10NKeySet.ActivityDependencies);
761: }
762:
763: /**
764: * Returns the group to which the activity type belongs (for organizational purposes)
765: *
766: * @param type The type for which we want to get the corresponding group's name
767: * @return
768: */
769: private LocalizationKey getActivityGroup(ActivityType activityType) {
770: L10NKey key = (L10NKey) typeDescriptions.get(activityType
771: .getCode());
772: return key;
773: }
774: }
|