This page is a considered
complete and accurate. However, if you find an error, please take the time
report it.
In this short article we are going to learn how to write a simple formatter provider for ScrewTurn Wiki 3.0 (STW from now on). A formatter provider is a type of plugin that processes and alters page content.
For this article, we’ll use the Download Counter plugin (available in the standard distributions of STW) as an example.
Introduction to Formatter Providers
This brief section assumes you have already a
general knowledge on STW providers.
The main task a formatter provider performs is processing the content that the wiki engine hands to it during one or more of the following phases, performed sequentially:
- Phase 1: performed before the wiki engine formats the content with its own formatter.
This phase is suitable to process custom tags, markup or other content that might be otherwise misinterpreted by the integrated formatter; overriding the behavior of existing tags is also possible. - Phase 2: performed after the wiki engine formats the content with its own formatter.
This phase is suitable for processing custom tags that do not conflict with the built-in markup.
Note: the result of Phase 1 + Phase 2 is cached: those phases are not executed again until the cache is refreshed. - Phase 3: performed before sending the content to the browser, at every web request.
This phase is suitable to process tags whose output is dynamic and potentially changes at every request.
The formatter is allowed to declare which phase(s) it must execute, so that formatters execution is optimized. The formatter is also allowed to alter the title of pages before they are sent to the browser (in other words, title formatting is only performed in
Phase 3). Last but not least, the formatter can declare its execution priority (0 to 100, 100 highest) that ultimately determines the execution order of all installed formatters.
Important note: all providers should be totally thread-safe.
Download Counter Mini Specification
The formatter is used for displaying the number of times a set of files and/or attachments has been downloaded.
Input
The formatter parses the page content during
Phase 3, looking for pieces of XML formed like the following:
<countDownloads pattern="..."[ startDate="yyyy/mm/dd"]>
<file name="..."[ provider="..."] />
<attachment name="..." page="..."[ provider="..."] />
</countDownloads>The
pattern attribute determines the output text of the formatter. It can contain any string and any of the following placeholders (case insensitive):
- #COUNT#: total download count
- #DAILY#: downloads per day
- #WEEKLY#: downloads per week
- #MONTHLY#: downloads per month.
startDate is the date used for calculating the daily, weekly and monthly downloads values. If omitted,
January 1st, 2009 is assumed.
The
countDownloads root tag can contain one or more
file and
attachment elements. The
name attribute is respectively the full name of the file or the name of the attachment. The optional
provider attribute is the full type name of the files storage provider that manages the file or attachment. If omitted, the default provider is assumed. For attachment elements, the
page attribute specifies the name of the page the attachment belongs to.
Output
The output of the formatter is determined by the
pattern attribute. The pattern string will be rendered, replacing the placeholders with their respective calculated values.
For example, a pattern string formed as follows:
Downloads: #count# (#weekly# downloads per week)
Would generate an output like this:
Downloads: 1234 (45 downloads per week)
Input Error Handling and Configuration¶
If a specified file or attachment does not exist (or any other parameter is invalid), the element is ignored and a warning message logged for debugging purposes.
If the configuration string equals "nolog" (case insensitive), then no messages are logged.
Implementation
The implementation of the
Download Counter plugin is relatively easy. The required steps are described in the following sections. To see the final result, you can download the source code package of STW.
Environment Setup
Assuming you have
Visual Studio 2008 Standard or
Professional installed on your development machine, download the source code of STW and unpack it on your hard drive.
- Open the solution in Visual Studio (ScrewTurnWiki.sln)
- add a new C# Class Library project, named MyPlugins
- for the new project, add a reference to the PluginFramework project, already part of the solution
- also add a reference to System.Xml (for processing XML) and to System.Web (required by the Plugin Framework).
Generating a Stub
You can now add a class to the
MyPlugins project, named
DownloadCounter. The class should implement the
IFormatterProviderV30 interface, defined in the
ScrewTurn.Wiki.PluginFramework namespace:
public class DownloadCounter : IFormatterProviderV30 {
}You can now use Visual Studio to automatically implement the interface with empty methods (omitted for brevity).
Filling in Required Information
As previously said, our formatter will work in
Phase 3, so the
PerformPhase1 and
PerformPhase2 properties should both return
false, while
PerformPhase3 should return true.
Execution priority is not important in this case, thus the
ExecutionPriority property should simply return 50 (midway between 0 and 100).
The
Init method should set the
enableLogging flag accordingly to the configuration string:
public void Init(IHostV30 host, string config) {
this.host = host;
this.config = config != null ? config : "";
if(this.config.ToLowerInvariant() == "nolog") enableLogging = false;
}The
PrepareTitle method should simply return the
title parameter unchanged because the formatter will not process titles.
You can set the
ConfigHelpHtml property to return a brief help text such as:
"Specify <i>nolog</i> for disabling warning log messages for non-existent files or attachments."
Also, properly return an instance of
ComponentInformation in the
Information property.
Implementing the Formatter
The main formatter method,
Format, can now be implemented as follows:
public string Format(string raw, ContextInformation context, FormattingPhase phase) {
StringBuilder buffer = new StringBuilder(raw);
KeyValuePair<int, string> block = FindAndRemoveFirstOccurrence(buffer);
while(block.Key != -1) {
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));
buffer.Insert(block.Key,
BuildResult(pattern, (int)downloads, dailyDownloads, weeklyDownloads, monthlyDownloads));
block = FindAndRemoveFirstOccurrence(buffer);
}
return buffer.ToString();
}As you can see from the code, the
Format method looks for
countDownloads tag pairs and replaces them with the appropriate output string. Implementation details are not relevant for this article and are therefore omitted for brevity. It is however important to know how the plugin can interact with the wiki engine via the
IHostV30 interface: in this case, the
GetProvider method is used to retrieve an instance of the proper files storage provider as file or attachment elements are processed:
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;
}The host class instance variable, set in the
Init method, allows access to a number of methods and properties that, in this case, are used to get the existing files storage providers and the default one. For details about the
IHostV30 interface, please take a look at the
ScrewTurnWiki.chm file included in the source code distribution.
Deployment
Once compiled, the formatter is essentially a .NET class library file:
MyPlugins.dll. You can upload it on your wiki using the administration interface,
Plugins tab, and it will be immediately operational.