Fixed and closed #494: PluginPack now compiled as separate assemblies. Also implemented Updater plugin to silently replace PluginPack.dll with new DLLs.
This commit is contained in:
parent
0a54e553cc
commit
c89e576368
26 changed files with 905 additions and 936 deletions
|
@ -1,373 +0,0 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a formatter provider that counts download of files and attachments.
|
||||
/// </summary>
|
||||
public class DownloadCounter : IFormatterProviderV30 {
|
||||
|
||||
private static readonly DateTime DefaultStartDate = new DateTime(2009, 1, 1);
|
||||
private const string CountPlaceholder = "#count#";
|
||||
private const string DailyPlaceholder = "#daily#";
|
||||
private const string WeeklyPlaceholder = "#weekly#";
|
||||
private const string MonthlyPlaceholder = "#monthly#";
|
||||
|
||||
private IHostV30 _host;
|
||||
private string _config;
|
||||
private bool _enableLogging = true;
|
||||
private static readonly ComponentInformation Info = new ComponentInformation("Download Counter Plugin", "Threeplicate Srl", "3.0.1.471", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/DownloadCounter.txt");
|
||||
|
||||
private static readonly Regex XmlRegex = new Regex(@"\<countDownloads(.+?)\>(.+?)\<\/countDownloads\>",
|
||||
RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 1.
|
||||
/// </summary>
|
||||
public bool PerformPhase1 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 2.
|
||||
/// </summary>
|
||||
public bool PerformPhase2 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 3.
|
||||
/// </summary>
|
||||
public bool PerformPhase3 {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution priority of the provider (0 lowest, 100 highest).
|
||||
/// </summary>
|
||||
public int ExecutionPriority {
|
||||
get { return 50; }
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
// <countDownloads pattern="..."[ startDate="yyyy/mm/dd"]>
|
||||
// <file name="..."[ provider="..."] />
|
||||
// <attachment name="..." page="..."[ provider="..."] />
|
||||
// </countDownloads>
|
||||
// All downloads are grouped together
|
||||
// Pattern placeholders: #COUNT#, #DAILY#, #WEEKLY#, #MONTHLY# (case insensitive)
|
||||
// Pattern example: "Downloaded #COUNT# times (#MONTHLY#/month)!"
|
||||
// StartDate omitted -> 2009/01/01
|
||||
// Provider omitted -> default
|
||||
// File/attachment/page not found -> ignored
|
||||
|
||||
StringBuilder buffer = new StringBuilder(raw);
|
||||
|
||||
KeyValuePair<int, string> block = FindAndRemoveFirstOccurrence(buffer);
|
||||
|
||||
while(block.Key != -1) {
|
||||
string blockHash = "DownCount-" + block.Value.ToString();
|
||||
|
||||
string result = null;
|
||||
|
||||
if(System.Web.HttpContext.Current != null) {
|
||||
result = System.Web.HttpContext.Current.Cache[blockHash] as string;
|
||||
}
|
||||
|
||||
if(result == null) {
|
||||
XmlDocument doc = new XmlDocument();
|
||||
doc.LoadXml(block.Value);
|
||||
|
||||
string pattern;
|
||||
DateTime startDate;
|
||||
GetRootAttributes(doc, out pattern, out startDate);
|
||||
|
||||
double downloads = CountAllDownloads(doc);
|
||||
|
||||
double timeSpanInDays = (DateTime.Now - startDate).TotalDays;
|
||||
|
||||
int dailyDownloads = (int)Math.Round(downloads / timeSpanInDays);
|
||||
int weeklyDownloads = (int)Math.Round(downloads / (timeSpanInDays / 7D));
|
||||
int monthlyDownloads = (int)Math.Round(downloads / (timeSpanInDays / 30D));
|
||||
|
||||
result = BuildResult(pattern, (int)downloads, dailyDownloads, weeklyDownloads, monthlyDownloads);
|
||||
|
||||
if(System.Web.HttpContext.Current != null) {
|
||||
System.Web.HttpContext.Current.Cache.Add(blockHash, result, null, DateTime.Now.AddMinutes(10),
|
||||
System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.Insert(block.Key, result);
|
||||
|
||||
block = FindAndRemoveFirstOccurrence(buffer);
|
||||
}
|
||||
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the result.
|
||||
/// </summary>
|
||||
/// <param name="pattern">The result pattern.</param>
|
||||
/// <param name="downloads">The downloads.</param>
|
||||
/// <param name="daily">The daily downloads.</param>
|
||||
/// <param name="weekly">The weekly downloads.</param>
|
||||
/// <param name="monthly">The monthly downloads.</param>
|
||||
/// <returns>The result.</returns>
|
||||
private static string BuildResult(string pattern, int downloads, int daily, int weekly, int monthly) {
|
||||
StringBuilder buffer = new StringBuilder(pattern);
|
||||
|
||||
ReplacePlaceholder(buffer, CountPlaceholder, downloads.ToString());
|
||||
ReplacePlaceholder(buffer, DailyPlaceholder, daily.ToString());
|
||||
ReplacePlaceholder(buffer, WeeklyPlaceholder, weekly.ToString());
|
||||
ReplacePlaceholder(buffer, MonthlyPlaceholder, monthly.ToString());
|
||||
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces a placeholder with its value.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <param name="placeholder">The placeholder.</param>
|
||||
/// <param name="value">The value.</param>
|
||||
private static void ReplacePlaceholder(StringBuilder buffer, string placeholder, string value) {
|
||||
int index = -1;
|
||||
|
||||
do {
|
||||
index = buffer.ToString().ToLowerInvariant().IndexOf(placeholder);
|
||||
if(index != -1) {
|
||||
buffer.Remove(index, placeholder.Length);
|
||||
buffer.Insert(index, value);
|
||||
}
|
||||
} while(index != -1);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root attributes.
|
||||
/// </summary>
|
||||
/// <param name="doc">The XML document.</param>
|
||||
/// <param name="pattern">The pattern.</param>
|
||||
/// <param name="startDate">The start date/time.</param>
|
||||
private static void GetRootAttributes(XmlDocument doc, out string pattern, out DateTime startDate) {
|
||||
XmlNodeList root = doc.GetElementsByTagName("countDownloads");
|
||||
|
||||
pattern = TryGetAttribute(root[0], "pattern");
|
||||
string startDateTemp = TryGetAttribute(root[0], "startDate");
|
||||
|
||||
if(!DateTime.TryParseExact(startDateTemp, "yyyy'/'MM'/'dd", null, System.Globalization.DateTimeStyles.AssumeLocal, out startDate)) {
|
||||
startDate = DefaultStartDate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the value of an attribute.
|
||||
/// </summary>
|
||||
/// <param name="node">The node.</param>
|
||||
/// <param name="attribute">The name of the attribute.</param>
|
||||
/// <returns>The value of the attribute or <c>null</c> if no value is available.</returns>
|
||||
private static string TryGetAttribute(XmlNode node, string attribute) {
|
||||
XmlAttribute attr = node.Attributes[attribute];
|
||||
if(attr != null) return attr.Value;
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counts all the downloads.
|
||||
/// </summary>
|
||||
/// <param name="doc">The XML document.</param>
|
||||
/// <returns>The download count.</returns>
|
||||
private int CountAllDownloads(XmlDocument doc) {
|
||||
XmlNodeList files = doc.GetElementsByTagName("file");
|
||||
XmlNodeList attachments = doc.GetElementsByTagName("attachment");
|
||||
|
||||
int count = 0;
|
||||
|
||||
foreach(XmlNode node in files) {
|
||||
string name = TryGetAttribute(node, "name");
|
||||
string provider = TryGetAttribute(node, "provider");
|
||||
|
||||
count += CountDownloads(name, provider);
|
||||
}
|
||||
|
||||
foreach(XmlNode node in attachments) {
|
||||
string name = TryGetAttribute(node, "name");
|
||||
string page = TryGetAttribute(node, "page");
|
||||
string provider = TryGetAttribute(node, "provider");
|
||||
|
||||
count += CountDownloads(name, page, provider);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counts the downloads of a file.
|
||||
/// </summary>
|
||||
/// <param name="fullFilePath">The full file path.</param>
|
||||
/// <param name="providerName">The provider or <c>null</c> or <b>string.Empty</b>.</param>
|
||||
/// <returns>The downloads.</returns>
|
||||
private int CountDownloads(string fullFilePath, string providerName) {
|
||||
if(string.IsNullOrEmpty(fullFilePath)) return 0;
|
||||
|
||||
IFilesStorageProviderV30 provider = GetProvider(providerName);
|
||||
if(provider == null) return 0;
|
||||
|
||||
if(!fullFilePath.StartsWith("/")) fullFilePath = "/" + fullFilePath;
|
||||
|
||||
string directory = StDirectoryInfo.GetDirectory(fullFilePath);
|
||||
StFileInfo[] files = _host.ListFiles(new StDirectoryInfo(directory, provider));
|
||||
|
||||
fullFilePath = fullFilePath.ToLowerInvariant();
|
||||
|
||||
foreach(StFileInfo file in files) {
|
||||
if(file.FullName.ToLowerInvariant() == fullFilePath) {
|
||||
return file.RetrievalCount;
|
||||
}
|
||||
}
|
||||
|
||||
LogWarning("File " + provider.GetType().FullName + fullFilePath + " not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Counts the downloads of a file.
|
||||
/// </summary>
|
||||
/// <param name="attachmentName">The name of the attachment.</param>
|
||||
/// <param name="pageName">The full name of the page.</param>
|
||||
/// <param name="providerName">The provider or <c>null</c> or <b>string.Empty</b>.</param>
|
||||
/// <returns>The downloads.</returns>
|
||||
private int CountDownloads(string attachmentName, string pageName, string providerName) {
|
||||
if(string.IsNullOrEmpty(attachmentName)) return 0;
|
||||
if(string.IsNullOrEmpty(pageName)) return 0;
|
||||
|
||||
PageInfo page = _host.FindPage(pageName);
|
||||
if(page == null) {
|
||||
LogWarning("Page " + pageName + " not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
IFilesStorageProviderV30 provider = GetProvider(providerName);
|
||||
if(provider == null) return 0;
|
||||
|
||||
StFileInfo[] attachments = _host.ListPageAttachments(page);
|
||||
|
||||
attachmentName = attachmentName.ToLowerInvariant();
|
||||
|
||||
foreach(StFileInfo attn in attachments) {
|
||||
if(attn.FullName.ToLowerInvariant() == attachmentName) {
|
||||
return attn.RetrievalCount;
|
||||
}
|
||||
}
|
||||
|
||||
LogWarning("Attachment " + provider.GetType().FullName + "(" + pageName + ") " + attachmentName + " not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the specified provider or the default one.
|
||||
/// </summary>
|
||||
/// <param name="provider">The provider.</param>
|
||||
/// <returns></returns>
|
||||
private IFilesStorageProviderV30 GetProvider(string provider) {
|
||||
if(string.IsNullOrEmpty(provider)) provider = _host.GetSettingValue(SettingName.DefaultFilesStorageProvider);
|
||||
provider = provider.ToLowerInvariant();
|
||||
|
||||
IFilesStorageProviderV30[] all = _host.GetFilesStorageProviders(true);
|
||||
foreach(IFilesStorageProviderV30 prov in all) {
|
||||
if(prov.GetType().FullName.ToLowerInvariant() == provider) return prov;
|
||||
}
|
||||
|
||||
LogWarning("Provider " + provider + " not found");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and removes the first occurrence of the XML markup.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <returns>The index-content data.</returns>
|
||||
private static KeyValuePair<int, string> FindAndRemoveFirstOccurrence(StringBuilder buffer) {
|
||||
Match match = XmlRegex.Match(buffer.ToString());
|
||||
|
||||
if(match.Success) {
|
||||
buffer.Remove(match.Index, match.Length);
|
||||
|
||||
return new KeyValuePair<int, string>(match.Index, match.Value);
|
||||
}
|
||||
|
||||
return new KeyValuePair<int, string>(-1, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
private void LogWarning(string message) {
|
||||
if(_enableLogging) {
|
||||
_host.LogEntry(message, LogEntryType.Warning, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the title of an item for display (always during phase 3).
|
||||
/// </summary>
|
||||
/// <param name="title">The input title.</param>
|
||||
/// <param name="context">The context information.</param>
|
||||
/// <returns>The prepared title (no markup allowed).</returns>
|
||||
public string PrepareTitle(string title, ContextInformation context) {
|
||||
return title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <remarks>If the configuration string is not valid, the methoud should throw a <see cref="InvalidConfigurationException"/>.</remarks>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
this._host = host;
|
||||
this._config = config != null ? config : "";
|
||||
|
||||
if(this._config.ToLowerInvariant() == "nolog") _enableLogging = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return Info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 "Specify <i>nolog</i> for disabling warning log messages for non-existent files or attachments."; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a footnotes plugin.
|
||||
/// </summary>
|
||||
public class Footnotes : IFormatterProviderV30 {
|
||||
|
||||
// Kindly contributed by Jens Felsner
|
||||
private static readonly ComponentInformation info = new ComponentInformation("Footnotes Plugin", "Threeplicate Srl", "3.0.1.471", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/Footnotes.txt");
|
||||
|
||||
private static readonly Regex ReferencesRegex = new Regex("(<[ ]*references[ ]*/[ ]*>|<[ ]*references[ ]*>.*?<[ ]*/[ ]*references[ ]*>)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex RefRegex = new Regex("<[ ]*ref[ ]*>.*?<[ ]*/[ ]*ref[ ]*>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
private static readonly Regex RefRemovalRegex = new Regex("(<[ ]*ref[ ]*>|<[ ]*/[ ]*ref[ ]*>)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
||||
|
||||
private IHostV30 host = null;
|
||||
private string config = "";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="host"/> or <paramref name="config"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="InvalidConfigurationException">If <paramref name="config"/> is not valid or is incorrect.</exception>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
this.host = host;
|
||||
this.config = config != null ? config : "";
|
||||
}
|
||||
|
||||
// Replaces the first occurence of 'find' in 'input' with 'replace'
|
||||
private static string ReplaceFirst(string input, string find, string replace) {
|
||||
return input.Substring(0, input.IndexOf(find)) + replace + input.Substring(input.IndexOf(find) + find.Length);
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
// Match all <ref>*</ref>
|
||||
MatchCollection mc = RefRegex.Matches(raw);
|
||||
|
||||
// No ref-tag found, nothing to do
|
||||
if(mc.Count == 0) return raw;
|
||||
|
||||
// No references tag
|
||||
if(ReferencesRegex.Matches(raw).Count == 0) {
|
||||
return raw + "<br/><span style=\"color: #FF0000;\">Reference Error! Missing element <references/></span>";
|
||||
}
|
||||
|
||||
string output = raw;
|
||||
string ref_string = "<table class=\"footnotes\">";
|
||||
|
||||
int footnoteCounter = 0;
|
||||
|
||||
// For each <ref>...</ref> replace it with Footnote, append it to ref-section
|
||||
foreach(Match m in mc) {
|
||||
footnoteCounter++;
|
||||
output = ReplaceFirst(output, m.Value, "<a id=\"refnote" + footnoteCounter.ToString() + "\" href=\"#footnote" + footnoteCounter.ToString() + "\"><sup>" + footnoteCounter.ToString() + "</sup></a>");
|
||||
|
||||
ref_string += "<tr><td><a id=\"footnote" + footnoteCounter.ToString() + "\" href=\"#refnote" + footnoteCounter.ToString() + "\"><sup>" + footnoteCounter.ToString() + "</sup></a></td><td>" + RefRemovalRegex.Replace(m.Value, "") + "</td></tr>";
|
||||
}
|
||||
ref_string += "</table>";
|
||||
|
||||
// Replace <reference/> with ref-section
|
||||
output = ReferencesRegex.Replace(output, ref_string);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
#region IFormatterProviderV30 Member
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 1.
|
||||
/// </summary>
|
||||
public bool PerformPhase1 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 2.
|
||||
/// </summary>
|
||||
public bool PerformPhase2 {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 3.
|
||||
/// </summary>
|
||||
public bool PerformPhase3 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution priority of the provider (0 lowest, 100 highest).
|
||||
/// </summary>
|
||||
public int ExecutionPriority {
|
||||
get { return 50; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the title of an item for display (always during phase 3).
|
||||
/// </summary>
|
||||
/// <param name="title">The input title.</param>
|
||||
/// <param name="context">The context information.</param>
|
||||
/// <returns>The prepared title (no markup allowed).</returns>
|
||||
public string PrepareTitle(string title, ContextInformation context) {
|
||||
return title;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IProviderV30 Member
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 null; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
|
@ -1,147 +0,0 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a Formatter Provider that allows to write multi-language content in Wiki Pages.
|
||||
/// </summary>
|
||||
public class MultilanguageContentPlugin : IFormatterProviderV30 {
|
||||
|
||||
private IHostV30 host;
|
||||
private string config;
|
||||
private ComponentInformation info = new ComponentInformation("Multilanguage Content Plugin", "Threeplicate Srl", "3.0.1.471", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/Multilanguage.txt");
|
||||
|
||||
private string defaultLanguage = "en-us";
|
||||
private bool displayWarning = false;
|
||||
|
||||
private const string DivStyle = "padding: 2px; margin-bottom: 10px; font-size: 11px; background-color: #FFFFC4; border: solid 1px #DDDDDD;";
|
||||
private const string StandardMessage = @"<div style=""" + DivStyle + @""">The content of this Page is localized in your language. To change the language settings, please go to the <a href=""Language.aspx"">Language Selection</a> page.</div>";
|
||||
private const string NotLocalizedMessage = @"<div style=""" + DivStyle + @""">The content of this Page is <b>not</b> available in your language, and it is displayed in the default language of the Wiki. To change the language settings, please go to the <a href=""Language.aspx"">Language Selection</a> page.</div>";
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 1.
|
||||
/// </summary>
|
||||
public bool PerformPhase1 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 2.
|
||||
/// </summary>
|
||||
public bool PerformPhase2 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 3.
|
||||
/// </summary>
|
||||
public bool PerformPhase3 {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
string result = ExtractLocalizedContent(context.Language, raw); // Try to load localized content
|
||||
bool notLocalized = false;
|
||||
bool noLocalization = false;
|
||||
if(result == null) {
|
||||
result = ExtractLocalizedContent(defaultLanguage, raw); // Load content in the default language
|
||||
notLocalized = true;
|
||||
noLocalization = false;
|
||||
}
|
||||
if(result == null) {
|
||||
result = raw; // The Page is not localized, return all the content
|
||||
notLocalized = false;
|
||||
noLocalization = true;
|
||||
}
|
||||
if(displayWarning && !noLocalization && context.Page != null) {
|
||||
if(notLocalized) return NotLocalizedMessage + result;
|
||||
else return StandardMessage + result;
|
||||
}
|
||||
else return result;
|
||||
}
|
||||
|
||||
private string ExtractLocalizedContent(string language, string content) {
|
||||
string head = "\\<" + language + "\\>", tail = "\\<\\/" + language + "\\>";
|
||||
Regex regex = new Regex(head + "(.+?)" + tail, RegexOptions.IgnoreCase | RegexOptions.Singleline);
|
||||
Match match = regex.Match(content);
|
||||
StringBuilder sb = new StringBuilder(1000);
|
||||
while(match.Success) {
|
||||
sb.Append(match.Groups[1].Value);
|
||||
match = regex.Match(content, match.Index + match.Length);
|
||||
}
|
||||
if(sb.Length > 0) return sb.ToString();
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <remarks>If the configuration string is not valid, the methoud should throw a <see cref="InvalidConfigurationException"/>.</remarks>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
this.host = host;
|
||||
this.config = config != null ? config : "";
|
||||
defaultLanguage = host.GetSettingValue(SettingName.DefaultLanguage);
|
||||
displayWarning = config.ToLowerInvariant().Equals("display warning");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() { }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution priority of the provider (0 lowest, 100 highest).
|
||||
/// </summary>
|
||||
public int ExecutionPriority {
|
||||
get { return 50; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the title of an item for display (always during phase 3).
|
||||
/// </summary>
|
||||
/// <param name="title">The input title.</param>
|
||||
/// <param name="context">The context information.</param>
|
||||
/// <returns>The prepared title (no markup allowed).</returns>
|
||||
public string PrepareTitle(string title, ContextInformation context) {
|
||||
string result = ExtractLocalizedContent(context.Language, title); // Try to load localized content
|
||||
if(context.ForIndexing || result == null) {
|
||||
result = ExtractLocalizedContent(defaultLanguage, title); // Load content in the default language
|
||||
}
|
||||
if(result == null) {
|
||||
result = title; // The Page is not localized, return all the content
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 "Specify 'display warning' to notify the user of the available content languages."; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,798 +0,0 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using ScrewTurn.Wiki.SearchEngine;
|
||||
|
||||
namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a sandbox plugin for pages.
|
||||
/// </summary>
|
||||
public class PagesSandbox {
|
||||
|
||||
private IHostV30 host;
|
||||
private string config;
|
||||
private static readonly ComponentInformation info = new ComponentInformation("Pages Sandbox", "Threeplicate Srl", "3.0.1.471", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/PagesSandbox.txt");
|
||||
|
||||
private List<NamespaceInfo> allNamespaces = new List<NamespaceInfo>(5);
|
||||
|
||||
private List<PageInfo> allPages;
|
||||
private Dictionary<PageInfo, PageContent> allContents;
|
||||
private Dictionary<PageContent, List<PageContent>> allBackups;
|
||||
private Dictionary<PageInfo, PageContent> allDrafts;
|
||||
|
||||
private List<CategoryInfo> allCategories;
|
||||
|
||||
private uint freeDocumentId = 1;
|
||||
private uint freeWordId = 1;
|
||||
private IInMemoryIndex index;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a namespace.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the namespace (cannot be <c>null</c> or empty).</param>
|
||||
/// <returns>The <see cref="T:NamespaceInfo"/>, or <c>null</c> if no namespace is found.</returns>
|
||||
public NamespaceInfo GetNamespace(string name) {
|
||||
if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty");
|
||||
|
||||
return allNamespaces.Find(n => n.Name == name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the sub-namespaces.
|
||||
/// </summary>
|
||||
/// <returns>The sub-namespaces, sorted by name.</returns>
|
||||
public NamespaceInfo[] GetNamespaces() {
|
||||
lock(this) {
|
||||
return allNamespaces.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new namespace.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the namespace.</param>
|
||||
/// <returns>The correct <see cref="T:NamespaceInfo"/> object.</returns>
|
||||
public NamespaceInfo AddNamespace(string name) {
|
||||
throw new NotImplementedException();
|
||||
/*if(name == null) throw new ArgumentNullException("name");
|
||||
if(name.Length == 0) throw new ArgumentException("Name cannot be empty", "name");
|
||||
|
||||
lock(this) {
|
||||
if(GetNamespace(name) != null) return null;
|
||||
|
||||
// This does not compile unless PagesSandbox implements IPagesStorageProviderV30
|
||||
NamespaceInfo newSpace = new NamespaceInfo(name, this, null);
|
||||
|
||||
allNamespaces.Add(newSpace);
|
||||
|
||||
return newSpace;
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace to rename.</param>
|
||||
/// <param name="newName">The new name of the namespace.</param>
|
||||
/// <returns>The correct <see cref="T:NamespaceInfo"/> object.</returns>
|
||||
public NamespaceInfo RenameNamespace(NamespaceInfo nspace, string newName) {
|
||||
if(nspace == null) throw new ArgumentNullException("nspace");
|
||||
if(newName == null) throw new ArgumentNullException("newName");
|
||||
if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");
|
||||
|
||||
lock(this) {
|
||||
if(GetNamespace(newName) != null) return null;
|
||||
|
||||
nspace.Name = newName;
|
||||
|
||||
return nspace;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the default page of a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace of which to set the default page.</param>
|
||||
/// <param name="page">The page to use as default page, or <c>null</c>.</param>
|
||||
/// <returns>The correct <see cref="T:NamespaceInfo"/> object.</returns>
|
||||
public NamespaceInfo SetNamespaceDefaultPage(NamespaceInfo nspace, PageInfo page) {
|
||||
if(nspace == null) throw new ArgumentNullException("nspace");
|
||||
|
||||
lock(this) {
|
||||
nspace.DefaultPage = page;
|
||||
|
||||
return nspace;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace to remove.</param>
|
||||
/// <returns><c>true</c> if the namespace is removed, <c>false</c> otherwise.</returns>
|
||||
public bool RemoveNamespace(NamespaceInfo nspace) {
|
||||
if(nspace == null) throw new ArgumentNullException("nspace");
|
||||
|
||||
lock(this) {
|
||||
return allNamespaces.Remove(nspace);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves a page from its namespace into another.
|
||||
/// </summary>
|
||||
/// <param name="page">The page to move.</param>
|
||||
/// <param name="destination">The destination namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="copyCategories">A value indicating whether to copy the page categories in the destination
|
||||
/// namespace, if not already available.</param>
|
||||
/// <returns>The correct instance of <see cref="T:PageInfo"/>.</returns>
|
||||
public PageInfo MovePage(PageInfo page, NamespaceInfo destination, bool copyCategories) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a category.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the category.</param>
|
||||
/// <returns>The <see cref="T:CategoryInfo"/>, or <c>null</c> if no category is found.</returns>
|
||||
public CategoryInfo GetCategory(string fullName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the Categories in a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace.</param>
|
||||
/// <returns>All the Categories in the namespace, sorted by name.</returns>
|
||||
public CategoryInfo[] GetCategories(NamespaceInfo nspace) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the categories of a page.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns>The categories, sorted by name.</returns>
|
||||
public CategoryInfo[] GetCategoriesForPage(PageInfo page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Category.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The target namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="name">The Category name.</param>
|
||||
/// <returns>The correct CategoryInfo object.</returns>
|
||||
/// <remarks>The moethod should set category's Pages to an empty array.</remarks>
|
||||
public CategoryInfo AddCategory(string nspace, string name) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a Category.
|
||||
/// </summary>
|
||||
/// <param name="category">The Category to rename.</param>
|
||||
/// <param name="newName">The new Name.</param>
|
||||
/// <returns>The correct CategoryInfo object.</returns>
|
||||
public CategoryInfo RenameCategory(CategoryInfo category, string newName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Category.
|
||||
/// </summary>
|
||||
/// <param name="category">The Category to remove.</param>
|
||||
/// <returns>True if the Category has been removed successfully.</returns>
|
||||
public bool RemoveCategory(CategoryInfo category) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merges two Categories.
|
||||
/// </summary>
|
||||
/// <param name="source">The source Category.</param>
|
||||
/// <param name="destination">The destination Category.</param>
|
||||
/// <returns>The correct <see cref="T:CategoryInfo"/> object.</returns>
|
||||
/// <remarks>The <b>destination</b> Category remains, while the <b>source</b> Category is deleted, and all its Pages re-bound
|
||||
/// in the <b>destination</b> Category.</remarks>
|
||||
public CategoryInfo MergeCategories(CategoryInfo source, CategoryInfo destination) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs a search in the index.
|
||||
/// </summary>
|
||||
/// <param name="parameters">The search parameters.</param>
|
||||
/// <returns>The results.</returns>
|
||||
public SearchResultCollection PerformSearch(SearchParameters parameters) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds the search index.
|
||||
/// </summary>
|
||||
public void RebuildIndex() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets some statistics about the search engine index.
|
||||
/// </summary>
|
||||
/// <param name="documentCount">The total number of documents.</param>
|
||||
/// <param name="wordCount">The total number of unique words.</param>
|
||||
/// <param name="occurrenceCount">The total number of word-document occurrences.</param>
|
||||
/// <param name="size">The approximated size, in bytes, of the search engine index.</param>
|
||||
public void GetIndexStats(out int documentCount, out int wordCount, out int occurrenceCount, out long size) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the search engine index is corrupted and needs to be rebuilt.
|
||||
/// </summary>
|
||||
public bool IsIndexCorrupted {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a page.
|
||||
/// </summary>
|
||||
/// <param name="fullName">The full name of the page.</param>
|
||||
/// <returns>The <see cref="T:PageInfo"/>, or <c>null</c> if no page is found.</returns>
|
||||
public PageInfo GetPage(string fullName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the Pages in a namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>All the Pages in the namespace, sorted by name.</returns>
|
||||
public PageInfo[] GetPages(NamespaceInfo nspace) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the pages in a namespace that are bound to zero categories.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The namespace (<c>null</c> for the root).</param>
|
||||
/// <returns>The pages, sorted by name.</returns>
|
||||
public PageInfo[] GetUncategorizedPages(NamespaceInfo nspace) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Content of a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <returns>The Page Content object.</returns>
|
||||
public PageContent GetContent(PageInfo page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the content of a draft of a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <returns>The draft, or <c>null</c> if no draft exists.</returns>
|
||||
public PageContent GetDraft(PageInfo page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes a draft of a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <returns><c>true</c> if the draft is deleted, <c>false</c> otherwise.</returns>
|
||||
public bool DeleteDraft(PageInfo page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Backup/Revision numbers of a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to get the Backups of.</param>
|
||||
/// <returns>The Backup/Revision numbers.</returns>
|
||||
public int[] GetBackups(PageInfo page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Content of a Backup of a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to get the backup of.</param>
|
||||
/// <param name="revision">The Backup/Revision number.</param>
|
||||
/// <returns>The Page Backup.</returns>
|
||||
public PageContent GetBackupContent(PageInfo page, int revision) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces to overwrite or create a Backup.
|
||||
/// </summary>
|
||||
/// <param name="content">The Backup content.</param>
|
||||
/// <param name="revision">The revision.</param>
|
||||
/// <returns>True if the Backup has been created successfully.</returns>
|
||||
public bool SetBackupContent(PageContent content, int revision) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a Page.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The target namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="name">The Page Name.</param>
|
||||
/// <param name="creationDateTime">The creation Date/Time.</param>
|
||||
/// <returns>The correct PageInfo object or null.</returns>
|
||||
/// <remarks>This method should <b>not</b> create the content of the Page.</remarks>
|
||||
public PageInfo AddPage(string nspace, string name, DateTime creationDateTime) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renames a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to rename.</param>
|
||||
/// <param name="newName">The new Name.</param>
|
||||
/// <returns>The correct <see cref="T:PageInfo"/> object.</returns>
|
||||
public PageInfo RenamePage(PageInfo page, string newName) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies the Content of a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <param name="title">The Title of the Page.</param>
|
||||
/// <param name="username">The Username.</param>
|
||||
/// <param name="dateTime">The Date/Time.</param>
|
||||
/// <param name="comment">The Comment of the editor, about this revision.</param>
|
||||
/// <param name="content">The Page Content.</param>
|
||||
/// <param name="keywords">The keywords, usually used for SEO.</param>
|
||||
/// <param name="description">The description, usually used for SEO.</param>
|
||||
/// <param name="saveMode">The save mode for this modification.</param>
|
||||
/// <returns><c>true</c> if the Page has been modified successfully, <c>false</c> otherwise.</returns>
|
||||
/// <remarks>If <b>saveMode</b> equals <b>Draft</b> and a draft already exists, it is overwritten.</remarks>
|
||||
public bool ModifyPage(PageInfo page, string title, string username, DateTime dateTime, string comment, string content, string[] keywords, string description, SaveMode saveMode) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the rollback of a Page to a specified revision.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to rollback.</param>
|
||||
/// <param name="revision">The Revision to rollback the Page to.</param>
|
||||
/// <returns><c>true</c> if the rollback succeeded, <c>false</c> otherwise.</returns>
|
||||
public bool RollbackPage(PageInfo page, int revision) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes the Backups of a Page, up to a specified revision.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to delete the backups of.</param>
|
||||
/// <param name="revision">The newest revision to delete (newer revision are kept) or -1 to delete all the Backups.</param>
|
||||
/// <returns><c>true</c> if the deletion succeeded, <c>false</c> otherwise.</returns>
|
||||
public bool DeleteBackups(PageInfo page, int revision) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to remove.</param>
|
||||
/// <returns>True if the Page is removed successfully.</returns>
|
||||
public bool RemovePage(PageInfo page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Binds a Page with one or more Categories.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page to bind.</param>
|
||||
/// <param name="categories">The Categories to bind the Page with.</param>
|
||||
/// <returns>True if the binding succeeded.</returns>
|
||||
/// <remarks>After a successful operation, the Page is bound with all and only the categories passed as argument.</remarks>
|
||||
public bool RebindPage(PageInfo page, string[] categories) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Page Messages.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <returns>The list of the <b>first-level</b> Messages, containing the replies properly nested, sorted by date/time.</returns>
|
||||
public Message[] GetMessages(PageInfo page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the total number of Messages in a Page Discussion.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <returns>The number of messages.</returns>
|
||||
public int GetMessageCount(PageInfo page) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all messages for a page and stores the new messages.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="messages">The new messages to store.</param>
|
||||
/// <returns><c>true</c> if the messages are stored, <c>false</c> otherwise.</returns>
|
||||
public bool BulkStoreMessages(PageInfo page, Message[] messages) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new Message to a Page.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <param name="username">The Username.</param>
|
||||
/// <param name="subject">The Subject.</param>
|
||||
/// <param name="dateTime">The Date/Time.</param>
|
||||
/// <param name="body">The Body.</param>
|
||||
/// <param name="parent">The Parent Message ID, or -1.</param>
|
||||
/// <returns>True if the Message is added successfully.</returns>
|
||||
public bool AddMessage(PageInfo page, string username, string subject, DateTime dateTime, string body, int parent) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Message.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <param name="id">The ID of the Message to remove.</param>
|
||||
/// <param name="removeReplies">A value specifying whether or not to remove the replies.</param>
|
||||
/// <returns>True if the Message is removed successfully.</returns>
|
||||
public bool RemoveMessage(PageInfo page, int id, bool removeReplies) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies a Message.
|
||||
/// </summary>
|
||||
/// <param name="page">The Page.</param>
|
||||
/// <param name="id">The ID of the Message to modify.</param>
|
||||
/// <param name="username">The Username.</param>
|
||||
/// <param name="subject">The Subject.</param>
|
||||
/// <param name="dateTime">The Date/Time.</param>
|
||||
/// <param name="body">The Body.</param>
|
||||
/// <returns>True if the Message is modified successfully.</returns>
|
||||
public bool ModifyMessage(PageInfo page, int id, string username, string subject, DateTime dateTime, string body) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the Navigation Paths in a Namespace.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The Namespace.</param>
|
||||
/// <returns>All the Navigation Paths, sorted by name.</returns>
|
||||
public NavigationPath[] GetNavigationPaths(NamespaceInfo nspace) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new Navigation Path.
|
||||
/// </summary>
|
||||
/// <param name="nspace">The target namespace (<c>null</c> for the root).</param>
|
||||
/// <param name="name">The Name of the Path.</param>
|
||||
/// <param name="pages">The Pages array.</param>
|
||||
/// <returns>The correct <see cref="T:NavigationPath"/> object.</returns>
|
||||
public NavigationPath AddNavigationPath(string nspace, string name, PageInfo[] pages) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies an existing navigation path.
|
||||
/// </summary>
|
||||
/// <param name="path">The navigation path to modify.</param>
|
||||
/// <param name="pages">The new pages array.</param>
|
||||
/// <returns>The correct <see cref="T:NavigationPath"/> object.</returns>
|
||||
public NavigationPath ModifyNavigationPath(NavigationPath path, PageInfo[] pages) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a Navigation Path.
|
||||
/// </summary>
|
||||
/// <param name="path">The navigation path to remove.</param>
|
||||
/// <returns><c>true</c> if the path is removed, <c>false</c> otherwise.</returns>
|
||||
public bool RemoveNavigationPath(NavigationPath path) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the snippets.
|
||||
/// </summary>
|
||||
/// <returns>All the snippets, sorted by name.</returns>
|
||||
public Snippet[] GetSnippets() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new snippet.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the snippet.</param>
|
||||
/// <param name="content">The content of the snippet.</param>
|
||||
/// <returns>The correct <see cref="T:Snippet"/> object.</returns>
|
||||
public Snippet AddSnippet(string name, string content) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies an existing snippet.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the snippet to modify.</param>
|
||||
/// <param name="content">The content of the snippet.</param>
|
||||
/// <returns>The correct <see cref="T:Snippet"/> object.</returns>
|
||||
public Snippet ModifySnippet(string name, string content) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a new Snippet.
|
||||
/// </summary>
|
||||
/// <param name="name">The Name of the Snippet to remove.</param>
|
||||
/// <returns><c>true</c> if the snippet is removed, <c>false</c> otherwise.</returns>
|
||||
public bool RemoveSnippet(string name) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the content templates.
|
||||
/// </summary>
|
||||
/// <returns>All the content templates, sorted by name.</returns>
|
||||
public ContentTemplate[] GetContentTemplates() {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new content template.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of template.</param>
|
||||
/// <param name="content">The content of the template.</param>
|
||||
/// <returns>The correct <see cref="T:ContentTemplate"/> object.</returns>
|
||||
public ContentTemplate AddContentTemplate(string name, string content) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Modifies an existing content template.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the template to modify.</param>
|
||||
/// <param name="content">The content of the template.</param>
|
||||
/// <returns>The correct <see cref="T:ContentTemplate"/> object.</returns>
|
||||
public ContentTemplate ModifyContentTemplate(string name, string content) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a content template.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the template to remove.</param>
|
||||
/// <returns><c>true</c> if the template is removed, <c>false</c> otherwise.</returns>
|
||||
public bool RemoveContentTemplate(string name) {
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value specifying whether the provider is read-only, i.e. it can only provide data and not store it.
|
||||
/// </summary>
|
||||
public bool ReadOnly {
|
||||
get { throw new NotImplementedException(); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <remarks>If the configuration string is not valid, the methoud should throw a <see cref="InvalidConfigurationException"/>.</remarks>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
if(host == null) throw new ArgumentNullException("host");
|
||||
if(config == null) throw new ArgumentNullException("config");
|
||||
|
||||
this.host = host;
|
||||
this.config = config;
|
||||
|
||||
allPages = new List<PageInfo>(50);
|
||||
allContents = new Dictionary<PageInfo, PageContent>(50);
|
||||
allBackups = new Dictionary<PageContent, List<PageContent>>(50);
|
||||
allDrafts = new Dictionary<PageInfo, PageContent>(5);
|
||||
|
||||
allCategories = new List<CategoryInfo>(10);
|
||||
|
||||
// Prepare search index
|
||||
index = new StandardIndex();
|
||||
index.SetBuildDocumentDelegate(BuildDocumentHandler);
|
||||
index.IndexChanged += index_IndexChanged;
|
||||
}
|
||||
|
||||
private void index_IndexChanged(object sender, IndexChangedEventArgs e) {
|
||||
lock(this) {
|
||||
if(e.Change == IndexChangeType.DocumentAdded) {
|
||||
List<WordId> newWords = new List<WordId>(e.ChangeData.Words.Count);
|
||||
foreach(DumpedWord w in e.ChangeData.Words) {
|
||||
newWords.Add(new WordId(w.Text, freeWordId));
|
||||
freeWordId++;
|
||||
}
|
||||
|
||||
e.Result = new IndexStorerResult(freeDocumentId, newWords);
|
||||
freeDocumentId++;
|
||||
}
|
||||
else e.Result = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles the construction of an <see cref="T:IDocument" /> for the search engine.
|
||||
/// </summary>
|
||||
/// <param name="dumpedDocument">The input dumped document.</param>
|
||||
/// <returns>The resulting <see cref="T:IDocument" />.</returns>
|
||||
private IDocument BuildDocumentHandler(DumpedDocument dumpedDocument) {
|
||||
if(dumpedDocument.TypeTag == PageDocument.StandardTypeTag) {
|
||||
string pageName = PageDocument.GetPageName(dumpedDocument.Name);
|
||||
|
||||
PageInfo page = GetPage(pageName);
|
||||
|
||||
if(page == null) return null;
|
||||
else return new PageDocument(page, dumpedDocument, TokenizeContent);
|
||||
}
|
||||
else if(dumpedDocument.TypeTag == MessageDocument.StandardTypeTag) {
|
||||
string pageFullName;
|
||||
int id;
|
||||
MessageDocument.GetMessageDetails(dumpedDocument.Name, out pageFullName, out id);
|
||||
|
||||
PageInfo page = GetPage(pageFullName);
|
||||
if(page == null) return null;
|
||||
else return new MessageDocument(page, id, dumpedDocument, TokenizeContent);
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tokenizes page content.
|
||||
/// </summary>
|
||||
/// <param name="content">The content to tokenize.</param>
|
||||
/// <returns>The tokenized words.</returns>
|
||||
private static WordInfo[] TokenizeContent(string content) {
|
||||
WordInfo[] words = SearchEngine.Tools.Tokenize(content);
|
||||
return words;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indexes a page.
|
||||
/// </summary>
|
||||
/// <param name="content">The content of the page.</param>
|
||||
/// <returns>The number of indexed words, including duplicates.</returns>
|
||||
private int IndexPage(PageContent content) {
|
||||
lock(this) {
|
||||
string documentName = PageDocument.GetDocumentName(content.PageInfo);
|
||||
|
||||
DumpedDocument ddoc = new DumpedDocument(0, documentName, host.PrepareTitleForIndexing(content.PageInfo, content.Title),
|
||||
PageDocument.StandardTypeTag, content.LastModified);
|
||||
|
||||
// Store the document
|
||||
// The content should always be prepared using IHost.PrepareForSearchEngineIndexing()
|
||||
return index.StoreDocument(new PageDocument(content.PageInfo, ddoc, TokenizeContent),
|
||||
content.Keywords, host.PrepareContentForIndexing(content.PageInfo, content.Content), null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a page from the search engine index.
|
||||
/// </summary>
|
||||
/// <param name="content">The content of the page to remove.</param>
|
||||
private void UnindexPage(PageContent content) {
|
||||
lock(this) {
|
||||
string documentName = PageDocument.GetDocumentName(content.PageInfo);
|
||||
|
||||
DumpedDocument ddoc = new DumpedDocument(0, documentName, host.PrepareTitleForIndexing(content.PageInfo, content.Title),
|
||||
PageDocument.StandardTypeTag, content.LastModified);
|
||||
index.RemoveDocument(new PageDocument(content.PageInfo, ddoc, TokenizeContent), null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indexes a message.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="id">The message ID.</param>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="dateTime">The date/time.</param>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <returns>The number of indexed words, including duplicates.</returns>
|
||||
private int IndexMessage(PageInfo page, int id, string subject, DateTime dateTime, string body) {
|
||||
lock(this) {
|
||||
// Trim "RE:" to avoid polluting the search engine index
|
||||
if(subject.ToLowerInvariant().StartsWith("re:") && subject.Length > 3) subject = subject.Substring(3).Trim();
|
||||
|
||||
string documentName = MessageDocument.GetDocumentName(page, id);
|
||||
|
||||
DumpedDocument ddoc = new DumpedDocument(0, documentName, host.PrepareTitleForIndexing(null, subject),
|
||||
MessageDocument.StandardTypeTag, dateTime);
|
||||
|
||||
// Store the document
|
||||
// The content should always be prepared using IHost.PrepareForSearchEngineIndexing()
|
||||
return index.StoreDocument(new MessageDocument(page, id, ddoc, TokenizeContent), null,
|
||||
host.PrepareContentForIndexing(null, body), null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indexes a message tree.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="root">The tree root.</param>
|
||||
private void IndexMessageTree(PageInfo page, Message root) {
|
||||
IndexMessage(page, root.ID, root.Subject, root.DateTime, root.Body);
|
||||
foreach(Message reply in root.Replies) {
|
||||
IndexMessageTree(page, reply);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a message from the search engine index.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="id">The message ID.</param>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="dateTime">The date/time.</param>
|
||||
/// <param name="body">The body.</param>
|
||||
/// <returns>The number of indexed words, including duplicates.</returns>
|
||||
private void UnindexMessage(PageInfo page, int id, string subject, DateTime dateTime, string body) {
|
||||
lock(this) {
|
||||
// Trim "RE:" to avoid polluting the search engine index
|
||||
if(subject.ToLowerInvariant().StartsWith("re:") && subject.Length > 3) subject = subject.Substring(3).Trim();
|
||||
|
||||
string documentName = MessageDocument.GetDocumentName(page, id);
|
||||
|
||||
DumpedDocument ddoc = new DumpedDocument(0, documentName, host.PrepareTitleForIndexing(null, subject),
|
||||
MessageDocument.StandardTypeTag, DateTime.Now);
|
||||
index.RemoveDocument(new MessageDocument(page, id, ddoc, TokenizeContent), null);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a message tree from the search engine index.
|
||||
/// </summary>
|
||||
/// <param name="page">The page.</param>
|
||||
/// <param name="root">The tree root.</param>
|
||||
private void UnindexMessageTree(PageInfo page, Message root) {
|
||||
UnindexMessage(page, root.ID, root.Subject, root.DateTime, root.Body);
|
||||
foreach(Message reply in root.Replies) {
|
||||
UnindexMessageTree(page, reply);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() {
|
||||
// Nothing do to
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 null; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -68,13 +68,8 @@
|
|||
<Compile Include="..\AssemblyVersion.cs">
|
||||
<Link>AssemblyVersion.cs</Link>
|
||||
</Compile>
|
||||
<Compile Include="Footnotes.cs" />
|
||||
<Compile Include="RssFeedDisplay.cs" />
|
||||
<Compile Include="DownloadCounter.cs" />
|
||||
<Compile Include="MultilanguageContentPlugin.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="PagesSandbox.cs" />
|
||||
<Compile Include="UnfuddleTickets.cs" />
|
||||
<Compile Include="Updater.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\PluginFramework\PluginFramework.csproj">
|
||||
|
@ -103,9 +98,6 @@
|
|||
<Install>false</Install>
|
||||
</BootstrapperPackage>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Resources\UnfuddleTickets.xsl" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
|
|
@ -1,101 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
|
||||
<xsl:output method="html"/>
|
||||
<xsl:template match="/">
|
||||
<div id="UnfuddleTicketsDiv">
|
||||
<!-- Highest -->
|
||||
<xsl:if test="count(/root/ticket-report/groups/group/title[text() = 'Highest']/parent::group/tickets) > 0">
|
||||
<h2 class="separator">Priority: Highest</h2>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates select="/root/ticket-report/groups/group/title[text() = 'Highest']/parent::group/tickets"/>
|
||||
<!-- High -->
|
||||
<xsl:if test="count(/root/ticket-report/groups/group/title[text() = 'High']/parent::group/tickets) > 0">
|
||||
<h2 class="separator">Priority: High</h2>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates select="/root/ticket-report/groups/group/title[text() = 'High']/parent::group/tickets"/>
|
||||
<!-- Normal -->
|
||||
<xsl:if test="count(/root/ticket-report/groups/group/title[text() = 'Normal']/parent::group/tickets) > 0">
|
||||
<h2 class="separator">Priority: Normal</h2>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates select="/root/ticket-report/groups/group/title[text() = 'Normal']/parent::group/tickets"/>
|
||||
<!-- Low -->
|
||||
<xsl:if test="count(/root/ticket-report/groups/group/title[text() = 'Low']/parent::group/tickets) > 0">
|
||||
<h2 class="separator">Priority: Low</h2>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates select="/root/ticket-report/groups/group/title[text() = 'Low']/parent::group/tickets"/>
|
||||
<!-- Lowest -->
|
||||
<xsl:if test="count(/root/ticket-report/groups/group/title[text() = 'Lowest']/parent::group/tickets) > 0">
|
||||
<h2 class="separator">Priority: Lowest</h2>
|
||||
</xsl:if>
|
||||
<xsl:apply-templates select="/root/ticket-report/groups/group/title[text() = 'Lowest']/parent::group/tickets"/>
|
||||
</div>
|
||||
</xsl:template>
|
||||
<xsl:template match="/root/ticket-report/groups/group/tickets" priority="5">
|
||||
<table style="width: 100%;" cellspacing="0" cellpadding="0">
|
||||
<thead>
|
||||
<tr class="tableheader">
|
||||
<th style="white-space: nowrap; text-align: left; width: 50px;">
|
||||
#
|
||||
</th>
|
||||
<th style="white-space: nowrap; text-align: left;">
|
||||
Summary
|
||||
</th>
|
||||
<th style="white-space: nowrap; text-align: left; width: 80px;">
|
||||
Status
|
||||
</th>
|
||||
<th style="white-space: nowrap;width: 70px;">
|
||||
Priority
|
||||
</th>
|
||||
<th style="white-space: nowrap; text-align: left;width: 120px;">
|
||||
Milestone
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<xsl:apply-templates select="ticket"/>
|
||||
</tbody>
|
||||
</table>
|
||||
<br />
|
||||
<br />
|
||||
</xsl:template>
|
||||
<xsl:template match="/root/ticket-report/groups/group/tickets/ticket">
|
||||
<tr>
|
||||
<xsl:if test="position() mod 2 != 1">
|
||||
<xsl:attribute name="class">priority_{priority} tablerowalternate</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:if test="position() mod 2 != 0">
|
||||
<xsl:attribute name="class">priority_{priority} tablerow</xsl:attribute>
|
||||
</xsl:if>
|
||||
<xsl:variable name="priority" select="priority"/>
|
||||
<td class='priority_{priority}' style="text-align: left;">
|
||||
<xsl:value-of select="number"/>
|
||||
</td>
|
||||
<td class='priority_{priority}'>
|
||||
<xsl:value-of select="summary" disable-output-escaping="yes"/>
|
||||
</td>
|
||||
<td class='priority_{priority}'>
|
||||
<xsl:variable name="c" select="substring(status,1,1)"/>
|
||||
<xsl:value-of select="translate($c,'abcdefghijklmnopqrst','ABCDEFGHIJKLMNOPQRST')"/>
|
||||
<xsl:value-of select="substring-after(status,$c)"/>
|
||||
</td>
|
||||
<td class='priority_{priority}' style="text-align: center;">
|
||||
<xsl:choose>
|
||||
<xsl:when test="priority = '5'">Highest</xsl:when>
|
||||
<xsl:when test="priority = '4'">High</xsl:when>
|
||||
<xsl:when test="priority = '3'">Normal</xsl:when>
|
||||
<xsl:when test="priority = '2'">Low</xsl:when>
|
||||
<xsl:when test="priority = '1'">Lowest</xsl:when>
|
||||
</xsl:choose>
|
||||
</td>
|
||||
<td class='priority_{priority}'>
|
||||
<xsl:variable name="milestoneID" select="milestone-id"/>
|
||||
<xsl:if test="$milestoneID = ''">
|
||||
None
|
||||
</xsl:if>
|
||||
<xsl:if test="$milestoneID != ''">
|
||||
<xsl:value-of select="/root/milestones/milestone/id[text()=$milestoneID]/parent::milestone/title"/>
|
||||
</xsl:if>
|
||||
</td>
|
||||
</tr>
|
||||
</xsl:template>
|
||||
</xsl:stylesheet>
|
|
@ -1,316 +0,0 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Xml;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
|
||||
namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a formatter provider that counts download of files and attachments.
|
||||
/// </summary>
|
||||
public class RssFeedDisplay : IFormatterProviderV30 {
|
||||
|
||||
private IHostV30 _host;
|
||||
private string _config;
|
||||
private bool _enableLogging = true;
|
||||
private static readonly ComponentInformation Info = new ComponentInformation("RSS Feed Display Plugin", "Threeplicate Srl", "3.0.2.528", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/RssFeedDisplay.txt");
|
||||
|
||||
private static readonly Regex RssRegex = new Regex(@"{(RSS|Twitter):(.+?)(\|(.+?))?}",
|
||||
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 1.
|
||||
/// </summary>
|
||||
public bool PerformPhase1 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 2.
|
||||
/// </summary>
|
||||
public bool PerformPhase2 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 3.
|
||||
/// </summary>
|
||||
public bool PerformPhase3 {
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution priority of the provider (0 lowest, 100 highest).
|
||||
/// </summary>
|
||||
public int ExecutionPriority {
|
||||
get { return 50; }
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
// {RSS:FeedAddress}
|
||||
// FeedAddress not found -> ignored
|
||||
|
||||
StringBuilder buffer = new StringBuilder(raw);
|
||||
|
||||
try {
|
||||
KeyValuePair<int, Match> block = FindAndRemoveFirstOccurrence(buffer);
|
||||
|
||||
while(block.Key != -1) {
|
||||
string blockHash = block.Value.ToString();
|
||||
|
||||
string result = null;
|
||||
|
||||
if(System.Web.HttpContext.Current != null) {
|
||||
result = System.Web.HttpContext.Current.Cache[blockHash] as string;
|
||||
}
|
||||
|
||||
if(result == null) {
|
||||
bool isTwitter = block.Value.Groups[1].Value.ToLowerInvariant() == "twitter";
|
||||
int entries = 1;
|
||||
bool newWindow = true;
|
||||
int words = 350;
|
||||
|
||||
if(block.Value.Groups.Count > 3) {
|
||||
AnalyzeSettings(block.Value.Groups[4].Value, out entries, out newWindow, out words);
|
||||
}
|
||||
|
||||
if(isTwitter) {
|
||||
result = @"<div class=""twitterfeed"">";
|
||||
}
|
||||
else {
|
||||
result = @"<div class=""rssfeed"">";
|
||||
}
|
||||
XmlDocument feedXml = GetXml(block.Value.Groups[2].Value);
|
||||
XmlNode node = feedXml.DocumentElement;
|
||||
for(int i = 0; i < entries; i++) {
|
||||
XmlNode itemTitle = node.SelectNodes("/rss/channel/item/title")[i];
|
||||
if(itemTitle != null) {
|
||||
XmlNode itemLink = node.SelectNodes("/rss/channel/item/link")[i];
|
||||
XmlNode itemContent = node.SelectNodes("/rss/channel/item/description")[i];
|
||||
string itemContentStr = StripHtml(itemContent.InnerText);
|
||||
itemContentStr = (itemContentStr.Length > words && itemContentStr.Substring(words - 3, 5) != "[...]") ? itemContentStr.Substring(0, itemContentStr.IndexOf(" ", words - 5) + 1) + " [...]" : itemContentStr;
|
||||
if(itemContentStr.Length <= 1) itemContentStr = StripHtml(itemContent.InnerText);
|
||||
|
||||
if(isTwitter) {
|
||||
string tweet = itemTitle.InnerText;
|
||||
tweet = tweet.Substring(tweet.IndexOf(":") + 2);
|
||||
result += @"<div class=""tweet"">
|
||||
<a href=""" + itemLink.InnerText + @""" title=""Go to this Tweet""";
|
||||
if(newWindow) result += @" target=""_blank""";
|
||||
result += @">" + tweet + @"</a>
|
||||
</div>";
|
||||
}
|
||||
else {
|
||||
result += @"<div class=""rssentry"">
|
||||
<span class=""rsstitle"">
|
||||
<a href=""" + itemLink.InnerText + @""" title=""" + itemTitle.InnerText + @"""";
|
||||
if(newWindow) result += @" target=""_blank""";
|
||||
result += @">" + itemTitle.InnerText + @"</a>
|
||||
</span>
|
||||
<br />
|
||||
<span class=""rsscontent"">" + itemContentStr + @"</span>
|
||||
</div>";
|
||||
}
|
||||
}
|
||||
}
|
||||
result += @"</div>";
|
||||
|
||||
if(System.Web.HttpContext.Current != null) {
|
||||
System.Web.HttpContext.Current.Cache.Add(blockHash, result, null, DateTime.Now.AddMinutes(60),
|
||||
System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.Normal, null);
|
||||
}
|
||||
}
|
||||
|
||||
buffer.Insert(block.Key, result);
|
||||
|
||||
block = FindAndRemoveFirstOccurrence(buffer);
|
||||
}
|
||||
}
|
||||
catch(Exception ex) {
|
||||
LogWarning(string.Format("Exception occurred: {0}", ex.Message));
|
||||
}
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Analizes the settings string.
|
||||
/// </summary>
|
||||
/// <param name="settingString">The setting string.</param>
|
||||
/// <param name="entries">The number of entries.</param>
|
||||
/// <param name="newWindow">The newWindow value.</param>
|
||||
/// <param name="words">The max number of words.</param>
|
||||
private void AnalyzeSettings(string settingString, out int entries, out bool newWindow, out int words) {
|
||||
entries = 1;
|
||||
newWindow = true;
|
||||
words = 350;
|
||||
|
||||
String[] settings = settingString.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
foreach(string set in settings) {
|
||||
string key = set.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries)[0].Trim().ToLowerInvariant();
|
||||
string value = set.Split(new char[] { '=' }, StringSplitOptions.RemoveEmptyEntries)[1].Trim().ToLowerInvariant();
|
||||
if(key == "entries") {
|
||||
try {
|
||||
entries = Int32.Parse(value);
|
||||
}
|
||||
catch(ArgumentNullException) {
|
||||
throw new ArgumentNullException("entries setting could not be null.");
|
||||
//LogWarning("entries setting could not be null.");
|
||||
}
|
||||
catch(FormatException) {
|
||||
throw new FormatException("entries setting is not a valid integer.");
|
||||
}
|
||||
}
|
||||
else if(key == "newwindow") {
|
||||
if(value == "true" || value == "1" || value == "yes") {
|
||||
newWindow = true;
|
||||
}
|
||||
else if(value == "false" || value == "0" || value == "no") {
|
||||
newWindow = false;
|
||||
}
|
||||
else {
|
||||
throw new FormatException("newWindow setting is not a valid value. Use: true/false or 1/0 or yes/no.");
|
||||
}
|
||||
}
|
||||
else if(key == "words") {
|
||||
try {
|
||||
words = Int32.Parse(value);
|
||||
}
|
||||
catch(ArgumentNullException) {
|
||||
throw new ArgumentNullException("words setting could not be null.");
|
||||
}
|
||||
catch(FormatException) {
|
||||
throw new FormatException("words setting is not a valid integer.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produces an API call, then returns the results as an Xml Document
|
||||
/// </summary>
|
||||
/// <param name="Url">The Url to the specific API call</param>
|
||||
/// <returns></returns>
|
||||
private XmlDocument GetXml(string Url) {
|
||||
try {
|
||||
var results = new XmlDocument();
|
||||
Url = string.Format("{0}", Url);
|
||||
var request = WebRequest.Create(Url);
|
||||
var response = request.GetResponse();
|
||||
using(var reader = new StreamReader(response.GetResponseStream())) {
|
||||
var xmlString = reader.ReadToEnd();
|
||||
try {
|
||||
results.LoadXml(xmlString);
|
||||
}
|
||||
catch {
|
||||
LogWarning("Received Unexpected Response from server.");
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
catch(Exception ex) {
|
||||
LogWarning(string.Format("Exception occurred: {0}", ex.Message));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds and removes the first occurrence of the custom tag.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <returns>The index->content data.</returns>
|
||||
private static KeyValuePair<int, Match> FindAndRemoveFirstOccurrence(StringBuilder buffer) {
|
||||
Match match = RssRegex.Match(buffer.ToString());
|
||||
|
||||
if(match.Success) {
|
||||
buffer.Remove(match.Index, match.Length);
|
||||
|
||||
return new KeyValuePair<int, Match>(match.Index, match);
|
||||
}
|
||||
|
||||
return new KeyValuePair<int, Match>(-1, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all HTML markup from a string.
|
||||
/// </summary>
|
||||
/// <param name="content">The string.</param>
|
||||
/// <returns>The result.</returns>
|
||||
private static string StripHtml(string content) {
|
||||
if(string.IsNullOrEmpty(content)) return "";
|
||||
|
||||
StringBuilder sb = new StringBuilder(Regex.Replace(content, "<[^>]*>", " "));
|
||||
sb.Replace(" ", "");
|
||||
sb.Replace(" ", " ");
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the title of an item for display (always during phase 3).
|
||||
/// </summary>
|
||||
/// <param name="title">The input title.</param>
|
||||
/// <param name="context">The context information.</param>
|
||||
/// <returns>The prepared title (no markup allowed).</returns>
|
||||
public string PrepareTitle(string title, ContextInformation context) {
|
||||
return title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <remarks>If the configuration string is not valid, the methoud should throw a <see cref="InvalidConfigurationException"/>.</remarks>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
this._host = host;
|
||||
this._config = config != null ? config : "";
|
||||
|
||||
if(this._config.ToLowerInvariant() == "nolog") _enableLogging = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
private void LogWarning(string message) {
|
||||
if(_enableLogging) {
|
||||
_host.LogEntry(message, LogEntryType.Warning, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return Info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 "Specify <i>nolog</i> for disabling warning log messages."; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,292 +0,0 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web;
|
||||
using System.Web.Caching;
|
||||
using System.Xml;
|
||||
using System.Xml.Xsl;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
|
||||
namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||
|
||||
/// <summary>
|
||||
/// Implements a formatter that display tickets from Unfuddle.
|
||||
/// </summary>
|
||||
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 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 ComponentInformation Info = new ComponentInformation("Unfuddle Tickets Plugin", "Threeplicate Srl", "3.0.1.471", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/PluginPack/UnfuddleTickets.txt");
|
||||
|
||||
private string _config;
|
||||
private IHostV30 _host;
|
||||
private string _baseUrl;
|
||||
private string _username;
|
||||
private string _password;
|
||||
|
||||
#region IFormatterProviderV30 Members
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 1.
|
||||
/// </summary>
|
||||
public bool PerformPhase1 {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 2.
|
||||
/// </summary>
|
||||
public bool PerformPhase2 {
|
||||
get {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 3.
|
||||
/// </summary>
|
||||
public bool PerformPhase3 {
|
||||
get {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution priority of the provider (0 lowest, 100 highest).
|
||||
/// </summary>
|
||||
public int ExecutionPriority {
|
||||
get {
|
||||
return 50;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get {
|
||||
return Info;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
if(block.Key != -1) {
|
||||
string unfuddleTickets = null;
|
||||
if(HttpContext.Current != null)
|
||||
unfuddleTickets = HttpContext.Current.Cache["UnfuddleTicketsStore"] as string;
|
||||
|
||||
if(string.IsNullOrEmpty(unfuddleTickets))
|
||||
unfuddleTickets = LoadUnfuddleTicketsFromWeb();
|
||||
|
||||
if(string.IsNullOrEmpty(unfuddleTickets))
|
||||
unfuddleTickets = LoadErrorMessage;
|
||||
|
||||
do {
|
||||
buffer.Insert(block.Key, unfuddleTickets);
|
||||
block = FindAndRemoveFirstOccurrence(buffer);
|
||||
} while(block.Key != -1);
|
||||
}
|
||||
|
||||
return buffer.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the title of an item for display (always during phase 3).
|
||||
/// </summary>
|
||||
/// <param name="title">The input title.</param>
|
||||
/// <param name="context">The context information.</param>
|
||||
/// <returns>The prepared title (no markup allowed).</returns>
|
||||
public string PrepareTitle(string title, ContextInformation context) {
|
||||
return title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <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);
|
||||
|
||||
if(configEntries.Length != 3)
|
||||
throw new InvalidConfigurationException("Configuration missing required parameters.");
|
||||
|
||||
_baseUrl = configEntries[0];
|
||||
|
||||
if(_baseUrl.EndsWith("/"))
|
||||
_baseUrl = _baseUrl.Substring(0, _baseUrl.Length - 1);
|
||||
|
||||
_username = configEntries[1];
|
||||
_password = configEntries[2];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Finds and removes the first occurrence of the custom tag.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer.</param>
|
||||
/// <returns>The index->content data.</returns>
|
||||
private static KeyValuePair<int, string> FindAndRemoveFirstOccurrence(StringBuilder buffer) {
|
||||
Match match = UnfuddleRegex.Match(buffer.ToString());
|
||||
|
||||
if(match.Success) {
|
||||
buffer.Remove(match.Index, match.Length);
|
||||
|
||||
return new KeyValuePair<int, string>(match.Index, match.Value);
|
||||
}
|
||||
|
||||
return new KeyValuePair<int, string>(-1, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds an xml document from API calls to Unfuddle.com then runs them through an Xslt to format them.
|
||||
/// </summary>
|
||||
/// <returns>An html string that contains the tables to display the ticket information, or null</returns>
|
||||
private string LoadUnfuddleTicketsFromWeb() {
|
||||
var xml = BuildXmlFromApiCalls();
|
||||
if(xml == null)
|
||||
return null;
|
||||
|
||||
var settings = new XsltSettings {
|
||||
EnableScript = true,
|
||||
EnableDocumentFunction = true
|
||||
};
|
||||
var xsl = new XslCompiledTransform(true);
|
||||
using(var reader = XmlReader.Create(Assembly.GetExecutingAssembly().GetManifestResourceStream("ScrewTurn.Wiki.Plugins.PluginPack.Resources.UnfuddleTickets.xsl"))) {
|
||||
xsl.Load(reader, settings, new XmlUrlResolver());
|
||||
}
|
||||
|
||||
string results;
|
||||
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);
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds 3 Xml Documents, the first two are lookups for Milestone, and People information, the second is the
|
||||
/// ticket information.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private XmlDocument BuildXmlFromApiCalls() {
|
||||
var milestones = GetXml("/milestones", _username, _password);
|
||||
if(milestones == null) {
|
||||
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var people = GetXml("/people", _username, _password);
|
||||
if(people == null) {
|
||||
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
||||
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);
|
||||
if(tickets == null) {
|
||||
LogWarning("Exception occurred while pulling unfuddled ticket information from the API.");
|
||||
return null;
|
||||
}
|
||||
|
||||
var results = new XmlDocument();
|
||||
results.AppendChild(results.CreateXmlDeclaration("1.0", "UTF-8", string.Empty));
|
||||
var element = results.CreateElement("root");
|
||||
results.AppendChild(element);
|
||||
element.AppendChild(results.ImportNode(milestones.ChildNodes[1], true));
|
||||
element.AppendChild(results.ImportNode(people.ChildNodes[1], true));
|
||||
element.AppendChild(results.ImportNode(tickets.ChildNodes[1], true));
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Produces an API call, then returns the results as an Xml Document
|
||||
/// </summary>
|
||||
/// <param name="Url">The Url to the specific API call</param>
|
||||
/// <param name="Username">An unfuddle account username</param>
|
||||
/// <param name="Password">The password to above unfuddle account</param>
|
||||
/// <returns></returns>
|
||||
private XmlDocument GetXml(string Url, string Username, string Password) {
|
||||
try {
|
||||
var results = new XmlDocument();
|
||||
Url = string.Format("{0}{1}", _baseUrl, Url);
|
||||
var request = WebRequest.Create(Url);
|
||||
request.Credentials = new NetworkCredential(Username, Password);
|
||||
var response = request.GetResponse();
|
||||
using(var reader = new StreamReader(response.GetResponseStream())) {
|
||||
var xmlString = reader.ReadToEnd();
|
||||
try {
|
||||
results.LoadXml(xmlString);
|
||||
}
|
||||
catch {
|
||||
LogWarning("Received Unexpected Response from Unfuddle Server.");
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
catch(Exception ex) {
|
||||
LogWarning(string.Format("Exception occurred: {0}", ex.Message));
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Logs a warning.
|
||||
/// </summary>
|
||||
/// <param name="message">The message.</param>
|
||||
private void LogWarning(string message) {
|
||||
_host.LogEntry(message, LogEntryType.Warning, null, this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
}
|
172
PluginPack/Updater.cs
Normal file
172
PluginPack/Updater.cs
Normal file
|
@ -0,0 +1,172 @@
|
|||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using ScrewTurn.Wiki.PluginFramework;
|
||||
using System.Net;
|
||||
using System.IO;
|
||||
|
||||
namespace ScrewTurn.Wiki.Plugins.PluginPack {
|
||||
|
||||
/// <summary>
|
||||
/// Plugin with the sole purpose of removing PluginPack.dll in favor of separate DLLs.
|
||||
/// </summary>
|
||||
public class Updater : IFormatterProviderV30 {
|
||||
|
||||
private static bool AlreadyRun = false;
|
||||
|
||||
private static readonly ComponentInformation _info = new ComponentInformation("Updater Plugin", "Threeplicate Srl", "3.0.2.538", "http://www.screwturn.eu", null);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the Storage Provider.
|
||||
/// </summary>
|
||||
/// <param name="host">The Host of the Component.</param>
|
||||
/// <param name="config">The Configuration data, if any.</param>
|
||||
/// <exception cref="ArgumentNullException">If <paramref name="host"/> or <paramref name="config"/> are <c>null</c>.</exception>
|
||||
/// <exception cref="InvalidConfigurationException">If <paramref name="config"/> is not valid or is incorrect.</exception>
|
||||
public void Init(IHostV30 host, string config) {
|
||||
if(host == null) throw new ArgumentNullException("host");
|
||||
if(config == null) throw new ArgumentNullException("config");
|
||||
|
||||
if(AlreadyRun) return;
|
||||
|
||||
// 1. Delete PluginPack.dll
|
||||
// 2. Download all other DLLs
|
||||
|
||||
string root = "http://www.screwturn.eu/Version/PluginPack/";
|
||||
|
||||
string[] dllNames = new string[] {
|
||||
"DownloadCounterPlugin.dll",
|
||||
"FootnotesPlugin.dll",
|
||||
"MultilanguageContentPlugin.dll",
|
||||
"RssFeedDisplayPlugin.dll",
|
||||
"UnfuddleTicketsPlugin.dll"
|
||||
};
|
||||
|
||||
string[] providerNames = new string[] {
|
||||
"ScrewTurn.Wiki.Plugins.PluginPack.DownloadCounter",
|
||||
"ScrewTurn.Wiki.Plugins.PluginPack.Footnotes",
|
||||
"ScrewTurn.Wiki.Plugins.PluginPack.MultilanguageContentPlugin",
|
||||
"ScrewTurn.Wiki.Plugins.PluginPack.RssFeedDisplay",
|
||||
"ScrewTurn.Wiki.Plugins.PluginPack.UnfuddleTickets",
|
||||
};
|
||||
|
||||
Dictionary<string, byte[]> assemblies = new Dictionary<string, byte[]>(dllNames.Length);
|
||||
|
||||
try {
|
||||
foreach(string dll in dllNames) {
|
||||
host.LogEntry("Downloading " + dll, LogEntryType.General, null, this);
|
||||
|
||||
HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(root + dll);
|
||||
req.AllowAutoRedirect = true;
|
||||
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
|
||||
|
||||
if(resp.StatusCode == HttpStatusCode.OK) {
|
||||
BinaryReader reader = new BinaryReader(resp.GetResponseStream());
|
||||
byte[] content = reader.ReadBytes((int)resp.ContentLength);
|
||||
reader.Close();
|
||||
|
||||
assemblies.Add(dll, content);
|
||||
}
|
||||
else {
|
||||
throw new InvalidOperationException("Response status code for " + dll + ":" + resp.StatusCode.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
foreach(string dll in dllNames) {
|
||||
host.GetSettingsStorageProvider().StorePluginAssembly(dll, assemblies[dll]);
|
||||
}
|
||||
|
||||
foreach(string dll in dllNames) {
|
||||
LoadProvider(dll);
|
||||
}
|
||||
|
||||
host.GetSettingsStorageProvider().DeletePluginAssembly("PluginPack.dll");
|
||||
|
||||
AlreadyRun = true;
|
||||
}
|
||||
catch(Exception ex) {
|
||||
host.LogEntry("Error occurred during automatic DLL updating with Updater Plugin\n" + ex.ToString(), LogEntryType.Error, null, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadProvider(string dll) {
|
||||
Type loader = Type.GetType("ScrewTurn.Wiki.ProviderLoader, ScrewTurn.Wiki.Core");
|
||||
var method = loader.GetMethod("LoadFromAuto");
|
||||
method.Invoke(null, new[] { dll });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked on shutdown.
|
||||
/// </summary>
|
||||
/// <remarks>This method might not be invoked in some cases.</remarks>
|
||||
public void Shutdown() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 1.
|
||||
/// </summary>
|
||||
public bool PerformPhase1 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 2.
|
||||
/// </summary>
|
||||
public bool PerformPhase2 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Specifies whether or not to execute Phase 3.
|
||||
/// </summary>
|
||||
public bool PerformPhase3 {
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the execution priority of the provider (0 lowest, 100 highest).
|
||||
/// </summary>
|
||||
public int ExecutionPriority {
|
||||
get { return 50; }
|
||||
}
|
||||
|
||||
/// <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) {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the title of an item for display (always during phase 3).
|
||||
/// </summary>
|
||||
/// <param name="title">The input title.</param>
|
||||
/// <param name="context">The context information.</param>
|
||||
/// <returns>The prepared title (no markup allowed).</returns>
|
||||
public string PrepareTitle(string title, ContextInformation context) {
|
||||
return title;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the Information about the Provider.
|
||||
/// </summary>
|
||||
public ComponentInformation Information {
|
||||
get { return _info; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 null; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue