Writing extensions for BlogEngine 1.4 (part 3)

Lets say we want to write an extension to track user activities on our site. Blogger should be able to set basic settings, for example choose to track  posts, pages or both. Then every time user requests post or page, we increment corresponding counter by one. Look at the picture above. Its clear that we need two sets of settings: one for post/page options and another to list user activity.

ext14-3-1_thumb.png

In 1.4, extension has settings collection so you can have multiple settings per extension – lets use it. Start by defining both ExtensionSettings objects as in the code:

static protected ExtensionSettings _options = null;
static protected ExtensionSettings _locations = null;

When initialize first settings object, pass extension name (or “this” – class name will be used) to the constructor. Name second settings whatever  you like, in this case it will be “Locations”. I our example, extension object will have collection of settings, first with name Example_04 and second with name Locations. First settings set as scalar (IsScalar = true), second settings are tabular, which is default. In that second tabular settings we don’t want to show “Add” and “Edit” buttons – the data generated automatically by extension and should not be added/edited manually by blogger. This is another new feature in 1.4 – you can display only what you want, even hiding whole settings section in the admin interface.

private void InitSettings()
{
  ExtensionSettings settings = new ExtensionSettings(this);
  settings.IsScalar = true;
  ...
  ExtensionSettings locations = new ExtensionSettings("Locations");
  ...
  locations.ShowAdd = false;
  locations.ShowEdit = false;   
  ...
}

Now we need to subscribe to Post.Serving and Page.Serving events, passing name of the function to execute when event fires.

Post.Serving += new EventHandler<ServingEventArgs>(Post_Serving);
Page.Serving += new EventHandler<ServingEventArgs>(Page_Serving);

As we learned in the first tutorial, event handlers get back Post/Page as “sender” object along with some event arguments. In the handler, we look up title and link in the sender and pass it to Track function which will increment counter.

void Page_Serving(object sender, ServingEventArgs e)
{
  if (bool.Parse(_options.GetSingleValue("Page").ToString()))
  {
    string title = ((Page)(sender)).Title;
    string link = ((Page)(sender)).AbsoluteLink.ToString();
    Track(title, link);
  }
}

Another neat thing in 1.4 is ability to disable extension on initialization. If your extension requires first to set some unknown ahead of time values (application key, blogger name etc.) you can disable it on first load by setting extension status to false. Then blogger can go to admin page, set this value(s) and enable extension.

private void InitSettings()
{
  ...
  _settings = ExtensionManager.InitSettings("MyExtension", settings);
  ExtensionManager.SetStatus("MyExtension", false);
}

The entire code for extension shown below, as you can see it is simple and you can build sophisticated system to track down site statistics using this kind of functionality.

using System;
using BlogEngine.Core.Web.Controls;
using BlogEngine.Core;
using System.Data;

[Extension("Example_04", "1.0", "<a href=\"http://me.net\">Me</a>")]
public class Example_04
{
  static protected ExtensionSettings _options = null;
  static protected ExtensionSettings _locations = null;
  public Example_04()
  {
    Post.Serving += new EventHandler<ServingEventArgs>(Post_Serving);
    Page.Serving += new EventHandler<ServingEventArgs>(Page_Serving);
    InitSettings();
  }
  void Page_Serving(object sender, ServingEventArgs e)
  {
    if (bool.Parse(_options.GetSingleValue("Page").ToString()))
    {
      string title = ((Page)(sender)).Title;
      string link = ((Page)(sender)).AbsoluteLink.ToString();
      Track(title, link);
    }
  }
  private void Post_Serving(object sender, ServingEventArgs e)
  {
    if (bool.Parse(_options.GetSingleValue("Post").ToString())
      e.Location == ServingLocation.SinglePost)
    {
      string title = ((Post)(sender)).Title;
      string link = ((Post)(sender)).AbsoluteLink.ToString();
      Track(title, link);
    }
  }
  private void Track(string title, string link)
  {
    DataTable dt = _locations.GetDataTable();
    int i = 0;
    int cnt = 0;
    foreach (DataRow row in dt.Rows)
    {
      if (row["Link"].ToString() == link)
      {
        cnt = int.Parse(_locations.Parameters[2].Values[i]) + 1;
        _locations.Parameters[2].Values[i] = cnt.ToString();
        break;
      }
      i++;
    }
    if (cnt == 0)
    {
      _locations.AddValues(new string[] { title, link, "1" });
    }
    ExtensionManager.SaveSettings("Locations", _locations);
  }
  private void InitSettings()
  {
    ExtensionSettings settings = new ExtensionSettings(this);
    settings.IsScalar = true;
    settings.AddParameter("Post", "Track post clicks", 10, false, false, ParameterType.Boolean);
    settings.AddValue("Post", true);
    settings.AddParameter("Page", "Track page clicks", 10, false, false, ParameterType.Boolean);
    settings.AddValue("Page", true);
    _options = ExtensionManager.InitSettings(this.GetType().Name, settings);
    ExtensionSettings locations = new ExtensionSettings("Locations");
    locations.AddParameter("Title");
    locations.AddParameter("Link", "Link", 150, true, true);
    locations.AddParameter("Count", "Count", 10, false, false, ParameterType.Integer);
    locations.ShowAdd = false;
    locations.ShowEdit = false;   
    _locations = ExtensionManager.InitSettings(this.GetType().Name, locations);
  }
}

Built-in admin functionality for extensions is a great feature in BlogEngine, but for some it still might be not enough. What if you need non-standard admin interface? No worries, you can build your own and simply plug it in the BlogEngine admin interface. We'll go through this exercise in the next tutorial.

About RTUR.NET

This site is all about developing web applications with focus on designing and building open source blogging solutions. Technologies include ASP.NET Core, C#, Angular, JavaScript and more.