Skip to content

Commit

Permalink
Issue Subject formatted text uses named parameters
Browse files Browse the repository at this point in the history
IssueUpdatedSubject (in Global Notifications.resx) can now use any of
the following named parameters:
* IssueId
* IssueTitle
* DisplayName
* FieldName
* NewValue
* OldValue
where the FieldName, NewValue and OldValue are from the IssueHistory
selected to have the highest priority (based on FieldChanged). At the
moment this priority order is hard-coded as: "Title", "Status",
"Resolution", "Priority", "Description"

Created a FormatContentWith function in CultureContent to be used
instead of the FormatContent. Based on code from:
http://james.newtonking.com/archive/2008/03/29/formatwith-2-0-string-formatting-with-named-variables

Would need to have, perhaps, a different subject if the value was long
(such as for Description...perhaps just truncate if using?), or if the
Title (as often may want to say Issue "OldValue" (id) title has changed
to "NewValue", rather than Issue "Title" (id) FieldName has changed to
"NewValue"
  • Loading branch information
aliciam committed Jan 8, 2015
1 parent 4110a9f commit 834b837
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 93 deletions.
47 changes: 44 additions & 3 deletions src/BugNET.BLL/IssueNotificationManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Linq;
using System.Net.Mail;
using System.Web;
using System.Web.UI.WebControls;
using BugNET.BLL.Notifications;
using BugNET.Common;
using BugNET.DAL;
Expand Down Expand Up @@ -233,7 +234,12 @@ public static void SendIssueNotifications(int issueId, IEnumerable<IssueHistory>
var data = new Dictionary<string, object> {{"Issue", issue}};

var writer = new System.IO.StringWriter();

var issueHistories = issueChanges as IssueHistory[] ?? issueChanges.ToArray();

var fieldPriorityOrder = new string[] { "Title", "Status", "Resolution", "Priority", "Description" }; //and the rest don't matter
var priorityHistory = GetPriorityHistory(issueHistories, fieldPriorityOrder);

using (System.Xml.XmlWriter xml = new System.Xml.XmlTextWriter(writer))
{
xml.WriteStartElement("IssueHistoryChanges");
Expand All @@ -250,10 +256,15 @@ public static void SendIssueNotifications(int issueId, IEnumerable<IssueHistory>
}

var templateCache = new List<CultureNotificationContent>();

IssueHistory statusChange = issueHistories.FirstOrDefault(h => h.FieldChanged == "Status");
bool statusChanged = statusChange != null;
string subjectKey = "IssueUpdatedSubject";

var emailFormatKey = (emailFormatType == EmailFormatType.Text) ? "" : "HTML";
const string subjectKey = "IssueUpdatedSubject";
var bodyKey = string.Concat("IssueUpdatedWithChanges", emailFormatKey);


// get a list of distinct cultures
var distinctCultures = (from c in issNotifications
select c.NotificationCulture
Expand Down Expand Up @@ -285,15 +296,25 @@ select c.NotificationCulture
// skip to the next user if the changes are of no interest to this user
bool changeIsOfInterest = false;
int[] notificationPrefs = Array.ConvertAll(profile.NotificationOfChanges.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries), int.Parse);
if (issueHistories.Any(h => h.FieldChanged == "Status") && notificationPrefs.Any(i => i == (int)NotificationOfChange.IssueStatusChanged)) changeIsOfInterest = true;
if (statusChanged && notificationPrefs.Any(i => i == (int)NotificationOfChange.IssueStatusChanged)) changeIsOfInterest = true;
if (issueHistories.Any(h => h.FieldChanged != "Status") && notificationPrefs.Any(i => i == (int)NotificationOfChange.IssueOtherColumnChanged)) changeIsOfInterest = true;
if (!changeIsOfInterest) continue;

var nc = templateCache.First(p => p.CultureString == notification.NotificationCulture);

var subjectData = new
{
IssueId = issue.FullId,
IssueTitle = issue.Title,
DisplayName = displayname,
FieldName = priorityHistory.FieldChanged,
NewValue = priorityHistory.NewValue,
OldValue = priorityHistory.OldValue
};

var emailSubject = nc.CultureContents
.First(p => p.ContentKey == subjectKey)
.FormatContent(issue.FullId, displayname);
.FormatContentWith(subjectData);

var bodyContent = nc.CultureContents
.First(p => p.ContentKey == bodyKey)
Expand All @@ -315,6 +336,7 @@ select c.NotificationCulture
}
}


/// <summary>
/// Sends an email to the user that is assigned to the issue
/// </summary>
Expand Down Expand Up @@ -461,6 +483,25 @@ select c.NotificationCulture
}
}


/// <summary>
/// Gets the IssueHistory in the list with the highest priority field name (as per the passed in list), or the first one
/// if no field names appear in the priority list.
/// </summary>
/// <param name="issueChanges">The non-null list of IssueHistory changes</param>
/// <param name="fieldPriorityOrder">An list of fieldnames in order of decreasing priority.</param>
/// <returns>The highest priority IssueHistory change.</returns>
private static IssueHistory GetPriorityHistory(IssueHistory[] issueChanges, IEnumerable<string> fieldPriorityOrder)
{
foreach (string fieldname in fieldPriorityOrder)
{
var issueChange = issueChanges.FirstOrDefault(change => change.FieldChanged == fieldname);
if (issueChange != null) return issueChange;
}
return issueChanges.First();
}


/// <summary>
/// Processes the exception by logging and throwing a wrapper exception with non-sensitive data.
/// </summary>
Expand Down
40 changes: 40 additions & 0 deletions src/BugNET.BLL/Notifications/CultureContent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web.UI;

namespace BugNET.BLL.Notifications
{
Expand Down Expand Up @@ -56,6 +59,43 @@ public string FormatContent(params object[] contentValues)
return content;
}


/// <summary>
/// Used when culture content is in the string.format format, except with named (rather than numbered) parameters
/// </summary>
/// <param name="source">The anonymous object containing the data to be inserted into the string.format call</param>
/// <returns>The formatted string with parameter values inserted at the specified places.</returns>
public string FormatContentWith(object source)
{
if (source == null) return Content;

var r = new Regex(@"(?<start>\{)+(?<property>[\w\.\[\]]+)(?<format>:[^}]+)?(?<end>\})+",
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);


var values = new List<object>();

string rewrittenFormat = r.Replace(Content, delegate(Match m)
{
Group startGroup = m.Groups["start"];
Group propertyGroup = m.Groups["property"];
Group formatGroup = m.Groups["format"];
Group endGroup = m.Groups["end"];

values.Add((propertyGroup.Value == "0")? source: DataBinder.Eval(source, propertyGroup.Value));

return new string('{', startGroup.Captures.Count) + (values.Count - 1) + formatGroup.Value
+ new string('}', endGroup.Captures.Count);
});

SetCultureThread();
var content = string.Format(rewrittenFormat, values.ToArray());
ResetCultureThread();

return content;
}


private void SetCultureThread()
{
// store the current culture
Expand Down
6 changes: 3 additions & 3 deletions src/BugNET_WAP/App_GlobalResources/Notifications.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 834b837

Please sign in to comment.