Scarab commit: svn commit: r10838 - trunk: . src/conf/classes src/java/org/tigris/scarab/actions src/java/org/tigris/scarab/notification src/java/org/tigris/scarab/om src/java/org/tigris/scarab/tools src/java/org/tigris/scarab/tools/localization src/java/org/tigris/scarab/workflow src/webapp/WEB-INF/templates/email/notification src/webapp/WEB-INF/templates/viewIssue xdocs

Hussayn Dabbous <[email protected]>
Newsgroups gmane.comp.java.scarab.cvs
Message-ID <[email protected]>
Author: dabbous
Date: 2009-07-28 07:21:53-0700
New Revision: 10838

Added:
   trunk/src/java/org/tigris/scarab/workflow/IssueState.java   (contents, props changed)
Modified:
   trunk/project.properties
   trunk/src/conf/classes/ScarabBundle_de.properties
   trunk/src/conf/classes/ScarabBundle_en.properties
   trunk/src/java/org/tigris/scarab/actions/ModifyIssue.java
   trunk/src/java/org/tigris/scarab/notification/ActivityType.java
   trunk/src/java/org/tigris/scarab/notification/NotificationManager.java
   trunk/src/java/org/tigris/scarab/notification/NotificationManagerJob.java
   trunk/src/java/org/tigris/scarab/notification/ScarabNotificationManager.java
   trunk/src/java/org/tigris/scarab/om/Issue.java
   trunk/src/java/org/tigris/scarab/om/IssueType.java
   trunk/src/java/org/tigris/scarab/om/NotificationStatus.java
   trunk/src/java/org/tigris/scarab/om/NotificationStatusPeer.java
   trunk/src/java/org/tigris/scarab/tools/ScarabGlobalTool.java
   trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java
   trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivity.vm
   trunk/src/webapp/WEB-INF/templates/viewIssue/ViewIssueTab5.vm
   trunk/xdocs/scarab_properties.xml

Log:
SCB2989: Created a major update concerning ScarabStatus.
more info see issue comments.

Modified: trunk/project.properties
Url: http://scarab.tigris.org/source/browse/scarab/trunk/project.properties?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/project.properties	(original)
+++ trunk/project.properties	2009-07-28 07:21:53-0700
@@ -882,6 +882,7 @@
 #   scarab.common.status.sealed
 #   scarab.common.status.onhold
 #   scarab.common.status.onhold.dateProperty
+#   scarab.common.status.onhold.reminder.period
 #   scarab.common.status.sealed.modifyPermission
 #
 #  Helper properties for the workflow.
@@ -1005,6 +1006,18 @@

 scarab.common.status.onhold.dateProperty=onhold

+# -------------------------------------------
+# scarab.common.status.onhold.reminder.period
+# -------------------------------------------
+#
+#
+#  This attribute tells the NotificationManager how long to wait
+# en sending reminder notifications for issues in state onhold.
+# nit is minutes. The default setting is 1440 (1 day)
+#
+
+scarab.common.status.onhold.reminder.period40
+
 # --------------------------------------------
 # scarab.common.status.sealed.modifyPermission
 # --------------------------------------------

Modified: trunk/src/conf/classes/ScarabBundle_de.properties
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/conf/classes/ScarabBundle_de.properties?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/conf/classes/ScarabBundle_de.properties	(original)
+++ trunk/src/conf/classes/ScarabBundle_de.properties	2009-07-28 07:21:53-0700
@@ -1455,6 +1455,7 @@
 ActivityTypeSendSelfText=Meine eigenen Modifikationen benachrichtigen.
 ActivityTypeFailureText=Online Benachrichtigung im Fehlerfall.

+ActivityTypeIssueOnHold= Ticket auf Wiedervorlage
 ActivityTypeIssueMoved = Ticket verschoben
 ActivityTypeIssueDeleted = Ticket gelöscht
 ActivityTypeDependencyChanged = Abhängigkeit geändert
@@ -1477,6 +1478,7 @@
 NotificationIssueMoved=verschoben
 NotificationIssueCopied=Kopie
 NotificationIssueDeleted=gelöscht
+NotificationIssueOnHold=zur Wiedervorlage
 NotificationCommentAdded=Kommentar
 NotificationCommentChanged=Kommentar
 NotificationAttributeChanged=Attribut
@@ -1492,6 +1494,9 @@
 NotificationDependencyDeleted«hängigkeit
 NotificationIssueOther=?

+WakeupFromOnHoldstateÞr Wiedervorlagezeitpunkt für das Ticket ist erreicht, bzw. überschritten.
+GeneratedOnHoldState=Wiedervorlage
+
 ## ***********************************************************************
 ## Misc Java Objects
 ## ***********************************************************************

Modified: trunk/src/conf/classes/ScarabBundle_en.properties
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/conf/classes/ScarabBundle_en.properties?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/conf/classes/ScarabBundle_en.properties	(original)
+++ trunk/src/conf/classes/ScarabBundle_en.properties	2009-07-28 07:21:53-0700
@@ -251,6 +251,7 @@
 NotificationIssueMoved=moved
 NotificationIssueCopied=copied
 NotificationIssueDeletedÞleted
+NotificationIssueOnHold=for resubmission
 NotificationCommentAdded=comment
 NotificationCommentChanged=comment
 NotificationAttributeChanged=attribute
@@ -266,6 +267,9 @@
 NotificationDependencyDeletedÞpendency
 NotificationIssueOther=?

+WakeupFromOnHoldstate=The resubmission date for this issue has been reached.
+GeneratedOnHoldState=resubmission
+
 ##ButtonLabels
 Filter=Filter
 AssociateSelectedUserWithIssue=Associate selected users with issue
@@ -1546,6 +1550,7 @@
 ActivityTypeFailureText=Notify me online about E-Mail send failures
 ActivityTypeHelpText=Here you can specify, for which activities on issues you will get an automatic notification message. The switches in the table have following meaning:

+ActivityTypeIssueOnHold= Issue on hold
 ActivityTypeIssueMoved = Issue moved
 ActivityTypeIssueDeleted = Issue deleted
 ActivityTypeDependencyChanged = Dependency changed

Modified: trunk/src/java/org/tigris/scarab/actions/ModifyIssue.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/actions/ModifyIssue.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/actions/ModifyIssue.java	(original)
+++ trunk/src/java/org/tigris/scarab/actions/ModifyIssue.java	2009-07-28 07:21:53-0700
@@ -483,6 +483,12 @@
             doAddwatchers(runData, context);
         }

+        Attribute myOnHoldDateAttribute = issue.getMyOnHoldExpirationDate();
+        Attribute statusAttribute       = issue.getMyStatusAttribute();
+        boolean statusChanged = false;
+        boolean onHoldDateSet = false;
+        boolean isOnHold      = issue.isOnHold();
+
         while (iter2.hasNext())
         {
             final AttributeValue aval = (AttributeValue)modMap.get(iter2.next());
@@ -532,6 +538,16 @@
                     group.setProperties(aval2);
                     newAttVals.put(aval.getAttributeId(), aval2);
                     modifiedAttribute = true;
+
+                    Attribute att = aval.getAttribute();
+                    if(att.equals(statusAttribute))
+                    {
+                        statusChanged = true;
+                    }
+                    else if (att.equals(myOnHoldDateAttribute))
+                    {
+                        onHoldDateSet = true;
+                    }
                 }
                 // The attribute is being undefined.
                 else if (oldValue != null && newValue.length() == 0 &&
@@ -572,6 +588,31 @@
                                     ActivityType.ATTRIBUTE_CHANGED,
                                     activitySet, issue, user);
                 }
+
+                if (statusChanged)
+                {
+                    if(isOnHold)
+                    {
+                        // ok, the issue was on hold, but now the status has changed.
+                        // Hence we must cancel a previous onHoldNotification now.
+                        NotificationManagerFactory.getInstance().cancelOnHoldNotification(issue);
+                    }
+                    else if(issue.isOnHold())
+                    {
+                        // From the boolean isOnHold=úlse we deduce that the issue was NOT on hold
+                        // prior to this transaction. But from the issue.isOnHold()==true we deduce
+                        // that now it IS on hold. Hence the status must have changed during this transaction
+                        // from something to onHold.
+                        //
+                        // And now is the time to create another notification which will be used as wakeup
+                        // Notification when the onHoldDate expires. This information must be handled
+                        // by the NotificationManager (see the NotificationManager.wakeupOnHoldTimeouts() )
+                        NotificationManagerFactory.getInstance().addOnHoldNotification(activitySet, issue, user);
+                    }
+                }
+
+
+
             }
             intake.removeAll();
             scarabR.setConfirmMessage(L10NKeySet.ChangesSaved);

Modified: trunk/src/java/org/tigris/scarab/notification/ActivityType.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/notification/ActivityType.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/notification/ActivityType.java	(original)
+++ trunk/src/java/org/tigris/scarab/notification/ActivityType.java	2009-07-28 07:21:53-0700
@@ -14,6 +14,7 @@
     LocalizationKey notificationKey = null;

     Integer notificationPriority    = null;



+    public static final ActivityType ISSUE_ONHOLD       = new ActivityType("issue_onhold","ActivityTypeIssueOnHold",             L10NKeySet.NotificationIssueOnHold, 18);

     public static final ActivityType ISSUE_CREATED      = new ActivityType("issue_created","ActivityTypeIssueCreated",           L10NKeySet.NotificationIssueCreated, 17);

     public static final ActivityType ISSUE_MOVED        = new ActivityType("issue_moved","ActivityTypeIssueMoved",               L10NKeySet.NotificationIssueMoved, 16);

     public static final ActivityType ISSUE_COPIED       = new ActivityType("issue_copied","ActivityTypeIssueCopied",             L10NKeySet.NotificationIssueCopied, 15);

@@ -55,6 +56,7 @@
         types.put(DEPENDENCY_CREATED.getCode(), DEPENDENCY_CREATED);

         types.put(DEPENDENCY_CHANGED.getCode(), DEPENDENCY_CHANGED);

         types.put(DEPENDENCY_DELETED.getCode(), DEPENDENCY_DELETED);

+        types.put(ISSUE_ONHOLD.getCode(), ISSUE_ONHOLD);



     }




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&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/notification/NotificationManager.java	(original)
+++ trunk/src/java/org/tigris/scarab/notification/NotificationManager.java	2009-07-28 07:21:53-0700
@@ -93,5 +93,29 @@
      * @return

      */

     public Integer getManagerId();

-

+

+    /**

+     * Implementations of this method should provide the means to wakeup issues,

+     * which are currently in the "onHold state" and have timed out. For such issues

+     * the NM should sends out "wakeup notifications" to all observers and assignees.

+     */

+    public void wakeupOnHoldTimeouts();

+

+    /**

+     * Create a new onHoldNotification. This notification will only be sent out AFTER

+     * the onHoldDate has expired! So it shall be treated differently from the "normal"

+     * notifications.

+     * @param activitySet

+     * @param issue

+     * @param user

+     */

+    public void addOnHoldNotification(ActivitySet activitySet, Issue issue, ScarabUser user);

+

+    /**

+     * Cancel a previously created onHoldNotification. If no such Notification exists,

+     * this method will terminate gracefully (nothing will happen). Otherwise

+     * any pending onHoldNotification for that issue will be removed.

+     * @param issue

+     */

+    public void cancelOnHoldNotification(Issue issue);

 }


Modified: trunk/src/java/org/tigris/scarab/notification/NotificationManagerJob.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/notification/NotificationManagerJob.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/notification/NotificationManagerJob.java	(original)
+++ trunk/src/java/org/tigris/scarab/notification/NotificationManagerJob.java	2009-07-28 07:21:53-0700
@@ -75,7 +75,10 @@
 			if (jobContext.getScheduler().getCurrentlyExecutingJobs().size() > 2)
 				log.info("Skipping call to NotificationManager.sendPendingNotifications because there's already once call waiting.");
 			else
-				NotificationManagerFactory.getInstance().sendPendingNotifications();
+			{
+                NotificationManagerFactory.getInstance().sendPendingNotifications();
+                NotificationManagerFactory.getInstance().wakeupOnHoldTimeouts();
+			}
 		} catch (SchedulerException e) {
 			log.error("execute(): " + e);
 		}

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&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/notification/ScarabNotificationManager.java	(original)
+++ trunk/src/java/org/tigris/scarab/notification/ScarabNotificationManager.java	2009-07-28 07:21:53-0700
@@ -46,8 +46,11 @@
  * individuals on behalf of CollabNet.

  */



+import java.text.ParseException;

 import java.util.ArrayList;

+import java.util.Calendar;

 import java.util.Date;

+import java.util.GregorianCalendar;

 import java.util.HashMap;

 import java.util.HashSet;

 import java.util.Iterator;

