I'm Keyvan Nayyeri, a 25 years old Ph.D. student at
the Computer Science department of
the University of Texas at San Antonio.
I'm also
a Software Architect and Developer and previously held a B.Sc.
degree in Applied Mathematics.
This is my blog where I publish content about various topics specifically Programming Languages and Compilers, Software
Engineering and Programming.
Probably you've seen a common part of many blogging engines which is a simple navigator for individual posts that lets you navigate to the next and previous posts when viewing an individual post. This usually appears on top or under the post body and I've experienced that a reasonable number of bloggers would like to use it.
I don't know what's the exact name of this control in blogging engines but I named it Post Navigator for my Graffiti implementation.
Here I want to show you how did I write this extension for Graffiti. For those who want to dig into the Graffiti API, especially fetching data for posts based on custom queries, this example may be helpful.
Post Navigator is a simple extension that shows links to the previous and next posts in a Graffiti application. This is usually useful for sites that use Graffiti as a blog engine (like mine).
Obviously the implementation of such an extension has a simple process of finding the next and previous posts and doing some extra work to generate the appropriate output. I'll detail this process in a moment but for now keep in mind that the best way to recognize the next and previous posts is via the unique identifier of them. The previous post is the immediate post with smaller ID before the specified post and the next post is the immediate post with larger ID after the specified post. But there is an important point here and that is about unpublished posts so you need to select from published posts only.
The implementation of Post Navigator is straightforward. In a nutshell you need to:
My implementation is a simple Graffiti extension that is nothing but a single class that must be flagged with a Chalk attribute to become a Chalk extension. In addition to this, I also need two constants for the pattern of the previous and next post links and two objects of Post type for the previous and next posts.
So here is the initial part of the code:
using System;
using System.Collections.Generic;
using System.Text;
using Graffiti.Core;
using DataBuddy;
using System.Data;
namespace Graffiti.PostNavigator
{
[Chalk("postNavigator")]
public class Generator
{
private const string linkFormatPrevious =
"<a href=\"{0}\" title=\"{3}\">{2}{1}</a>";
private const string linkFormatNext =
"<a href=\"{0}\" title=\"{3}\">{1}{2}</a>";
Post _previous = null;
Post _next = null;
After this the implementation consists of two public methods that generate the navigator HTML code and some private helper methods for it. I've provided two overloads for GetNavigator which returns the string value of the HTML code. One overload is simple and a quick shortcut to use the other one and simplify the usage. The other one is the fully customizable overload that lets you pass the post as well as Boolean parameters to specify if you only want links to the previous posts or next posts and also some string parameters that let you customize the text value to be shown for the next and previous post links and a separator between them as well as two string values to append to these texts for customization and showing arrows or images. If user passes empty values for these string values then the code will apply the default behavior which is what I inspired from common blogging engines.
Here is the code for both overloads of GetNavigator:
public string GetNavigator(Post post, string previousText, string nextText)
{
return GetNavigator(post, true, true, " | ", previousText, nextText, "« ", " »");
}
public string GetNavigator(Post post, bool showPrevious, bool showNext,
string separator, string previousText, string nextText,
string previousAppendText, string nextAppendText)
{
if ((!showPrevious) && (!showNext))
{
Log.Error("Post Navigator Extension",
"What do you want to get? One of the next or previous posts must be shown!");
return string.Empty;
}
try
{
if (showPrevious)
this._previous = GetPreviousPost(post);
else
this._previous = null;
if (showNext)
this._next = GetNextPost(post);
else
this._next = null;
}
catch (Exception ex)
{
Log.Error("Post Navigator Extension", ex.Message + ex.StackTrace);
return string.Empty;
}
return GetHtmlCode(separator, previousText, nextText,
previousAppendText, nextAppendText);
}
The second overload simply calls GetPreviousPost and GetNextPost functions to get instances of the previous and next posts and then calls the GetHtmlCode function to return the string value of the final HTML code.
But the main part of the implementation is in GetPreviousPost and GetNextPost functions where we pass a Post object and get its previous and next posts. The logic behind both functions is same and there are minor differences to get the previous and next posts.
private Post GetPreviousPost(Post post)
{
Table table = new Table("graffiti_Posts", "PostCollection");
Query query = new Query(table);
query.Top = "100 PERCENT *";
Column idColumn = new Column("Id", DbType.Int32, typeof(Int32), "Id", false, true);
query.OrderByDesc(idColumn);
query.AndWhere(idColumn, post.Id, Comparison.LessThan);
Column isPublishedColumn = new Column("IsPublished", DbType.Boolean, typeof(bool),
"IsPublished", false, false);
query.AndWhere(isPublishedColumn, false, Comparison.NotEquals);
PostCollection posts = PostCollection.FetchByQuery(query);
if (posts.Count > 0)
return PostCollection.FetchByQuery(query)[0];
else
return null;
}
private Post GetNextPost(Post post)
{
Table table = new Table("graffiti_Posts", "PostCollection");
Query query = new Query(table);
query.Top = "100 PERCENT *";
Column idColumn = new Column("Id", DbType.Int32, typeof(Int32), "Id", false, true);
query.OrderByAsc(idColumn);
query.AndWhere(idColumn, post.Id, Comparison.GreaterThan);
Column isPublishedColumn = new Column("IsPublished", DbType.Boolean, typeof(bool),
"IsPublished", false, false);
query.AndWhere(isPublishedColumn, false, Comparison.NotEquals);
PostCollection posts = PostCollection.FetchByQuery(query);
if (posts.Count > 0)
return PostCollection.FetchByQuery(query)[0];
else
return null;
}
Here I have to thank Scott for helping me on this part of the code. After getting his help on applying Graffiti classes I still had a problem in selecting data so had to go back and forth and debug the code to find what's the exact reason. As you see I used an uncommon code for the Top property of Query class to solve an issue that I think it's related to Graffiti API. I will begin a private conversation with Scott about this after this post.
But how these methods are written? First I created a Table object for graffiti_Posts table to select data from it. Then I created a Query object for this table which adds a WHERE clause to the select method in order to filter posts with smaller IDs or larger IDs than the ID of the post. The other filter is for published posts and finally a call to PostCollection.FetchByQuery for the first item in the list always returns our desire post.
The Top property of the Query class is something to select top items in the list but I modified the default "100 PERCENT" value to be able to use the Query object and solve an issue.
The last piece of code is for GetHtmlCode function which returns the string value of the HTML output. I don't step in its details since it's nothing more than some case statements to decide what to add to the output.
private string GetHtmlCode(string separator, string previousText,
string nextText, string previousAppendText, string nextAppendText)
{
string result = string.Empty;
try
{
if (this._previous != null)
{
string linkText = this._previous.Title;
if (!string.IsNullOrEmpty(previousText))
linkText = previousText;
result += string.Format(linkFormatPrevious,
new Macros().FullUrl(this._previous.Url),
linkText, previousAppendText, this._previous.Title);
}
if ((this._previous != null) && (this._next != null))
result += separator;
if (this._next != null)
{
string linkText = this._next.Title;
if (!string.IsNullOrEmpty(nextText))
linkText = nextText;
result += string.Format(linkFormatNext,
new Macros().FullUrl(this._next.Url),
linkText, nextAppendText, this._next.Title);
}
}
catch (Exception ex)
{
Log.Error("Post Navigator Extension", ex.Message + ex.StackTrace);
return string.Empty;
}
return result;
}
You can download the binary and source code of Post Navigator extension for Graffiti from here.
Like all other Graffiti extensions you just need to copy the binary DLL file of the extension to the bin folder on your server and modify your theme files to use this extension.
As I outlined where was talking about the implementation, there are two ways to use this extension in your themes. After applying your own markup for the container section of the navigator you can use one of these approaches.
The first way is easier. You pass the post as well as two string values for the previous and next link texts. Here is an example:
$postNavigator.GetNavigator($post, "Previous", "Next")

The second way is a customized one and an example is presented here:
$postNavigator.GetNavigator($post, true, true, " | ", "", "", "« ", " »")

As the last note I have to say that you can pass many things as parameters to this extension like the HTML code that displays an image rather than a text value for the previous and next links or string value of the HTML code to link to the homepage of the site rather than a simple text separator.
Muhammad Suhada
Jan 13, 2008 9:33 PM
#
Great work Keyvan
Jon Sagara
Jan 14, 2008 3:36 PM
#
Thanks, Keyvan. This example was just what I needed to get started on my "Archives" widget. You can see a preview of it here:
sagara.net/.../new-widget-prev
Jacques
Apr 01, 2008 5:51 PM
#
Hi, I get an error that seems to be related to the reserved word "PERCENT" (used in your C# code for the PostNavigator) and the VistaDb provider. I can send you the complete error log if you want.
Installing Graffiti Extras
Dec 08, 2008 2:08 PM
#
I’ve been wanting to add Next/Previous links to my blog’s posts to provide easier navigation for folks who come to the blog and find themselves somewhere in the middle of it.  My goal is for the top and/or bottom of each post to have something like
Steve Smith
Dec 08, 2008 3:34 PM
#
Keyvan fixed the PERCENT issue with VistaDB today. More here: stevesmithblog.com/.../installing-graf
Keyvan Nayyeri
Dec 08, 2008 3:39 PM
#
@Steve
Thank you for the update :-)
Michael E
Jan 05, 2010 1:21 PM
#
Is there any other way to get the previous and next posts by Category or Parent Category and not by Date?
Leave a Comment