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="*"> </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