@@ -62,10 +65,15 @@
 import org.tigris.scarab.om.Activity;

 import org.tigris.scarab.om.ActivitySet;

 import org.tigris.scarab.notification.ActivityType;

+import org.tigris.scarab.om.ActivityManager;

+import org.tigris.scarab.om.ActivitySetManager;

+import org.tigris.scarab.om.ActivitySetType;

+import org.tigris.scarab.om.ActivitySetTypePeer;

+import org.tigris.scarab.om.Attachment;

+import org.tigris.scarab.om.AttachmentManager;

 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;

@@ -81,6 +89,7 @@
 import org.tigris.scarab.util.Email;

 import org.tigris.scarab.util.EmailContext;

 import org.tigris.scarab.util.Log;

+import org.tigris.scarab.util.ScarabException;

 import org.tigris.scarab.util.ScarabLink;



 /**

@@ -214,7 +223,7 @@
         ScarabCache.clear();

         log.debug("sendPendingNotifications(): Collect pending notifications ...");

         // It is now guaranteed, that the notifications arrive in order of CreationDate!

-        List pending = NotificationStatusPeer.getPendingNotifications();

+        List<Notification> pending = NotificationStatusPeer.getPendingNotifications();



         if(pending == null)

         {

@@ -380,6 +389,218 @@
         }

     }



+    /**

+     * Implementations of this method should provide the means to wakeup issues,

+     * which are currently in the "onHold state" and have timed out. Such issues

+     * shall be moved to the configured "processing state"

+     */

+    public void wakeupOnHoldTimeouts()

+    {

+        ScarabCache.clear();

+        log.debug("wakeupOnHoldTimeouts() : Collect onHold notifications ...");

+        // It is now guaranteed, that the notifications arrive in order of CreationDate!

+        List pending = NotificationStatusPeer.getOnholdNotifications();

+

+        if(pending == null)

+        {

+            log.warn("sendPendingNotifications(): ...Could not retrieve pending notifications from Database. Try again later.");

+            return;

+        }

+

+        Iterator<NotificationStatus> iter = pending.iterator();

+        Calendar now = new GregorianCalendar();

+        try

+        {

+            while(iter.hasNext())

+            {

+                NotificationStatus ns = iter.next();

+                Activity activity = ns.getActivity();

+                Calendar endDate = new GregorianCalendar();

+                endDate.setTime(activity.getEndDate()); // that is the onHoldExpiration date

+                if (endDate.before(now)) // onHold expired

+                {

+                    Issue issue = activity.getIssue();

+                    // onHoldTimeout reached.

+                    boolean notificationNeeded = false;

+

+                    Calendar changeDate = new GregorianCalendar();

+                    Date lastChangeAt = ns.getChangeDate();

+                    if(lastChangeAt == null)

+                    {

+                        // first reminder, set the change date and mark notification needed.

+                        ns.setChangeDate(now.getTime());

+                        ns.save();

+                        notificationNeeded = true;

+                    }

+                    else

+                    {

+                        int reminderPeriod = issue.getReminderPeriod();

+                        if(reminderPeriod > 0)

+                        {

+                            // Only send reminders if the reminderPeriod is set to a

+                            // positive value

+                            // note: The reminderPeriod is declared in system property

+                            //

+                            // "scarab.common.status.onhold.reminder.period"

+                            //

+                            Calendar gc = new GregorianCalendar();

+                            gc.setTime(lastChangeAt);

+                            gc.add(Calendar.MINUTE, reminderPeriod); // look if we are reminderPeriod minutes after last change date.

+                            if(gc.before(now))

+                            {

+                                // wait period expired. mark for resending notification and update changeDate.

+                                ns.setChangeDate(now.getTime());

+                                ns.save();

+                                notificationNeeded = true;

+                            }

+                        }

+                    }

+

+                    if(notificationNeeded)

+                    {

+

+                        // Create a set of notifications to all observers.

+                        // Note: This hap[pens as long as the onHoldNotification remains in the database.

+                        // The only way to stop reminder notifications is to change the issue state to

+                        // something different from the "onHoldState"!

+

+                        ActivitySet activitySet = activity.getActivitySet();

+                        Integer userId          = activitySet.getCreatedBy();

+                        ScarabUser user         = ScarabUserManager.getInstance(userId);

+                        createWakeupNotification(issue, user);

+                    }

+                }

+            }

+        }

+        catch(TorqueException te)

+        {

+            Log.get().warn("Can not access Database while processing wakeupOnHoldTimeouts");

+        }

+

+    }

+

+    private void createWakeupNotification(Issue issue, ScarabUser user) throws TorqueException

+    {

+        Date date;

+        try {

+            date = issue.getOnHoldUntil();

+        } catch (Exception e)

+        {

+            throw new RuntimeException("Can not retrieve the onHoldUntil date from the current issue");

+        }

+            Activity activity = ActivityManager.getInstance();

+            Attribute attribute = issue.getMyStatusAttribute();

+

+            activity.setAttribute(attribute);

+            activity.setActivityType(ActivityType.ISSUE_ONHOLD.getCode());

+            activity.setDescription("WakeupFromOnHoldstate");

+            activity.setIssue(issue);

+            activity.setEndDate(date);

+            activity.setNewValue("");

+            activity.setOldValue("");

+            activity.setOldOptionId(0);

+            activity.setNewOptionId(0);

+

+            Attachment attachment = AttachmentManager.getInstance();

+            attachment.setTextFields(user, issue,Attachment.COMMENT__PK);

+            attachment.setData("WakeupFromOnHoldstate");

+            attachment.setName("comment");

+            attachment.save();

+

+            activity.setAttachment(attachment);

+

+            Integer tt = ActivitySetTypePeer.EDIT_ISSUE__PK;

+            ActivitySet activitySet;

+            try {

+                activitySet = ActivitySetManager.getInstance(tt, user);

+                activitySet.addActivity(activity);

+                activitySet.save();

+                addActivityNotification(ActivityType.ISSUE_ONHOLD, activitySet, issue, user);

+            } catch (ScarabException e) {

+                throw new RuntimeException(e);

+            }

+    }

+

+

+

+    /**

+     * Create an activity and a Notification. Add the structures

+     * to the persistent storage. Note: The Notification will never be

+     * sent out. But after the expiration date has been reached, a set

+     * of "expire" notifications will be created instead once per day

+     * until the issue state is changed. See wakeupOnHoldTimeouts() and

+     * createWakeupActivity() above for further information.

+     *

+     * Important:The expiration end date is stored in the activity.endDate!

+     */

+    public void addOnHoldNotification(ActivitySet activitySet, Issue issue, ScarabUser user)

+    {

+        Date date;

+        try {

+            date = issue.getOnHoldUntil();

+            Activity activity = ActivityManager.getInstance();

+            Attribute attribute = issue.getMyStatusAttribute();

+            activity.setAttribute(attribute);

+            activity.setActivityType(ActivityType.ISSUE_ONHOLD.getCode());

+            activity.setIssue(issue);

+            activity.setEndDate(date);

+            activity.setNewValue("GeneratedOnHoldState");

+            activity.setOldValue("");

+            activity.setOldOptionId(0);

+            activity.setNewOptionId(0);

+            activity.setDescription("GeneratedOnHoldState");

+            activitySet.addActivity(activity);

+            activitySet.save();

+            NotificationStatus notification = new NotificationStatus(user, activity);

+            notification.setStatus(NotificationStatus.ON_HOLD);

+            NotificationStatusPeer.doInsert(notification);

+        } catch (Exception e)

+        {

+            throw new RuntimeException("Can not retrieve the onHoldUntil date from the current issue");

+        }

+

+    }

+

+    /**

+     * Cancel a previously created onHoldNotification. If no such Notification exists,

+     * this method will terminate gracefully (nothing will happen). Otherwise

+     * any pending onHoldNotification for that issue will be removed.

+     * @param issue

+     */

+    public void cancelOnHoldNotification(Issue issue)

+    {

+        ScarabCache.clear();

+        log.debug("cancelOnHoldTimeouts() : Collect onHold notifications ...");

+        // It is now guaranteed, that the notifications arrive in order of CreationDate!

+        List<NotificationStatus> pending = NotificationStatusPeer.getOnholdNotifications();

+

+        if(pending == null)

+        {

+            log.error("cancelOnHoldNotification(): Could not retrieve pending notifications for Issue ["+issue.getIdCount()+"]. Maybe something wrong with the issue state ?");

+            return;

+        }

+

+        Iterator<NotificationStatus> iter = pending.iterator();

+        Date now = new Date();

+        try

+        {

+            while(iter.hasNext())

+            {

+                NotificationStatus ns = iter.next();

+                Activity activity = ns.getActivity();

+                Issue activityIssue = activity.getIssue();

+                if(issue.equals(activityIssue))

+                {

+                    ns.delete();

+                }

+            }

+        }

+        catch(TorqueException te)

+        {

+            Log.get().warn("Can not access Database while processing wakeupOnHoldTimeouts");

+        }

+

+    }



     /**

      * Return the L10NKey associated to the Attribute change.

@@ -536,12 +757,7 @@


     /**

      * 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.

+     * the "status_attribute" for the given issue.

      *

      * @param attribute

      * @param issue

@@ -551,24 +767,12 @@
     private boolean getIsStatusAttribute(Attribute attribute, Issue issue)

     throws TorqueException

     {

-        boolean resultúlse;

-        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

+        Attribute statusAttribute = issue.getMyStatusAttribute();

+        if(statusAttribute == null)

         {

-            String name = attribute.getName().toLowerCase();

-            String globalStatusAttributeName = GlobalParameterManager.getString("scarab.common.status.id").toLowerCase();

-            if(name.equals(globalStatusAttributeName))

-            {

-                result=true;

-            }

+            return false;

         }

+        boolean result = statusAttribute.equals(attribute);

         return result;

     }



@@ -874,6 +1078,7 @@
         typeDescriptions.put(ActivityType.DEPENDENCY_CREATED.getCode(), L10NKeySet.ActivityDependencies);

         typeDescriptions.put(ActivityType.DEPENDENCY_CHANGED.getCode(), L10NKeySet.ActivityDependencies);

         typeDescriptions.put(ActivityType.DEPENDENCY_DELETED.getCode(), L10NKeySet.ActivityDependencies);

+        typeDescriptions.put(ActivityType.ISSUE_ONHOLD.getCode(),       L10NKeySet.ActivityComments);

     }





@@ -889,4 +1094,6 @@
         return key;

     }



+

+

 }


Modified: trunk/src/java/org/tigris/scarab/om/Issue.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/om/Issue.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/om/Issue.java	(original)
+++ trunk/src/java/org/tigris/scarab/om/Issue.java	2009-07-28 07:21:53-0700
@@ -93,6 +93,7 @@
 import org.tigris.scarab.util.SimpleSkipFiltering;
 import org.tigris.scarab.util.SkipFiltering;
 import org.tigris.scarab.util.word.SearchFactory;
+import org.tigris.scarab.workflow.IssueState;
 import org.tigris.scarab.workflow.WorkflowFactory;

 import com.workingdogs.village.Record;
@@ -184,8 +185,16 @@
      * new issues are created only when the issuetype and module are known
      * Or by the Peer when retrieving from db
      */
+
+    /**
+     * The associated issue status information.
+     */
+    private IssueState mystate = null;
+
+
     public Issue()
     {
+        mystate = new IssueState(this);
     }

     protected Issue(Module module, IssueType issueType)
@@ -4258,61 +4267,53 @@
         }
         return result;
     }
-
-
+
     /**
-     * Create a javascript function, which can be used to check if the given
-     * attribute would become required if a specific attributeOption was set.
-     * @param attribute
+     * Get active Attribute by its name, if it exists for this issue.
+     * If the requested attribute does not exist, return null
+     * @param attributeName
      * @return
      * @throws TorqueException
-     * @throws ScarabException
      */
-    public String createIssueChecker(RModuleAttribute rma, String setMarkerFunction, int indent) throws TorqueException, ScarabException
+    public Attribute getAttribute(String attributeName) throws TorqueException
     {
-        String result = "";
-        boolean isRequired = rma.getRequired();
-        if(!isRequired)
+        Attribute result = null;
+        List<Attribute> attributes = this.getIssueType().getActiveAttributes(getModule());
+        Iterator<Attribute> iter = attributes.iterator();
+        while(iter.hasNext())
         {
-            List<Condition> conditions = rma.getConditions();
-
-            Iterator<Condition> iter = conditions.iterator();
-            while(iter.hasNext())
+            Attribute attrib = iter.next();
+            String attribName = attrib.getName();
+            if(attribName.equals(attributeName))
             {
-                Condition condition = iter.next();
-                result += condition.createConditionCheckerScript(rma, setMarkerFunction, indent);
+                result = attrib;
+                break;
             }
         }
         return result;
     }


+    /**
+     * public accessor to the IssueState. To be used from velocity
+     * instead of the delegator methods below.
+     * @return
+     */
+    public IssueState getState()
+    {
+        return mystate;
+    }
+
+    // ===========================================================+    // delegation methods for IssueState
+    // ===========================================================+    /**
+     * Create a javascript snippet, which can be used to check if the given
+     * attribute would become required if a specific attributeOption was set.
+     */
     public SkipFiltering createIssueChecker(String setMarkerFunction, int indent) throws TorqueException, ScarabException
     {
-        String result = "";
-        List attributes = null;
-        Module module = getModule();
-        IssueType issueType = getIssueType();
-        attributes = issueType.getActiveAttributes(module);
-        Iterator<Attribute> iter = attributes.iterator();
-        while (iter.hasNext())
-        {
-            Attribute attribute = iter.next();
-            RModuleAttribute rma  = module.getRModuleAttribute(attribute, issueType);
-            result += createIssueChecker(rma, setMarkerFunction, indent);
-        }
-
-        String prepend;
-        if(result.length() > 0)
-        {
-            prepend="\n";
-        }
-        else
-        {
-            prepend = "/* No conditional attributes found for this module/issueType */";
-        }
-
-        return new SimpleSkipFiltering(prepend+result);
+        return mystate.createIssueChecker(setMarkerFunction, indent);
     }

     /**
@@ -4326,21 +4327,7 @@
      */
     public boolean isSealed() throws TorqueException
     {
-        boolean result = false;
-        String status = ScarabGlobalTool.getStatusAttributeName();
-        if (status != null)
-        {
-            String value = getProperty("scarab.common.status.sealed", null);
-            if(value != null)
-            {
-                AttributeValue attval = getAttributeValue(status);
-                if(attval != null && attval.getValue().equals(value))
-                {
-                    result = true;
-                }
-            }
-        }
-        return result;
+        return mystate.isSealed();
     }

     /**
@@ -4355,21 +4342,7 @@
      */
     public boolean isOnHold() throws TorqueException
     {
-        boolean result = false;
-        String status = ScarabGlobalTool.getStatusAttributeName();
-        if (status != null)
-        {
-            String value = ScarabGlobalTool.getOnHoldAttributeOptionValue();
-            if(value != null)
-            {
-                AttributeValue attval = getAttributeValue(status);
-                if(attval != null && attval.getValue().equals(value))
-                {
-                    result = true;
-                }
-            }
-        }
-        return result;
+        return mystate.isOnHold();
     }

     /**
@@ -4381,41 +4354,10 @@
      */
     public Attribute getMyStatusAttribute() throws TorqueException
     {
-        Attribute attribute = null;
-        String attributeName = ScarabGlobalTool.getStatusAttributeName();
-        if(attributeName != null)
-        {
-            attribute = this.getAttribute(attributeName);
-        }
-        return attribute;
-    }
-
-    /**
-     * Get active Attribute by its name, if it exists for this issue.
-     * If the requested attribute does not exist, return null
-     * @param attributeName
-     * @return
-     * @throws TorqueException
-     */
-    public Attribute getAttribute(String attributeName) throws TorqueException
-    {
-        Attribute result = null;
-        List<Attribute> attributes = this.getIssueType().getActiveAttributes(getModule());
-        Iterator<Attribute> iter = attributes.iterator();
-        while(iter.hasNext())
-        {
-            Attribute attrib = iter.next();
-            String attribName = attrib.getName();
-            if(attribName.equals(attributeName))
-            {
-                result = attrib;
-                break;
-            }
-        }
-        return result;
+        return mystate.getStatusAttribute();
     }

-    /**
+   /**
      * Returns the attribute instance, which contains the issues onHoldExpirationDate.
      * Note:  Currently Scarab expects that the Attribute is a DateAttribute
      * @return
@@ -4423,15 +4365,8 @@
      */
     public Attribute getMyOnHoldExpirationDate() throws TorqueException
     {
-        Attribute attribute = null;
-        String attributeName = ScarabGlobalTool.getOnHoldExpirationDateAttributeName();
-        if(attributeName != null)
-        {
-            attribute = this.getAttribute(attributeName);
-        }
-        return attribute;
+        return mystate.getOnHoldExpirationDate();
     }
-

     /**
      * Get the date until which this issue is onhold. This method searches
@@ -4443,41 +4378,19 @@
      */
     public Date getOnHoldUntil() throws TorqueException, ParseException
     {
-        Date date = null;
-        String attributeName = ScarabGlobalTool.getOnHoldExpirationDateAttributeName();
-
-        if (attributeName != null)
-        {
-            AttributeValue dateValue = this.getAttributeValue(attributeName);
-            if(dateValue!=null)
-            {
-                String value = dateValue.getValue();
-                if (value != null && value.length() > 0)
-                {
-                    date = DateAttribute.toDate(value);
-                }
-            }
-        }
-        return date;
+        return mystate.getOnHoldUntil();
     }
-
-

     /**
-     * helper funtion to retrieve properties from the Turbine Configuration sysstem.
-     * @param prop
-     * @param def
+     * If an issue is onHold and the revocation time has expired, the NotificationManager
+     * should periodically send a Notification to the Issue's Observer list. the reminderPeriod
+     * tells how long to wait between 2 reminder notifications. The unit is "hours".
+     * If this property is not set, return 0
      * @return
      */
-    private String getProperty(String prop, String def)
+    public int getReminderPeriod()
     {
-        String result = (String)Turbine.getConfiguration().getProperty(prop);
-        if(result == null)
-        {
-            result = def;
-        }
-        return result;
+        return mystate.getReminderPeriod();
     }

-
 }

Modified: trunk/src/java/org/tigris/scarab/om/IssueType.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/om/IssueType.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/om/IssueType.java	(original)
+++ trunk/src/java/org/tigris/scarab/om/IssueType.java	2009-07-28 07:21:53-0700
@@ -1198,7 +1198,7 @@
      *
      * @return an <code>List</code> of Attribute objects
      */
