Fixed memory leak (and refactored code).
This commit is contained in:
parent
ab80a18c18
commit
5bd3f0e0b4
1 changed files with 193 additions and 213 deletions
|
@ -17,151 +17,143 @@ namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Implements a formatter that display tickets from Unfuddle.
|
/// Implements a formatter that display tickets from Unfuddle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class UnfuddleTickets : IFormatterProviderV30 {
|
public class UnfuddleTickets : IFormatterProviderV30 {
|
||||||
|
|
||||||
private const string ConfigHelpHtmlValue = "Config consists of three lines:<br/><i><Url></i> - The base url to the Unfuddle API (i.e. http://account_name.unfuddle.com/api/v1/projects/project_ID)<br/><i><Username></i> - The username to the unfuddle account to use for authentication<br/><i><Password></i> - The password to the unfuddle account to use for authentication<br/>";
|
private const string ConfigHelpHtmlValue = "Config consists of three lines:<br/><i><Url></i> - The base url to the Unfuddle API (i.e. http://account_name.unfuddle.com/api/v1/projects/project_ID)<br/><i><Username></i> - The username to the unfuddle account to use for authentication<br/><i><Password></i> - The password to the unfuddle account to use for authentication<br/>";
|
||||||
private const string LoadErrorMessage = "Unable to load ticket report at this time.";
|
private const string LoadErrorMessage = "Unable to load ticket report at this time.";
|
||||||
private static readonly Regex UnfuddleRegex = new Regex(@"{unfuddle}", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
private static readonly Regex UnfuddleRegex = new Regex(@"{unfuddle}", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||||
private static readonly ComponentInformation Info = new ComponentInformation("Unfuddle Tickets Plugin", "Threeplicate Srl", "3.0.1.472", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/UnfuddleTickets2.txt");
|
private static readonly ComponentInformation Info = new ComponentInformation("Unfuddle Tickets Plugin", "Threeplicate Srl", "3.0.2.538", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/UnfuddleTickets2.txt");
|
||||||
|
|
||||||
private string _config;
|
private string _config;
|
||||||
private IHostV30 _host;
|
private IHostV30 _host;
|
||||||
private string _baseUrl;
|
private string _baseUrl;
|
||||||
private string _username;
|
private string _username;
|
||||||
private string _password;
|
private string _password;
|
||||||
|
|
||||||
#region IFormatterProviderV30 Members
|
private XslCompiledTransform _xslTransform = null;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specifies whether or not to execute Phase 1.
|
/// Initializes the Storage Provider.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool PerformPhase1 {
|
/// <param name="host">The Host of the Component.</param>
|
||||||
get {
|
/// <param name="config">The Configuration data, if any.</param>
|
||||||
return false;
|
/// <remarks>If the configuration string is not valid, the methoud should throw a <see cref="InvalidConfigurationException"/>.</remarks>
|
||||||
}
|
public void Init(IHostV30 host, string config) {
|
||||||
}
|
_host = host;
|
||||||
|
_config = config ?? string.Empty;
|
||||||
|
var configEntries = _config.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
/// <summary>
|
if(configEntries.Length != 3) throw new InvalidConfigurationException("Configuration is missing required parameters");
|
||||||
/// Specifies whether or not to execute Phase 2.
|
|
||||||
/// </summary>
|
|
||||||
public bool PerformPhase2 {
|
|
||||||
get {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
_baseUrl = configEntries[0];
|
||||||
/// Specifies whether or not to execute Phase 3.
|
|
||||||
/// </summary>
|
|
||||||
public bool PerformPhase3 {
|
|
||||||
get {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
if(_baseUrl.EndsWith("/"))
|
||||||
/// Gets the execution priority of the provider (0 lowest, 100 highest).
|
_baseUrl = _baseUrl.Substring(0, _baseUrl.Length - 1);
|
||||||
/// </summary>
|
|
||||||
public int ExecutionPriority {
|
|
||||||
get {
|
|
||||||
return 50;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
_username = configEntries[1];
|
||||||
/// Gets the Information about the Provider.
|
_password = configEntries[2];
|
||||||
/// </summary>
|
|
||||||
public ComponentInformation Information {
|
|
||||||
get {
|
|
||||||
return Info;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
var settings = new XsltSettings {
|
||||||
/// Performs a Formatting phase.
|
EnableScript = true,
|
||||||
/// </summary>
|
EnableDocumentFunction = true
|
||||||
/// <param name="raw">The raw content to Format.</param>
|
};
|
||||||
/// <param name="context">The Context information.</param>
|
_xslTransform = new XslCompiledTransform(true);
|
||||||
/// <param name="phase">The Phase.</param>
|
using(var reader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("ScrewTurn.Wiki.Plugins.PluginPack.Resources.UnfuddleTickets.xsl"))) {
|
||||||
/// <returns>The Formatted content.</returns>
|
_xslTransform.Load(reader, settings, new XmlUrlResolver());
|
||||||
public string Format(string raw, ContextInformation context, FormattingPhase phase) {
|
}
|
||||||
var buffer = new StringBuilder(raw);
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs a Formatting phase.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="raw">The raw content to Format.</param>
|
||||||
|
/// <param name="context">The Context information.</param>
|
||||||
|
/// <param name="phase">The Phase.</param>
|
||||||
|
/// <returns>The Formatted content.</returns>
|
||||||
|
public string Format(string raw, ContextInformation context, FormattingPhase phase) {
|
||||||
|
var buffer = new StringBuilder(raw);
|
||||||
|
|
||||||
var block = FindAndRemoveFirstOccurrence(buffer);
|
var block = FindAndRemoveFirstOccurrence(buffer);
|
||||||
|
|
||||||
if(block.Key != -1) {
|
if(block.Key != -1) {
|
||||||
string unfuddleTickets = null;
|
string unfuddleTickets = null;
|
||||||
if(HttpContext.Current != null)
|
if(HttpContext.Current != null)
|
||||||
unfuddleTickets = HttpContext.Current.Cache["UnfuddleTicketsStore"] as string;
|
unfuddleTickets = HttpContext.Current.Cache["UnfuddleTicketsStore"] as string;
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(unfuddleTickets))
|
if(string.IsNullOrEmpty(unfuddleTickets))
|
||||||
unfuddleTickets = LoadUnfuddleTicketsFromWeb();
|
unfuddleTickets = LoadUnfuddleTicketsFromWeb();
|
||||||
|
|
||||||
if(string.IsNullOrEmpty(unfuddleTickets))
|
if(string.IsNullOrEmpty(unfuddleTickets))
|
||||||
unfuddleTickets = LoadErrorMessage;
|
unfuddleTickets = LoadErrorMessage;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
buffer.Insert(block.Key, unfuddleTickets);
|
buffer.Insert(block.Key, unfuddleTickets);
|
||||||
block = FindAndRemoveFirstOccurrence(buffer);
|
block = FindAndRemoveFirstOccurrence(buffer);
|
||||||
} while(block.Key != -1);
|
} while(block.Key != -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer.ToString();
|
return buffer.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Prepares the title of an item for display (always during phase 3).
|
/// Prepares the title of an item for display (always during phase 3).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="title">The input title.</param>
|
/// <param name="title">The input title.</param>
|
||||||
/// <param name="context">The context information.</param>
|
/// <param name="context">The context information.</param>
|
||||||
/// <returns>The prepared title (no markup allowed).</returns>
|
/// <returns>The prepared title (no markup allowed).</returns>
|
||||||
public string PrepareTitle(string title, ContextInformation context) {
|
public string PrepareTitle(string title, ContextInformation context) {
|
||||||
return title;
|
return title;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes the Storage Provider.
|
/// Method invoked on shutdown.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="host">The Host of the Component.</param>
|
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||||
/// <param name="config">The Configuration data, if any.</param>
|
public void Shutdown() {
|
||||||
/// <remarks>If the configuration string is not valid, the methoud should throw a <see cref="InvalidConfigurationException"/>.</remarks>
|
// Nothing to do
|
||||||
public void Init(IHostV30 host, string config) {
|
}
|
||||||
_host = host;
|
|
||||||
_config = config ?? string.Empty;
|
|
||||||
var configEntries = _config.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
if(configEntries.Length != 3)
|
/// <summary>
|
||||||
throw new InvalidConfigurationException("Configuration missing required parameters.");
|
/// Gets a brief summary of the configuration string format, in HTML. Returns <c>null</c> if no configuration is needed.
|
||||||
|
/// </summary>
|
||||||
|
public string ConfigHelpHtml {
|
||||||
|
get { return ConfigHelpHtmlValue; }
|
||||||
|
}
|
||||||
|
|
||||||
_baseUrl = configEntries[0];
|
/// <summary>
|
||||||
|
/// Specifies whether or not to execute Phase 1.
|
||||||
|
/// </summary>
|
||||||
|
public bool PerformPhase1 {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
if(_baseUrl.EndsWith("/"))
|
/// <summary>
|
||||||
_baseUrl = _baseUrl.Substring(0, _baseUrl.Length - 1);
|
/// Specifies whether or not to execute Phase 2.
|
||||||
|
/// </summary>
|
||||||
|
public bool PerformPhase2 {
|
||||||
|
get { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
_username = configEntries[1];
|
/// <summary>
|
||||||
_password = configEntries[2];
|
/// Specifies whether or not to execute Phase 3.
|
||||||
}
|
/// </summary>
|
||||||
|
public bool PerformPhase3 {
|
||||||
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Method invoked on shutdown.
|
/// Gets the execution priority of the provider (0 lowest, 100 highest).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
public int ExecutionPriority {
|
||||||
public void Shutdown() {
|
get { return 50; }
|
||||||
// Nothing to do
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a brief summary of the configuration string format, in HTML. Returns <c>null</c> if no configuration is needed.
|
/// Gets the Information about the Provider.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ConfigHelpHtml {
|
public ComponentInformation Information {
|
||||||
get {
|
get { return Info; }
|
||||||
return ConfigHelpHtmlValue;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Private Methods
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds and removes the first occurrence of the custom tag.
|
/// Finds and removes the first occurrence of the custom tag.
|
||||||
|
@ -180,113 +172,101 @@ namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||||
return new KeyValuePair<int, string>(-1, null);
|
return new KeyValuePair<int, string>(-1, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds an xml document from API calls to Unfuddle.com then runs them through an Xslt to format them.
|
/// Builds an xml document from API calls to Unfuddle.com then runs them through an Xslt to format them.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>An html string that contains the tables to display the ticket information, or null</returns>
|
/// <returns>An html string that contains the tables to display the ticket information, or null</returns>
|
||||||
private string LoadUnfuddleTicketsFromWeb() {
|
private string LoadUnfuddleTicketsFromWeb() {
|
||||||
var xml = BuildXmlFromApiCalls();
|
var xml = BuildXmlFromApiCalls();
|
||||||
if(xml == null)
|
if(xml == null) return null;
|
||||||
return null;
|
|
||||||
|
|
||||||
var settings = new XsltSettings {
|
string results;
|
||||||
EnableScript = true,
|
using(var sw = new StringWriter()) {
|
||||||
EnableDocumentFunction = true
|
using(var xnr = new XmlNodeReader(xml)) {
|
||||||
};
|
_xslTransform.Transform(xnr, null, sw);
|
||||||
var xsl = new XslCompiledTransform(true);
|
}
|
||||||
using(var reader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("ScrewTurn.Wiki.Plugins.PluginPack.Resources.UnfuddleTickets.xsl"))) {
|
results = sw.ToString();
|
||||||
xsl.Load(reader, settings, new XmlUrlResolver());
|
}
|
||||||
}
|
|
||||||
|
|
||||||
string results;
|
HttpContext.Current.Cache.Add("UnfuddleTicketsStore", results, null, DateTime.Now.AddMinutes(10),
|
||||||
using(var sw = new StringWriter()) {
|
|
||||||
using(var xnr = new XmlNodeReader(xml)) {
|
|
||||||
xsl.Transform(xnr, null, sw);
|
|
||||||
}
|
|
||||||
results = sw.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpContext.Current.Cache.Add("UnfuddleTicketsStore", results, null, DateTime.Now.AddMinutes(10),
|
|
||||||
Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
|
Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Builds 3 Xml Documents, the first two are lookups for Milestone, and People information, the second is the
|
/// Builds 3 Xml Documents, the first two are lookups for Milestone, and People information, the second is the
|
||||||
/// ticket information.
|
/// ticket information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private XmlDocument BuildXmlFromApiCalls() {
|
private XmlDocument BuildXmlFromApiCalls() {
|
||||||
var milestones = GetXml("/milestones", _username, _password);
|
var milestones = GetXml("/milestones", _username, _password);
|
||||||
if(milestones == null) {
|
if(milestones == null) {
|
||||||
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var people = GetXml("/people", _username, _password);
|
var people = GetXml("/people", _username, _password);
|
||||||
if(people == null) {
|
if(people == null) {
|
||||||
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tickets = GetXml("/ticket_reports/dynamic?sort_by=priority&sort_direction=DESC&conditions_string=status-neq-closed&group_by=priority&fields_string=number,priority,summary,milestone,status,version", _username, _password);
|
var tickets = GetXml("/ticket_reports/dynamic?sort_by=priority&sort_direction=DESC&conditions_string=status-neq-closed&group_by=priority&fields_string=number,priority,summary,milestone,status,version", _username, _password);
|
||||||
if(tickets == null) {
|
if(tickets == null) {
|
||||||
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var results = new XmlDocument();
|
var results = new XmlDocument();
|
||||||
results.AppendChild(results.CreateXmlDeclaration("1.0", "UTF-8", string.Empty));
|
results.AppendChild(results.CreateXmlDeclaration("1.0", "UTF-8", string.Empty));
|
||||||
var element = results.CreateElement("root");
|
var element = results.CreateElement("root");
|
||||||
results.AppendChild(element);
|
results.AppendChild(element);
|
||||||
element.AppendChild(results.ImportNode(milestones.ChildNodes[1], true));
|
element.AppendChild(results.ImportNode(milestones.ChildNodes[1], true));
|
||||||
element.AppendChild(results.ImportNode(people.ChildNodes[1], true));
|
element.AppendChild(results.ImportNode(people.ChildNodes[1], true));
|
||||||
element.AppendChild(results.ImportNode(tickets.ChildNodes[1], true));
|
element.AppendChild(results.ImportNode(tickets.ChildNodes[1], true));
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Produces an API call, then returns the results as an Xml Document
|
/// Produces an API call, then returns the results as an Xml Document
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Url">The Url to the specific API call</param>
|
/// <param name="Url">The Url to the specific API call</param>
|
||||||
/// <param name="Username">An unfuddle account username</param>
|
/// <param name="Username">An unfuddle account username</param>
|
||||||
/// <param name="Password">The password to above unfuddle account</param>
|
/// <param name="Password">The password to above unfuddle account</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private XmlDocument GetXml(string Url, string Username, string Password) {
|
private XmlDocument GetXml(string Url, string Username, string Password) {
|
||||||
try {
|
try {
|
||||||
var results = new XmlDocument();
|
var results = new XmlDocument();
|
||||||
Url = string.Format("{0}{1}", _baseUrl, Url);
|
Url = string.Format("{0}{1}", _baseUrl, Url);
|
||||||
var request = WebRequest.Create(Url);
|
var request = WebRequest.Create(Url);
|
||||||
request.Credentials = new NetworkCredential(Username, Password);
|
request.Credentials = new NetworkCredential(Username, Password);
|
||||||
var response = request.GetResponse();
|
var response = request.GetResponse();
|
||||||
using(var reader = new StreamReader(response.GetResponseStream())) {
|
using(var reader = new StreamReader(response.GetResponseStream())) {
|
||||||
var xmlString = reader.ReadToEnd();
|
var xmlString = reader.ReadToEnd();
|
||||||
try {
|
try {
|
||||||
results.LoadXml(xmlString);
|
results.LoadXml(xmlString);
|
||||||
}
|
}
|
||||||
catch {
|
catch {
|
||||||
LogWarning("Received Unexpected Response from Unfuddle Server.");
|
LogWarning("Received Unexpected Response from Unfuddle Server.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
catch(Exception ex) {
|
catch(Exception ex) {
|
||||||
LogWarning(string.Format("Exception occurred: {0}", ex.Message));
|
LogWarning(string.Format("Exception occurred: {0}", ex.Message));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Logs a warning.
|
/// Logs a warning.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="message">The message.</param>
|
/// <param name="message">The message.</param>
|
||||||
private void LogWarning(string message) {
|
private void LogWarning(string message) {
|
||||||
_host.LogEntry(message, LogEntryType.Warning, null, this);
|
_host.LogEntry(message, LogEntryType.Warning, null, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue