cbxChangeLogger

The cbxChangeLogger is a extension for Blogengine that will track changes that are made on a blogengine site. It will track if you created, deleted, or updated posts and pages. When you want to clear the change log and embed the data in a page or post all you have to do is type [ CHANGELOG ]  ( without a space after [ and before ] ) and the tag will be replaced with the changes that were recorded since the last time you specified the [ CHANGELOG ] tag in a page or post. For a better understanding of how exactly this extension works I have made a video availible at the bottom of the page.

The reason I created this extension is bacause I wanted something that could track any change I made to the site and not have to worry if I had forgotten to mention it in my blog.

How to use

  1. First upload the cbxChangeLogger.cs file into your App_Code/extension folder where you have blog engine installed.
  2. Then edit your web.config file and add the fallowing entry in the appSettings section "<add key="cbxChangeLogger" value="~/App_Data/cbxChangeLogger.xml"/>"
    this is the xml file where the extension will store the changes.
  3. Next create the xml file that will hold the changes and place the fallowing content in it
    <?xml version="1.0" encoding="utf-8" standalone="yes"?>
    <entries>
     
    </entries>

Downloads

Source Code

// Created by: X
// Web: http://www.createdbyx.com/
// Date: Thursday, July 30, 2009
// This work by Dean Lunz is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.5 Canada License. 
// Permissions beyond the scope of this license may be available at http://contact.createdbyx.com/.
// =============================================
 
using System;
using System.Configuration;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using BlogEngine.Core;
using BlogEngine.Core.Web.Controls;
 
 
[Extension("Records the changes made to pages or posts. Use [ CHANGELOG ] (without spaces [<-->]) in a page or post to clear the log and embed it as content.", "1.0", "createdbyx.com")]
public class cbxChangeLogger
{
    public cbxChangeLogger()
    {
        // hook into events for pages and posts
        Post.Saved += this.SavedContent;
        Page.Saved += this.SavedContent;
 
        Page.Serving += this.Serving;
        Post.Serving += this.Serving;
    }
 
    void SavedContent(object sender, SavedEventArgs e)
    {
        // check if we need to do anything
        if (e.Action == SaveAction.None) return;
 
        // get the title and id for the page or post
        Guid id;
        string type = string.Empty;
        string title = string.Empty;
        if (sender is Page)
        {
            id = ((Page)sender).Id;
            type = "Page";
            title = ((Page)sender).Title;
        }
        else
        {
            id = ((Post)sender).Id;
            type = "Post";
            title = ((Post)sender).Title;
        }
 
        // load the log file
        var uri = ConfigurationManager.AppSettings["cbxChangeLogger"];
        var filePath = System.Web.HttpContext.Current.Server.MapPath(uri);
        var doc = XDocument.Load(filePath);
 
        // parse out the entries in the log ignoring any entries that have teh same id that we stored in "id"
        var entries = from item in doc.Descendants("entry")
                      where item.GetValue("id") != id.ToString() // exclude any prev changes to this page
                      select new XElement("entry",
                                          new XElement("id", item.GetValue("id")),
                                          new XElement("changetype", item.GetValue("changetype")),
                                          new XElement("title", item.GetValue("title")),
                                          new XElement("type", item.GetValue("type")),
                                          new XElement("date", item.GetValue("date")));
 
        // add a new entry into the list for the page or post that was changed
        entries = entries.Union(new[] { new XElement("entry", 
                                                     new XElement("id", id.ToString()), 
                                                     new XElement("changetype", e.Action), 
                                                     new XElement("type", type),
                                                     new XElement("date", DateTime.Today.ToLongDateString()),
                                                     new XElement("title", title))});
        // save the changes back out to the log file
        doc = new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("entries", entries));
        doc.Save(filePath);
    }
 
    private void Serving(object sender, ServingEventArgs e)
    {
        // search for the change log tag
        var tag = "[ CHANGELOGGER ]";
        while (e.Body.Contains(tag))
        {
            int pos = e.Body.IndexOf(tag);
            if (pos != -1)
            {
                // the tag was found so replace it with the log data
                e.Body = e.Body.Remove(pos, tag.Length);
                e.Body = e.Body.Insert(pos, this.GetHTML());
 
                // depending on the sender save the changes
                if (sender is Page)
                {
                    var page = sender as Page;
                    page.Content = e.Body;
                    page.Save();
                }
                else
                {
                    var post = sender as Post;
                    post.Content = e.Body;
                    post.Save();
                }
 
                // purge the log
                this.PurgeChangeLog();
            }
        }
    }
    // removes all entries from the log
    private void PurgeChangeLog()
    {
        var uri = ConfigurationManager.AppSettings["cbxChangeLogger"];
        var filePath = System.Web.HttpContext.Current.Server.MapPath(uri);
        System.IO.File.WriteAllText(filePath, "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\r\n<entries>\r\n</entries>");
    }
 
    // gets the html content for the data stored in the log
    private string GetHTML()
    {
        // open up the log file
        var uri = ConfigurationManager.AppSettings["cbxChangeLogger"];
        var filePath = System.Web.HttpContext.Current.Server.MapPath(uri);
        var doc = XDocument.Load(filePath);
 
        // parse out the updates/new pages or posts/deleted pages or posts
        var updates = from item in doc.Descendants("entry")
                      let pageRef = Page.GetPage(new Guid(item.GetValue("id")))
                      let postRef = Post.GetPost(new Guid(item.GetValue("id")))
                      where item.GetValue("changetype") == "Update"
                      select new string(("<li>" + item.GetValue("date") + " (" + item.GetValue("type") + "): " + "<a href=\"" + (pageRef != null ? pageRef.AbsoluteLink : postRef.AbsoluteLink) + "\">" + item.GetValue("title") + "</a></li>\r\n").ToCharArray());
 
        var newContent = from item in doc.Descendants("entry")
                         let pageRef = Page.GetPage(new Guid(item.GetValue("id")))
                         let postRef = Post.GetPost(new Guid(item.GetValue("id")))
                         where item.GetValue("changetype") == "Insert"
                         select new string(("<li>" + item.GetValue("date") + " (" + item.GetValue("type") + "): " + "<a href=\"" + (pageRef != null ? pageRef.AbsoluteLink : postRef.AbsoluteLink) + "\">" + item.GetValue("title") + "</a></li>\r\n").ToCharArray());
 
        var deletions = from item in doc.Descendants("entry")
                        where item.GetValue("changetype") == "Delete"
                        select new string(("<li>" + item.GetValue("date") + " (" + item.GetValue("type") + "): " + item.GetValue("title") + "</li>\r\n").ToCharArray());
 
                   // build some html content from the data
        string data = newContent.Count() > 0 ? "<p>New Content</p><ul>" + string.Join(string.Empty, newContent.ToArray()) + "</ul>" : string.Empty;
        data += updates.Count() > 0
                    ? "<p>Updates</p><ul>" + string.Join(string.Empty, updates.ToArray()) + "</ul>"
                    : string.Empty;
        data += deletions.Count() > 0
                    ? "<p>Removed Content</p><ul>" + string.Join(string.Empty, deletions.ToArray()) + "</ul>"
                    : string.Empty;
        return data;
    }
}
// extension methods used by cbxChangeLogger
public static class ExtensionMethodsForcbxChangeLogger
{
    public static string GetValue(this XElement element, string name)
    {
        return GetValue(element, name, false);
    }
 
    public static string GetValue(this XElement element, string name, bool throwError)
    {
        var value = element.Descendants(name).FirstOrDefault();
        if (value == null)
        {
            if (throwError) throw new XmlException("The '" + name + "' element is not present.");
            return null;
        }
        return value.Value;
    }
}
 

Video demonstration


Creative Commons License
This work by Dean Lunz is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 2.5 Canada License. Permissions beyond the scope of this license may be available at http://contact.createdbyx.com/.

Created by: X

Just another personal website in this crazy online world

Name of author Dean Lunz (aka Created by: X)
Computer programming nerd, and tech geek.
About Me -- Resume