// 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;
}
}