An Example of Writing Your Own Blog Engine (mini-version) Part Three

This is a three part series, if you haven’t already read part one, you may want to start there (An Example of Writing Your Own Blog Engine (mini-version)), or part two (An Example of Writing Your Own Blog Engine (mini-version) Part Two).

Presentation Layer

Ok, well this will be the last piece to the puzzle and we’re going to start with the main thread which will house the featured blog post itself as well as a mechanism for handling comments. We want to make it in such a way that we can easily control the number of blog posts that are displayed. For example, if we wanted to display one featured post (the most recent in this example), or allow a user to select a month and display all blog posts for that month. It seems to me that a repeater sounds like a nice, flexible, .NET control to handle this situation. 
Here is the following HTML I used, you can of course change this to however fits
your needs.

HTML and ASP.NET Controls

<asp:Repeater ID="rptMainList" runat="server">
    <ItemTemplate>
        <!-- Blog date -->
        <div><asp:Literal ID="litPublishDate" runat="server" Text='<%#Eval("PubDate").ToLongDateString()%>'></asp:Literal></div>
        <hr />

        <h1><asp:Literal ID="litBlogTitle" runat="server" Text='<%#Eval("Title")%>'></asp:Literal></h1>

        <asp:Literal ID="litBlogPost" runat="server" Text='<%#Eval("Description")%>'></asp:Literal>
        
        <asp:HiddenField ID="hdnBlogID" runat="server" Value='<%#Eval("BlogID")%>' />
        
        <hr />

        <!-- Blog: Add a comment -->
        <table cellpadding="2" cellspacing="4" width="100%">
            <tr>
                <td style="width: 35%;">Enter your name:</td>
                <td style="width: 65%;">Have a comment? Post it here.</td>
            </tr>
            <tr>
                <td><input type="text" id="tbName" runat="server" style="width: 100%;" /></td>
                <td rowspan="5" style="vertical-align: top;"><textarea id="tbComment" runat="server" cols="1" rows="1"></textarea></td>
           </tr>
            <tr>
                <td>Enter your email:</td>
            </tr>
            <tr>
                <td><input type="text" id="tbEmail" runat="server" style="width: 100%;" /></td>
            </tr>
            <tr>
                <td>Enter your URL here:</td>
            </tr>
            <tr>
                <td><input type="text" id="tbURL" runat="server" style="width: 100%;" /></td>
            </tr>
            <tr>
                <td colspan="2" style="text-align: right;">
                    <asp:LinkButton ID="btnPostComment" runat="server" Text="add comment" CommandName="PostComment"></asp:LinkButton>
                </td>
            </tr>
        </table>

        <hr />

        <!-- Blog: Previous comments -->
        <asp:UpdatePanel ID="upnlBlogComments" runat="server">
        <Triggers>
            <asp:AsyncPostBackTrigger ControlID="btnPostComment" EventName="Click" />
            <asp:AsyncPostBackTrigger ControlID="rptMainList" EventName="ItemCommand" />
        </Triggers>
        <ContentTemplate>
            <asp:Repeater ID="rptBlogComments" runat="server" OnItemDataBound="rptBlogComments_ItemDatabound">
                <HeaderTemplate>
                    <table cellpadding="2" cellspacing="4" width="100%" class="comments">
                        <tr>
                            <td colspan="2"><h3 style="padding-bottom: 0px; margin-bottom: 0px;"><span style="color: Black;"><asp:Label ID="lblNumberOfComments" runat="server"></asp:Label></span> Comments</h3></td>
                        </tr>
                </HeaderTemplate>
                <ItemTemplate>
                        <tr>
                            <td>
                                <asp:Label ID="lblDate" runat="server"></asp:Label><br />
                                <asp:Label ID="lblPoster" runat="server"></asp:Label><br />
                                <asp:HyperLink ID="lnkURL" runat="server"></asp:HyperLink>
                            </td>
                            <td style="padding-left: 25px;">
                                <asp:Label ID="lblComment" runat="server" Text=""></asp:Label>
                            </td>
                        </tr>
                </ItemTemplate>
                <AlternatingItemTemplate>
                        <tr>
                            <td colspan="2"><hr /></td>
                        </tr>
                        <tr>
                            <td>
                                <asp:Label ID="lblDate" runat="server"></asp:Label><br />
                                <asp:Label ID="lblPoster" runat="server"></asp:Label><br />
                                <asp:HyperLink ID="lnkURL" runat="server"></asp:HyperLink>
                            </td>
                            <td style="padding-left: 25px;">
                                <asp:Label ID="lblComment" runat="server" Text=""></asp:Label>
                            </td>
                        </tr>
                        <tr>
                            <td colspan="2"><hr /></td>
                        </tr>
                </AlternatingItemTemplate>
                <FooterTemplate>
                    </table>
                </FooterTemplate>
            </asp:Repeater>
        </ContentTemplate>
        </asp:UpdatePanel>
    </ItemTemplate>