-    public List getActiveAttributes(final Module module)
+    public List<Attribute> getActiveAttributes(final Module module)
         throws TorqueException
     {
         List attributes = null;

Modified: trunk/src/java/org/tigris/scarab/om/NotificationStatus.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/om/NotificationStatus.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/om/NotificationStatus.java	(original)
+++ trunk/src/java/org/tigris/scarab/om/NotificationStatus.java	2009-07-28 07:21:53-0700
@@ -74,6 +74,7 @@
     static public final Integer FAIL           = new Integer(4);
     static public final Integer SENT           = new Integer(5);
     static public final Integer MARK_DELETED   = new Integer(6);
+    static public final Integer ON_HOLD        = new Integer(7);

     static private final Integer ARCHIVER_ID   = new Integer(-1);

@@ -182,6 +183,7 @@
         if (status.equals(FAIL)) return "fail";
         if (status.equals(SENT)) return "delivered";
         if (status.equals(MARK_DELETED)) return "deleted";
+        if (status.equals(ON_HOLD)) return "onhold";
         throw new RuntimeException("Database inconsistency: status ["+status+"] is not known.");
     }


Modified: trunk/src/java/org/tigris/scarab/om/NotificationStatusPeer.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/om/NotificationStatusPeer.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/om/NotificationStatusPeer.java	(original)
+++ trunk/src/java/org/tigris/scarab/om/NotificationStatusPeer.java	2009-07-28 07:21:53-0700
@@ -82,4 +82,21 @@
 		}
 		return pending;
 	}
+
+    public static List getOnholdNotifications()
+    {
+        List onholdNotificationList = null;
+        Criteria crit = new Criteria();
+        crit.add(NotificationStatusPeer.STATUS, NotificationStatus.ON_HOLD, Criteria.EQUAL);
+        crit.addAscendingOrderByColumn(NotificationStatusPeer.CREATION_DATE);
+        try
+        {
+            onholdNotificationList = doSelect(crit);
+            //Collections.sort(pending);
+        } catch (TorqueException e)
+        {
+            log.error("getPendingNotifications(): " + e);
+        }
+        return onholdNotificationList;
+    }
 }

Modified: trunk/src/java/org/tigris/scarab/tools/ScarabGlobalTool.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/tools/ScarabGlobalTool.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/tools/ScarabGlobalTool.java	(original)
+++ trunk/src/java/org/tigris/scarab/tools/ScarabGlobalTool.java	2009-07-28 07:21:53-0700
@@ -460,7 +460,6 @@
         return result;
     }

-
     /**
      * Makes the workflow tool accessible.
      * @throws ScarabException
@@ -470,51 +469,6 @@
         return WorkflowFactory.getInstance();
     }

-
-    /**
-     * Return the value of the AttributeOption which expresses, that the
-     * issue is "onhold". This directly corresponds to the system property
-     *
-     * "scarab.common.status.onhold"
-     *
-     * If that property is not set or set to empty, then this method returns null.
-     * @return
-     */
-    public static String getOnHoldAttributeOptionValue() {
-        String value = getTurbineProperty("scarab.common.status.onhold", null);
-        return value;
-    }
-
-    /**
-     * Return the name of the Attribute which is interpreted as Issue-"status".
-     * This directly corresponds to the system property
-     *
-     * "scarab.common.status.id"
-     *
-     * If that property is not set or set to empty, then this method returns null.
-     * @return
-     */
-    public static String getStatusAttributeName()
-    {
-        String status = getTurbineProperty("scarab.common.status.id", null);
-        return status;
-    }
-
-    /**
-     * Return the name of the Attribute which is interpreted as the expiration date
-     * for an issue which is "onhold".  This directly corresponds to the system property
-     *
-     * "scarab.common.status.onhold.dateProperty"
-     *
-     * If that property is not set or set to empty, then this method returns null.
-     * @return
-     */
-    public static String getOnHoldExpirationDateAttributeName() {
-        String attributeName = getTurbineProperty("scarab.common.status.onhold.dateProperty", null);
-        return attributeName;
-    }
-
-
     /**
      * Returns a List of users based on the given search criteria. This method
      * is an overloaded function which returns an unsorted list of users.

Modified: trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java	(original)
+++ trunk/src/java/org/tigris/scarab/tools/localization/L10NKeySet.java	2009-07-28 07:21:53-0700
@@ -468,6 +468,7 @@
     public static final LocalizationKey ActivityAssociatedInfo = new L10NKey("ActivityAssociatedInfo");
     public static final LocalizationKey ActivityDependencies = new L10NKey("ActivityDependencies");

+    public static final LocalizationKey NotificationIssueOnHold           = new L10NKey("NotificationIssueOnHold");
     public static final LocalizationKey NotificationIssueCreated          = new L10NKey("NotificationIssueCreated");
     public static final LocalizationKey NotificationIssueMoved            = new L10NKey("NotificationIssueMoved");
     public static final LocalizationKey NotificationIssueCopied           = new L10NKey("NotificationIssueCopied");

Added: trunk/src/java/org/tigris/scarab/workflow/IssueState.java
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/java/org/tigris/scarab/workflow/IssueState.java?view=markup&pathrev838
=============================================================================--- (empty file)
+++ trunk/src/java/org/tigris/scarab/workflow/IssueState.java	2009-07-28 07:21:53-0700
@@ -0,0 +1,341 @@
+package org.tigris.scarab.workflow;

+

+import java.text.ParseException;

+import java.util.Date;

+import java.util.Iterator;

+import java.util.List;

+

+import org.apache.torque.TorqueException;

+import org.apache.turbine.Turbine;

+import org.tigris.scarab.attribute.DateAttribute;

+import org.tigris.scarab.om.Attribute;

+import org.tigris.scarab.om.AttributeValue;

+import org.tigris.scarab.om.Condition;

+import org.tigris.scarab.om.Issue;

+import org.tigris.scarab.om.IssueType;

+import org.tigris.scarab.om.Module;

+import org.tigris.scarab.om.RModuleAttribute;

+import org.tigris.scarab.util.ScarabException;

+import org.tigris.scarab.util.SimpleSkipFiltering;

+import org.tigris.scarab.util.SkipFiltering;

+

+/**

+ * The state class contains a collection of methods, which introduce "state" to Scarab.

+ * Following states are generically supported:

+ *

+ * sealed: the issue is in a mode where it should not be modified any more

+ * active: the issue is currently processed

+ * onhold: the issue is currently not processed, but it is still active (*)

+ *

+ * (*)The onhold state is always accompanied with a revocationDate.

+ *

+ * Currently the state support is only available on a system wide scope. It is

+ * mainly configured by using the Turbine runtime properties. It is planned to

+ * leverage status support at least to module scope. I prefer to leverage it

+ * to module/issueType scope.

+ *

+ * @author hdab

+ *

+ */

+public class IssueState

