Click here to Skip to main content
14,216,682 members
Click here to Skip to main content
Posted 4 Jan 2008


62 bookmarked

Lean and Mean Blogging Revisited

Rate this:
4.75 (24 votes)
Please Sign up or sign in to vote.
4.75 (24 votes)
4 Jan 2008     CDDL    
An alternative approach to blogging engines



First of all, before I even touch anything remotely technical, I’d like to say two things:

  1. A very big thank you to Marc Clifton (here too). His articles A Lean and Mean Blog Engine, and A Lean and Mean Blog Engine, part II on a lean and mean blogging engine are the inspiration for this article as an article.
  2. This is my first article, as such, I'm not asking you to be “gentle”, I'm telling you to take it apart, shred it to pieces, chew it and spit it back out. The more critical you are, the happier I'll be, because this way my next article will be that much better.

P.S.. I mention Marc Clifton many times in this article. I'm not idolizing him, but let it be known I totally respect the guy’s coding skillz. Yes, skillz.


Several forces moved me into writing this article. Marc’s articles were a part of it. I like to do things, even if they are thought to be reinventing the wheel because I prove to myself that I can do it, and also I enjoy beating my own path to the goal as that makes me a better Engineer. And since my website has long been overdue, I took this as a double opportunity to both develop my website and build ABE.
Ok, now let’s get cracking and introduce you to ABE and talk shop.

ABE - Using the Code

ABE stands for “Another Blogging Engine”. As Developers and Engineers, we know that there are several correct routes to a defined goal. Choosing which way to go could be due to technical limitations, constraints, personal choice or coding style. That said, and though there are several blogging engines out there [Subtext, DotText, DasBlog and more], Marc chose to write his own for his reasons, and I share many of those reasons.

Blog Entry Requirements

With lessons learned from studying other Blogging Engines, I would list the features of blogs as follows:

  • Titles & Subtitles
  • The entry itself
  • The date the blog was written
  • The tags that categorize the blog entry
  • The ability to add comments
  • Permalinks so that visitors can both reference the blog entry and so that they can navigate straight to the blog entry instead of plowing through hundreds
  • Archives to reach older blogs

There are other things which make up a more complete blog such as friendly URLs and TrackBacks/PingBacks/LinkBacks. But that is beyond the scope of ABE at this stage. In fact, I opted to forgo the ability for readers to comment for now. I do want people to be able to comment on my Blog Entries, but all in good time.


Before I get to the markup and code of the article, I'll post the diagram of the database that I used:

Figure 1: Database Diagram.

The backend is SQL Server 2005. This configuration ought to work with any version of SQL2k5. As you can see, it’s a very simple database. It’s all that is needed at the moment. In fact, it’s more than what is needed as there’s the foundation for comments that I haven't included in this version. Don't worry, the follow up article will be discussing comments, log ins, captcha and RSS feeds (although the feed is up and running).


Now that we've narrowed down our requirements to the bare essentials of what makes a blog, and since I'm not very artistic, I went for the following look:

Figure 2: Blog Entry Look and Feel.

I also gave the alternate a slightly different look with the background a pleasant shade of grey.

Blog Entry Control

The architecture of the Blog Entry: I'm not going to be pointing out the Title or the Subtitle or the entry itself, those are obvious, but you will see that in the lower left corner we have two elements worthy of note. The category tags and the permalink address. On the lower right hand we have the date in long format so as not to confuse the format in use. I implemented the Blog Entry (as I named it) as a User Control with the markup as:

<%@ control language="C#" autoeventwireup="true" codefile="blogEntry.ascx.cs"

                          inherits="controls_blogEntry" enableviewstate="true" %>
<asp:table runat="server" width="100%" id="blogEntryTable">
<asp:TableRow runat="server" CssClass="blogEntryTitleRow">
    <asp:TableCell runat="server" ID="blogEntryIDCell" Visible="false"></asp:TableCell>
    <asp:TableCell runat="server" ID="blogEntryTitleCell" CssClass="blogEntryTitle">
<asp:TableRow runat="server" CssClass="blogEntrySubTitleRow">
    <asp:TableCell runat="server" ID="blogEntrySubTitleCell">
<asp:TableRow runat="server" CssClass="blogEntryDataRow">
    <asp:TableCell runat="server" ID="blogEntryDataCell">blogEntryData</asp:TableCell>
<asp:TableRow runat="server" CssClass="TagsRow">
    <asp:TableCell runat="server" ID="TagsCell" CssClass="TagsRow">
    <div class="TagsRow">Tags Row</div>
<asp:TableRow runat="server" CssClass="blogEntryPermalinkRow">
    <asp:TableCell runat="server" ID="PermalinkCell"><div>PermaLink</div>
<asp:TableFooterRow runat="server" CssClass="blogEntryFooterRow">
    <asp:TableCell runat="server" ID="blogEntryFooterCell">
    <div>footer row</div>

As you can see from the markup, the ID and CssClass names are rather long and VERY descriptive. I like to have that during development because it simplifies debugging infinitely. But when it comes to deployment, I actually use much shorter names (usually acronyms), this cuts down considerably on the amount of text being sent over the wire (or over the air for that matter). It’s a pain, but worth it when deploying something that would face heavy usage. As you're reading this article, always maintain the idea that this is not high-throughput production quality. The code was written in 5 days and the article across almost 3 weeks with little if any proper specs mapped out.

Anyways, if you're more experienced in HTML/XHTML development you can skip forward this following paragraph, if not then I hope it answers your question as to why DIV tags? You can also see my liberal usage of the <DIV></DIV> tags (in this control and everywhere else on my site, just check out the page source). The reason is because DIVs (from their names) are logical divisions on a page. You can apply formatting to a particular DIV tag and ignore the other DIVs. As well, since the DIV can be ID’ed, it is excellent for dynamic content, you know precisely where to place it. Finally, the DIV tag adds a line break before and after, thus emphasizing the section that it is. Exposing the attributes of the control as normal, updating each entry is a cinch:

/// <span class="code-SummaryComment"><summary></span>
/// Exposes the BlogEntryID attribute of the underlying markup (blogEntryIDCell)
/// <span class="code-SummaryComment"></summary></span>
public int BlogEntryID
        return int.Parse(this.blogEntryIDCell.Text);
        this.blogEntryIDCell.Text = value.ToString();

/// <span class="code-SummaryComment"><summary></span>
/// Exposes the BlogEntryTitle attribute of the underlying markup
/// (blogEntryTitleCell)
/// <span class="code-SummaryComment"></summary></span>
public string BlogEntryTitle
        return this.blogEntryTitleCell.Text;
        //this will be a link on to the page with the single blog, complete.
        // Should be used as a permalink?
        this.blogEntryTitleCell.Text = CreateLink(value);

(And so on for the other attributes) Since we have (or will have I should say) many blog entries, we obviously need a data source to retrieve a set number (I chose 10) out of whatever the total number of blog entries that exist in the database. The rest can be paged as necessary. For the other elements existing, the blogEntry controls (Permalinks, Tags, and the Date) as can be seen are built into the actual control. Now, there’s a small trick involved with each of these. Each one of these links back to the blog page with a get request specifying which filter to use to get the proper entry or entries. So, I have four possible get requests for the blog page:

FormDescription*Example, entered manually or through the navigation bar and is the default way of accessing the blog{tag ID}ID, can be accessed through the Title or from the Permalink{tag text}Tagged, can be accessed through the tag cloud or through the tags found at the tag row of the blog{month #, year #}Date, can be accessed from the archive list just below the tag,2007

*I have shortened the URL but that's to make it fit on the page.

At the moment I'm settling for the Permalink to be as is, simply a GET request with a blog entry ID. Hopefully, for the next article, I'll have URL re-writing implemented and therefore end up with something like

So now all we need to worry about is the way the posts are handled at the blog.aspx page. I've got my Page_Load method as follows:

protected void Page_Load(object sender, EventArgs e)
            if (!IsPostBack)
                if (Request.QueryString.Count > 0)
                    eBlogAction blogAction = CheckKey(Request.QueryString);

                    switch (blogAction)
                        case eBlogAction.GetByID:
                        case eBlogAction.GetByTag:
                        case eBlogAction.GetByDate:
                        case eBlogAction.DefaultAction:
                            //anything else we simply transfer back to
                            //the default blogs
                            //anything unhandled previously we simply transfer back to
                            // the blogs not the error pages (for now)
        catch (Exception ex)
             //log the error
             using (ErrorLogger el = new DataLayer.ErrorLogger(ex,
             Response.Redirect("error.aspx", true);

Loading the Data

Now, I opted for the data handling and the display of the list of Blog Entries by a control dedicated to just that named blogEntries.

<%@ Control Language="C#" AutoEventWireup="true" CodeFile="blogEntries.ascx.cs"

                          Inherits="controls_blogEntries" %>
<%@ Register TagName="BlogEntry" Src="../controls/blogEntry.ascx" TagPrefix="uc" %>
<asp:Table runat="server" ID="blogEntries" Width="100%">
    <asp:TableRow runat="server">
        <asp:TableCell Width="100%" CssClass="blogEntriesBody" runat="server">
            <asp:Repeater runat="server" ID="rptBlog">
                    <table width="100%">
                    <tr style="width: 100%;">
                            <div class="rptblogEntriesTemplate">
                                <uc:BlogEntry runat="server" ID="ItemBlogEntryItem" />
                    <tr style="width: 100%;">
                            <div class="rptBlogAlternatingItemTemplate">
                                <uc:BlogEntry runat="server" ID="ItemBlogEntryItem" />
<asp:Table runat="server" ID="btnTable" Width="100%">
        <asp:TableCell HorizontalAlign="Left">
            <asp:LinkButton ID="rptPrev" runat="server" Text="<< Previous"

                     CommandName="rptPrev" Visible="false" />
        <asp:TableCell HorizontalAlign="Right">
            <asp:LinkButton ID="rptNext" runat="server" Text="Next >>"

                     CommandName="rptNext" Visible="false" />

As you can see, it really comprises of a DataRepeater, which is infinitely more lightweight, that is bound on the server side to the PagingDataSource which is how the paging was implemented. I’m not going to post the complete code behind of the control here, suffice it to say that it's begging a re-write. Also note how the only difference that exists between the ItemTemplate and the AlternatingItemTemplate is the CSS class that is used. This is because the ONLY difference between either is a different background color that specifies the alternate rows. However, I will post the ItemDataBound Event handler (with all its simplicity):

private void rptBlog_ItemDataBound(object sender, RepeaterItemEventArgs e)
        if(e.Item.ItemType == ListItemType.Item ||
           e.Item.ItemType == ListItemType.AlternatingItem)

            //pass the values from (DataRowView)e.Item.DataItem[];
            controls_blogEntry cbe =
            DsBlog.tblBlogEntryRow ber =
                          (DsBlog.tblBlogEntryRow)((DataRowView)e.Item.DataItem).Row ;

            if (ber != null)
                cbe.BlogEntryID = ber.BlogEntryID;
                cbe.BlogEntryTitle = ber.BlogTitle;
                cbe.BlogEntrySubTitle = ber.BlogSubTitle;
                cbe.BlogEntryData = string.Format("<div>{0}</div><br />", ber.BlogEntry);
                string link = string.Format
                cbe.PermaLink = string.Format("<div class=\"blogEntryPermalinkRow\">" +
                                      "permalink: <a href=\"{0}\">{0}</a></div>", link);
                cbe.BlogEntryFooter = ber.BlogDate.ToLongDateString();

                string s = ber.Tags;
                if(s.Length > 0)
                    cbe.Tags = s.Substring(1);
                    cbe.Tags = s;


For now, I don't have the luxury of having my server at my home. Also, as has been previously discussed, I like coding things for the sake of “I can”. So, and since this is my site, I wrote a WinForm application that simply accepts the text of my blog entry in HTML format (a big thank you to Patrick Sears and Code-Frog [Rex] for pointing out FCKEditor to me and its WinForms implementation) and sends it to a Web Service that accepts the data and inserts it into the database. The Web service and the Blog editor application are worthy of their own article, so I'll be posting them later on. But here’s a sneak preview.



This quick and nearly painless foray into the world of writing my own version of a Blogging engine took me about 5 days. The writing of the article took much longer. This, to me at least, proved that the concept behind the engine is simple and that the size of the other freely available open source engines seems to be completely unwarranted - as Marc has mentioned in his article here that other engines are big and clunky; well, Sub Text weighs in at a whopping 71.6 MB of source on my drive. The source code for my complete site including images and everything without the database is a measly 1.07 MB. Granted that ABE (for now) doesn't offer the same set of features but a difference of about 70 fold is still big in my opinion.

If you'd like to give ABE a run, please follow the steps in the installme.txt file you'll find in the source zip. If you feel that there is a feature that I need to include, then, by all means, send me an email. If you have any questions, I'll be more than happy to answer them.

I will continue work on ABE. I've already promised two more articles revolving around the usage of ABE and the more advanced features that are to be implemented. So keep on the lookout for new revisions and releases.


This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)


About the Author

A Systems Engineer, a Software Engineer, a Consultant and an Entrepreneur as well as a Barbeque aficionado.

Currently, working as a Senior Systems Engineer at Intergraph, where I help enterprises move the cloud.

Married and the proud father of two boys, my family is the center of my happy universe Smile | :)

Comments and Discussions

QuestionNice work Pin
Mike Hankey4-Feb-15 13:39
professionalMike Hankey4-Feb-15 13:39 
AnswerRe: Nice work Pin
Mustafa Ismail Mustafa10-Jan-17 6:52
memberMustafa Ismail Mustafa10-Jan-17 6:52 
GeneralRe: Nice work Pin
Mike Hankey10-Jan-17 7:30
professionalMike Hankey10-Jan-17 7:30 
GeneralRe: Nice work Pin
Mustafa Ismail Mustafa10-Jan-17 7:35
memberMustafa Ismail Mustafa10-Jan-17 7:35 
GeneralRe: Nice work Pin
Mike Hankey10-Jan-17 7:54
professionalMike Hankey10-Jan-17 7:54 
GeneralThanx!!!!! Pin
Nasturzija2-Oct-08 4:03
memberNasturzija2-Oct-08 4:03 
GeneralRe: Thanx!!!!! Pin
Mustafa Ismail Mustafa2-Oct-08 11:37
memberMustafa Ismail Mustafa2-Oct-08 11:37 
GeneralGreat! Pin
Muammar©4-Jul-08 23:16
member Muammar©4-Jul-08 23:16 
GeneralRe: Great! Pin
Mustafa Ismail Mustafa5-Jul-08 2:51
memberMustafa Ismail Mustafa5-Jul-08 2:51 
Generala few things to add Pin
Tim Golisch5-May-08 11:07
memberTim Golisch5-May-08 11:07 
GeneralRe: a few things to add Pin
Mustafa Ismail Mustafa5-May-08 11:18
memberMustafa Ismail Mustafa5-May-08 11:18 
GeneralCool Abe Pin
Tamim Salem4-Mar-08 23:52
memberTamim Salem4-Mar-08 23:52 
GeneralRe: Cool Abe Pin
Mustafa Ismail Mustafa5-Mar-08 0:55
memberMustafa Ismail Mustafa5-Mar-08 0:55 
QuestionDataLayer? Pin
~Nighthawk~20-Jan-08 22:26
member~Nighthawk~20-Jan-08 22:26 
AnswerRe: DataLayer? Pin
Mustafa Ismail Mustafa20-Jan-08 22:42
memberMustafa Ismail Mustafa20-Jan-08 22:42 
GeneralRe: DataLayer? Pin
~Nighthawk~21-Jan-08 0:14
member~Nighthawk~21-Jan-08 0:14 
GeneralVery Nice Pin
Hazem Nasereddin8-Jan-08 3:50
memberHazem Nasereddin8-Jan-08 3:50 
GeneralRe: Very Nice Pin
Mustafa Ismail Mustafa8-Jan-08 6:32
memberMustafa Ismail Mustafa8-Jan-08 6:32 
GeneralYou The Man! Pin
Karl Shifflett7-Jan-08 15:19
memberKarl Shifflett7-Jan-08 15:19 
GeneralRe: You The Man! Pin
Mustafa Ismail Mustafa8-Jan-08 0:11
memberMustafa Ismail Mustafa8-Jan-08 0:11 
GeneralFriendly URL's Pin
N a v a n e e t h6-Jan-08 18:42
memberN a v a n e e t h6-Jan-08 18:42 
GeneralRe: Friendly URL's Pin
Mustafa Ismail Mustafa6-Jan-08 21:00
memberMustafa Ismail Mustafa6-Jan-08 21:00 
GeneralRe: Friendly URL's Pin
N a v a n e e t h6-Jan-08 21:47
memberN a v a n e e t h6-Jan-08 21:47 
GeneralRe: Friendly URL's Pin
Mustafa Ismail Mustafa6-Jan-08 21:56
memberMustafa Ismail Mustafa6-Jan-08 21:56 
GeneralGreat Pin
Paul Conrad6-Jan-08 6:59
professionalPaul Conrad6-Jan-08 6:59 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.