.NET and Open Source: better together

RTur.net

  • Join Us on Facebook!
  • Follow Us on Twitter!
  • LinkedIn
  • Subcribe to Our RSS Feed

Extension Manager for BlogEngine

BlogEngine has excellent extensibility model, core libraries expose any significant event you can think of to the outside world and you can subscribe to this events from your code and get access to the posts, pages, comments, files etc. And it does not take a lot to write extension – all you need is to decorate your class with [Extension] attribute. On application load BlogEngine uses reflection to lookup types in the compiled assembly and, if class decorated as extension, BE creates instance of this class. Here is a code from Global.asax that does just that:

foreach (Type type in types) {
    object[] attributes = type.GetCustomAttributes(typeof(ExtensionAttribute), false);
                
    foreach (object attribute in attributes) {
        a.CreateInstance(type.FullName);
    }
} 

Now BE has a handle on your extension and, if you subscribed to any events, it will call method inside extension and pass appropriate object, like body of the post for example, so method can do the job. So far so good, for simple extensions you don’t need anything else. But what if you have extension that needs extra attention? What if you want it be customizable? May be you want to be able to turn it on and off without FTP-ing files to the host server? Make changes online? May be automated upload and istall? Wouldn’t it be cool if BlogEngine had admin pages that let you do all that and more? There is nothing ground braking here, other platforms do similar stuff. Take a look at how WordPress manages plugins:

I think it would be cool to have this kind of facility inside BlogEngine, too. But some might say that it is may be against KISS approach preached by BE. May be. How about compromise?

Here are my thoughts: let’s do a manager that is kind of extension (well, more like user control) itself. So we got best of two worlds: clean core code and rich optional functionality. How? Well, let’s make baby steps, shell we. I start with Extension Manager that emulates WP picture above: lists all of the extensions available to you and let you turn them on and off, all from within admin panel and with minimum changes to your precious install base.

First, I created directory under “~/User controls” and named it “xmanager” – this is where manager files will live. Then I added Default.aspx page and set master page for it:

MasterPageFile="~/admin/admin1.master"

Then I’ve added this line to Web.sitemap:

<siteMapNode url="~/User controls/xmanager/default.aspx" title="Extensions"  description=""  roles="administrators"/>

Now if you go to your blog and log in as admin, you’ll see “Extensions” tab in the administration panel. That wasn’t hard. Next I need to list all extensions in that new admin page, and this is where I can borrow from code found in Global.asax. Here how we use it:

Assembly a = Assembly.Load(assemblyName);
Type[] types = a.GetTypes();
 
foreach (Type type in types)
 
    object[] attributes = type.GetCustomAttributes(typeof(ExtensionAttribute), false);
 
    foreach (object attribute in attributes)
    {
        ExtensionAttribute x = (ExtensionAttribute)attribute;
 
        xname = type.Name;
        xdesc = x.Description;
        xvrsn = x.Version;
    
        // more code here...
    }
}

That will give us all we need for the list of extensions. A bit trikier is how exactly do we turn them on and off. ExtensionAttribute does not have “enabled” property, so we have to have a wrapper that would embrace needed ExtensionAttribute members and add on top some of our own. We can save this in XML file so that manager can load and use it when needed. The XML format will be:

<xmanager>
    <extensions>
        <extension 
            name="BBCode" 
            description="Converts BBCode to XHTML in the comments" 
            enabled="true" />
     // more externsions here
    </extensions>
</xmanager>

Ok, now we can load and save list of extensions that have “enabled” property, and we can easily identify if extensions should be enabled. Only if it is enabled we will create instance during Application load:

if (Enabled(type.Name))
{
    a.CreateInstance(type.FullName);
}
 
bool Enabled(string extension)
{
    bool breturn = true;
    string fileName = Server.MapPath(BlogSettings.Instance.StorageLocation) + "xmanager.xml";
    if (File.Exists(fileName))
    {
        XmlDocument doc = new XmlDocument();
        doc.Load(fileName);
 
        XmlNodeList nodelist = doc.SelectNodes("xmanager/extensions/extension");
        foreach (XmlNode node in nodelist)
        {
            if (node.Attributes["name"].Value == extension)
            {
                if (node.Attributes["enabled"].Value == "false")
                    breturn = false;  
            }
        }   
    }
    return breturn;
}

Now we rule. One last thing missing - remember, we instanciated extensions on Appliacation load event. It is easy to let user click “enable/disable” hiperlink in the admin page and update XML node with “true” or “false”, but we also need to recycle application if extension state was changed. It would be nice to use UnloadAppDomain(), but unfortunitly it requires full trust to work. Instead, we’ll touch web.config by resetting last updated time. This will force IIS to re-compile assemly and restart application.

public bool RestartWebApplication()
{
    try 
    {
        string ConfigPath = HttpContext.Current.Request.PhysicalApplicationPath + "\\web.config";
        System.IO.File.SetLastWriteTimeUtc(ConfigPath, DateTime.UtcNow);
    }
    catch 
    {
        return false;
    }
    return true;
}

And here you have it: Extension Manager for BlogEngine that let you list, enable and disable extensions within administration panel. Obviously, it is just a first step and a lot more will follow.