+{

+

+    private Issue issue; // the issue associated to this State instance

+

+    public IssueState(Issue issue)

+    {

+        this.issue = issue;

+    }

+

+    /**

+     * Create a javascript snippet, which can be used to check if the given

+     * attribute would become required if a specific attributeOption was set.

+     * This is an example javascript showing how to use this snippet:

+     *

+     *

+     *      // Helper array contains all RequiredMarkup elements

+     *      // which have already been visited form the current call of

+     *      // the conditions_observer. It is needed to correctly

+     *      // set the display mode (inline/none) of all visited markers.

+     *

+     *      var visitedMarkers = {};

+     *

+     *      // This function takes care about the "required attribute" markup.

+     *      // It adjusts the required-markup dynamically while the user edits the issue.

+     *      //

+     *      function conditions_observer(data)

+     *      {

+     *        if(data[0] == "treeview")

+     *        {

+     *            var attributeName = data[1];

+     *            var displayValue  = data[4];

+     *

+     *            //alert(data[1] + "=" + data[4]);

+     *

+     *            #if ($currentIssue)

+     *#set ($indent)

+     *$currentIssue.createIssueChecker("setMarkerValueIf", $indent)

+     *            #end

+     *

+     *            visitedMarkers = {};

+     *

+     *        }

+     *      }

+     *

+     *      // This helper function actually makes the requiredMarkup

+     *      //  visible or hides it depending on the condition.

+     *      //  Note: Currently only OR conditions are supported here!!!

+     *      function setMarkerValueIf(condAttName, isRequired)

+     *      {

+     *          var cid = "conditional:"+condAttName;

+     *          var element = getElementByIdCompatible(cid);

+     *          if (element != null)

+     *          {

+     *              if(isRequired)

+     *              {

+     *                visitedMarkers[condAttName] = "inline";

+     *              }

+     *              else

+     *              {

+     *                if (visitedMarkers[condAttName]==null)

+     *                {

+     *                   visitedMarkers[condAttName] = "none";

+     *                }

+     *              }

+     *              //alert ("Set " + cid + " to " + visitedMarkers[condAttName] );

+     *              element.style.display = visitedMarkers[condAttName];

+     *            }

+     *      }

+     *

+     * @param attribute

+     * @return

+     * @throws TorqueException

+     * @throws ScarabException

+     */

+    public  SkipFiltering createIssueChecker(String setMarkerFunction, int indent) throws TorqueException, ScarabException

+    {

+        String result = "";

+        List<Attribute> attributes = null;

+        Module module = issue.getModule();

+        IssueType issueType = issue.getIssueType();

+        attributes = issueType.getActiveAttributes(module);

+        Iterator<Attribute> iter = attributes.iterator();

+        while (iter.hasNext())

+        {

+            Attribute attribute = iter.next();

+            RModuleAttribute rma  = module.getRModuleAttribute(attribute, issueType);

+            result += createIssueChecker(rma, setMarkerFunction, indent);

+        }

+

+        String prepend;

+        if(result.length() > 0)

+        {

+            prepend="\n";

+        }

+        else

+        {

+            prepend = "/* No conditional attributes found for this module/issueType */";

+        }

+

+        return new SimpleSkipFiltering(prepend+result);

+    }

+

+    /**

+     * Helper function. Creates javascript code to examine the available required conditions

+     * in order to dynamically set/hide the "required attribute markers on screen.

+     * @param rma

+     * @param setMarkerFunction

+     * @param indent

+     * @return

+     * @throws TorqueException

+     * @throws ScarabException

+     */

+    private String createIssueChecker(RModuleAttribute rma, String setMarkerFunction, int indent) throws TorqueException, ScarabException

+    {

+        String result = "";

+        boolean isRequired = rma.getRequired();

+        if(!isRequired)

+        {

+            List<Condition> conditions = rma.getConditions();

+

+            Iterator<Condition> iter = conditions.iterator();

+            while(iter.hasNext())

+            {

+                Condition condition = iter.next();

+                result += condition.createConditionCheckerScript(rma, setMarkerFunction, indent);

+            }

+        }

+        return result;

+    }

+

+

+

+

+    /**

+     * Check if the properties scarab.common.status.id and scarab.common.status.sealed

+     * exist and if the current value of the status attribute matches the sealed

+     * value. Return true, if the issue is in the sealed state, otherwise return false.

+     * This method is used to find out if an issue shoul dbe rendered read-only

+     * because it is in closed (sealed) state and should never be touched again.

+     * @return

+     * @throws TorqueException

+     */

+    public boolean isSealed() throws TorqueException

+    {

+        boolean result = false;

+        String status = getProperty("scarab.common.status.id", null);

+        if (status != null)

+        {

+            String value = getProperty("scarab.common.status.sealed", null);

+            if(value != null)

+            {

+                AttributeValue attval = issue.getAttributeValue(status);

+                if(attval != null && attval.getValue().equals(value))

+                {

+                    result = true;

+                }

+            }

+        }

+        return result;

+    }

+

+    /**

+     * Check if this issue is on hold. Currently we use the attribute

+     * which has been specified by the system property

+     * scarab.common.status.id as the relevant attribute to check.

+     * We tell this issue is on hold when the status-attribute contains

+     * the value specified by the system property "scarab.common.status.onhold"

+     * By default an issue is onhold if attribute "status" == "onhold")

+     * @return

+     * @throws TorqueException

+     */

+    public boolean isOnHold() throws TorqueException

+    {

+        boolean result = false;

+        String status = getProperty("scarab.common.status.id", null);

+        if (status != null)

+        {

+            String value = getProperty("scarab.common.status.onhold", null);

+            if(value != null)

+            {

+                AttributeValue attval = issue.getAttributeValue(status);

+                if(attval != null && attval.getValue().equals(value))

+                {

+                    result = true;

+                }

+            }

+        }

+        return result;

+    }

+

+    /**

+     * Check if this issue is active (not on hold and not sealed).

+     * @param issue

+     * @return

+     * @throws TorqueException

+     */

+    public boolean isActive() throws TorqueException

+    {

+        return !(isOnHold() || isSealed());

+    }

+

+    /**

+     * Returns the attribute instance, which contains the issues status.

+     * Note:  Currently Scarab expects that the Attribute is a drop down list

+     * (i.e. its data type is AttributeOptionValue)

+     * @return

+     * @throws TorqueException

+     */

+    public Attribute getStatusAttribute() throws TorqueException

+    {

+        Attribute attribute = null;

+        String attributeName = getProperty("scarab.common.status.id", null);

+        if(attributeName != null)

+        {

+            attribute = issue.getAttribute(attributeName);

+        }

+        return attribute;

+    }

+

+    /**

+     * Returns the attribute instance, which contains the issues onHoldExpirationDate.

+     * Note:  Currently Scarab expects that the Attribute is a DateAttribute

+     * @return

+     * @throws TorqueException

+     */

+    public Attribute getOnHoldExpirationDate() throws TorqueException

+    {

+        Attribute attribute = null;

+        String attributeName = getProperty("scarab.common.status.onhold.dateProperty", null);

+        if(attributeName != null)

+        {

+            attribute = issue.getAttribute(attributeName);

+        }

+        return attribute;

+    }

+

+

+    /**

+     * Get the date until which this issue is onhold. This method searches

+     * for the attribute specified by the system property "scarab.common.status.onhold.dateProperty"

+     * And we expect this attribute to contain a Date value.

+     * @return

+     * @throws TorqueException

+     * @throws ParseException

+     */

+    public Date getOnHoldUntil() throws TorqueException, ParseException

+    {

+        Date date = null;

+        String attributeName = getProperty("scarab.common.status.onhold.dateProperty", null);

+

+        if (attributeName != null)

+        {

+            AttributeValue dateValue = issue.getAttributeValue(attributeName);

+            if(dateValue!=null)

+            {

+                String value = dateValue.getValue();

+                if (value != null && value.length() > 0)

+                {

+                    date = DateAttribute.toDate(value);

+                }

+            }

+        }

+        return date;

+    }

+

+    /**

+     * If an issue is onHold and the revocation time has expired, the NotificationManager

+     * should periodically send a Notification to the Issue's Observer list. the reminderPeriod

+     * tells how long to wait between 2 reminder notifications. The unit is "hours".

+     * If this property is not set, return 0

+     * @return

+     */

+    public int getReminderPeriod()

+    {

+        String rp = getProperty("scarab.common.status.onhold.reminder.period", null);

+        int result = 0;

+        if (rp != null)

+        {

+            result = Integer.parseInt(rp);

+        }

+        return result;

+    }

+

+

+    /**

+     * helper funtion to retrieve properties from the Turbine Configuration sysstem.

+     * @param prop

+     * @param def

+     * @return

+     */

+    private String getProperty(String prop, String def)

+    {

+        String result = (String)Turbine.getConfiguration().getProperty(prop);

+        if(result == null)

+        {

+            result = def;

+        }

+        return result;

+    }

+

+}