</asp:Repeater>

VB Code for Handling the Main Post

Now, we also need a bit of code to handle working with the blog post itself, we’ll reference what we created in the business layer to do this. Notice the use of a helper function to handle repetive code, this is good practice to reduce redundant code making maintenance easier. Also, I prefer to standardize the way I title these types of functions, it will also help in ease of maintenance.

Private _blogbll As BlogBLL

Public Property BlogAdapter() As BlogBLL
    Get
        If _blogbll Is Nothing Then
            _blogbll = New BlogBLL
        End If

        Return _blogbll
    End Get
    Set(ByVal value As BlogBLL)
        _blogbll = value
    End Set
End Property

''' <summary>
''' Get's most recent blog post.
''' </summary>
''' <remarks></remarks>
Private Sub GetBlogPost()
    Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetMostRecentBlogPost()

    Try
        GetBlogPost_Helper(blogPostDT)
    Catch ex As Exception
        'If this fails, it means there are no blog posts in database at all
        'Should maybe redirect to a plain / non-blog page
        Exit Sub
    End Try
End Sub

''' <summary>
''' Get's specific blog post.
''' </summary>
''' <param name="BlogID"></param>
''' <remarks></remarks>
Private Sub GetBlogPost(ByVal blogID As Integer)
    Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetBlogPost(blogID)
    GetBlogPost_Helper(blogPostDT)
End Sub

''' <summary>
''' Get's all blog posts for a given month/year (archived posts)
''' </summary>
''' <param name="month"></param>
''' <param name="year"></param>
''' <remarks></remarks>
Private Sub GetBlogPost(ByVal month As Integer, ByVal year As Integer)
    Dim blogPostDT As Blog.BlogPostsViewDataTable = BlogAdapter.GetBlogPosts(month, year)
    GetBlogPost_Helper(blogPostDT)
End Sub

Private Sub GetBlogPost_Helper(ByVal blogPostDR As Blog.BlogPostsViewDataTable)
    rptMainList.DataSource = blogPostDR
    rptMainList.DataBind()
End Sub

Protected Sub rptBlogPosts_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptBlogPosts.ItemDataBound
    Select Case e.Item.ItemType
        Case ListItemType.Item
            rptBlogPosts_Helper(sender, e)
        Case ListItemType.AlternatingItem
            rptBlogPosts_Helper(sender, e)
    End Select
End Sub

Private Sub rptBlogPosts_Helper(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
    Dim lnkBlogPost As HyperLink = DirectCast(e.Item.FindControl("lnkBlogPost"), HyperLink)

    Dim title As String = e.Item.DataItem("Title").ToString().Replace(" ", "_").Replace(".", "").Replace(",", "")

    lnkBlogPost.Text = e.Item.DataItem("Title").ToString()
    'lnkBlogPost.NavigateUrl = String.Format("default.aspx?blogid={0}", e.Item.DataItem("BlogID").ToString())
    lnkBlogPost.NavigateUrl = String.Format("{0}-{1}", e.Item.DataItem("BlogID").ToString(), title)
End Sub

VB Code for Handling Blog Comments

The same needs to be done with the blog comments.

Private Sub rptBlogComments_Helper(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
    Dim lblDate As Label = DirectCast(e.Item.FindControl("lblDate"), Label)
    Dim lblPoster As Label = DirectCast(e.Item.FindControl("lblPoster"), Label)
    Dim lnkURL As HyperLink = DirectCast(e.Item.FindControl("lnkURL"), HyperLink)
    Dim lblComment As Label = DirectCast(e.Item.FindControl("lblComment"), Label)

    Dim dateCreated As DateTime
    Dim pubDate As String = "Unknown"
    Try
        dateCreated = e.Item.DataItem("PubDate")
        pubDate = dateCreated.ToLongDateString()
    Catch ex As Exception
    End Try
    lblDate.Text = pubDate

    Dim poster As String = e.Item.DataItem("Username").ToString()
    If String.IsNullOrEmpty(poster) = True Then
        poster = "anonymous"
    End If

    lblPoster.Text = poster
    lblComment.Text = e.Item.DataItem("Comment").ToString()

    Dim url As String = e.Item.DataItem("URL").ToString()
    lnkURL.Text = url
    lnkURL.NavigateUrl = url
End Sub

Protected Sub rptBlogComments_ItemDatabound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
    Select Case e.Item.ItemType
        Case ListItemType.Header
            Dim blogID As Integer = hdnTempBlogID.Value
            Dim lblNumberOfComments As Label = DirectCast(e.Item.FindControl("lblNumberOfComments"), Label)

            lblNumberOfComments.Text = BlogAdapter.GetNumberOfBlogComments(blogID).ToString()
        Case ListItemType.Item
            rptBlogComments_Helper(sender, e)
        Case ListItemType.AlternatingItem
            rptBlogComments_Helper(sender, e)
    End Select
End Sub

Protected Sub rptMainList_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles rptMainList.ItemCommand
    Select Case e.CommandName
        Case "PostComment"
            Dim hdnBlogID As HiddenField = DirectCast(e.Item.FindControl("hdnBlogID"), HiddenField)
            Dim tbComment As HtmlTextArea = DirectCast(e.Item.FindControl("tbComment"), HtmlTextArea)
            Dim tbName As HtmlInputText = DirectCast(e.Item.FindControl("tbName"), HtmlInputText)
            Dim tbEmail As HtmlInputText = DirectCast(e.Item.FindControl("tbEmail"), HtmlInputText)
            Dim tbURL As HtmlInputText = DirectCast(e.Item.FindControl("tbURL"), HtmlInputText)

            Dim blogID As Integer = hdnBlogID.Value
            Dim comment As String = tbComment.Value
            Dim username As String = tbName.Value
            Dim email As String = tbEmail.Value
            Dim url As String = tbURL.Value

            Try
                BlogAdapter.CreateBlogComment(blogID, comment, username, email, url)
            Catch ex As Exception
                'FIX add error handling here
            End Try

            'Retrieve blog comments
            Dim rptBlogComments As Repeater = DirectCast(e.Item.FindControl("rptBlogComments"), Repeater)

            rptBlogComments.DataSource = BlogAdapter.GetBlogComments(blogID)
            rptBlogComments.DataBind()
    End Select
End Sub

Protected Sub rptMainList_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptMainList.ItemDataBound
    hdnTempBlogID.Value = e.Item.DataItem("BlogID")

    'Retrieve blog comments
    Dim rptBlogComments As Repeater = DirectCast(e.Item.FindControl("rptBlogComments"), Repeater)

    rptBlogComments.DataSource = BlogAdapter.GetBlogComments(e.Item.DataItem("BlogID"))
    rptBlogComments.DataBind()
End Sub

Finally, the Trigger

All the trigger does (in this case on page load) is handle the possible incoming data variations as well as kick off the whole process. As you can see below we have a few variations of data that can be passed in (and more in the real blog and yours as you add to it).

First, no additional data can be passed in and the blog engine will default to the most recent post only. Alternatively, you could pass in the month and year and retrieve a list of blog posts for that month. And finally, you can pass in a blog id and retrieve a specific blog post to read.

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    If Page.IsPostBack = False Then
        Dim month As Integer = 0
        Try
            month = Integer.Parse(Request.QueryString("month"))
        Catch ex As Exception
        End Try

        Dim year As Integer = 0
        Try
            year = Integer.Parse(Request.QueryString("year"))
        Catch ex As Exception
        End Try

        Dim blogID As Integer = 0
        Try
            blogID = Integer.Parse(Request.QueryString("blogid"))
        Catch ex As Exception
        End Try

        If month > 0 And year > 0 Then
            GetBlogPost(month, year)
        ElseIf blogID > 0 Then 'Populate requested blog post
            GetBlogPost(blogID)
        Else 'Populate most recent blog post
            GetBlogPost()
        End If
    End If
End Sub

And that’s it! In just three short installments we put together a basis for your very own blog engine which I encourage all of you to build upon, rewrite, improve, and generally play around with. Please leave comments on new ideas for the blog engine, updates to how you’ve enhanced it, or any bugs found within this base (there’s always a few).

One thought on “An Example of Writing Your Own Blog Engine (mini-version) Part Three

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s