Modified: trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivity.vm
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivity.vm?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivity.vm	(original)
+++ trunk/src/webapp/WEB-INF/templates/email/notification/IssueActivity.vm	2009-07-28 07:21:53-0700
@@ -97,7 +97,7 @@
 ${cr}-------------------------------------------------------------------------------
 #end
 #markup('b')${l10n.Comment} $not.Creator.Name#markup('b')$cr
-$not.Activity.Attachment.Data
+$l10n.getIgnoreMissingResource($not.Activity.Attachment.Data)
 #end
 #end
 ${cr}-------------------------------------------------------------------------------

Modified: trunk/src/webapp/WEB-INF/templates/viewIssue/ViewIssueTab5.vm
Url: http://scarab.tigris.org/source/browse/scarab/trunk/src/webapp/WEB-INF/templates/viewIssue/ViewIssueTab5.vm?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/src/webapp/WEB-INF/templates/viewIssue/ViewIssueTab5.vm	(original)
+++ trunk/src/webapp/WEB-INF/templates/viewIssue/ViewIssueTab5.vm	2009-07-28 07:21:53-0700
@@ -24,15 +24,15 @@
                 #set ($oldValue = $act.getOldValue($l10n).trim())
                 <th style="width:12em;">$act.getDisplayName($l10n)</th>
                 #if( $oldValue.length()>0 )
-                  <td>$!scarabG.textToHTML($oldValue, $link, $scarabR.CurrentModule)</td>
+                  <td>$!scarabG.textToHTML($l10n.getIgnoreMissingResource($oldValue), $link, $scarabR.CurrentModule)</td>
                   <td style="vertical-align:middle;" ><img name="change to" src="$staticLink.setPath('/images/icon_rightarrow_long.jpg')"/></td>
                   #if ( $newValue.length() > 0 )
-                    <td>$!scarabG.textToHTML($newValue, $link, $scarabR.CurrentModule)</td>
+                    <td>$!scarabG.textToHTML($l10n.getIgnoreMissingResource($newValue), $link, $scarabR.CurrentModule)</td>
                   #else
                     <td>[$l10n.Deleted]</td>
                   #end
                 #else
-                  <td colspan="3" style="white-space:normal;">$!scarabG.textToHTML($newValue, $link, $scarabR.CurrentModule)</td>
+                  <td colspan="3" style="white-space:normal;"> $!scarabG.textToHTML($l10n.getIgnoreMissingResource($newValue), $link, $scarabR.CurrentModule)</td>
                 #end
                 <td width="*"> &nbsp; </td>
               #else
@@ -45,7 +45,7 @@
           #if($reason.length()>0)
             <tr>
               <th style="width:12em;">$l10n.Comment</th>
-              <td colspan="4" width="*" style="white-space:normal;">$!scarabG.textToHTML($reason, $link, $scarabR.CurrentModule)</td>
+              <td colspan="4" width="*" style="white-space:normal;">$!scarabG.textToHTML($l10n.getIgnoreMissingResource($reason), $link, $scarabR.CurrentModule)</td>
             </tr>
           #end
         </table>

Modified: trunk/xdocs/scarab_properties.xml
Url: http://scarab.tigris.org/source/browse/scarab/trunk/xdocs/scarab_properties.xml?view=diff&pathrev838&r1837&r2838
=============================================================================--- trunk/xdocs/scarab_properties.xml	(original)
+++ trunk/xdocs/scarab_properties.xml	2009-07-28 07:21:53-0700
@@ -1119,6 +1119,20 @@
       </property>
 	
 	
+      <property>
+        <name>scarab.common.status.onhold.reminder.period</name>
+        <default>1440</default>
+        <comment/>
+         <type>Runtime</type>
+         <customization modification="optional">advanced</customization>
+        <file/>
+        <details>
+          This attribute tells the NotificationManager how long to wait
+		  between sending reminder notifications for issues in state onhold.
+		  the unit is minutes. The default setting is 1440 (1 day)
+        </details>
+      </property>
+	  	

       <property>
         <name>scarab.common.status.sealed.modifyPermission</name>


------------------------------------------------------
http://scarab.tigris.org/ds/viewMessage.do?dsForumId=3577&dsMessageId=2376291
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.