Writing an Archive Calendar, part 4b

Man, paging — the second bug I'd reported to myself in part 4 — was long-winded, mainly because I was trying to use the built-in paging system and spent far too much time tracing through Graffiti code in Reflector. In the end, I abandoned that line of attack, mainly because I think I'd been gaming GraffitiCMS by creating a "post" called archive, when it was really a list of posts, and the auto-generated default.aspx was all wrong for that purpose. Further investigation will wait for a rainy day.

Anyway, I decided to write the paging essentially from scratch using ideas I'd gathered from my spelunking through the Graffiti.Core.dll.

First off, there's this nice class called GraffitiContext. It's a hash map, a set of name/value pairs containing some well-defined ones. Since it's a hash map you can add your own if you want (and I will). It stays valid throughout the current ASP.NET cycle. One of the more interesting name/value pairs is the "request" key, whose value is the HTTP request that started this cycle off. Included in that is the QueryString property, which I'll be reading for the page number.

    private static GraffitiContext GetContext(PostCollection posts) {
      GraffitiContext gc = GraffitiContext.Current;
      HttpRequest request = (HttpRequest)gc["request"];
      if (request != null)
        gc.PageIndex = int.Parse(request.QueryString["p"] ?? "1");
      gc.PageSize = SiteSettings.PageSize;
      gc.TotalRecords = posts.Count;
      gc["where"] = archiveWhere;
      return gc;
    }

First I read the QueryString for the "p" parameter, the page number. If it's not there, I assume it's page 1. Next I load up the page size and the total number of records: these will be used by the page navigator in a moment. For fun, I set the "where" property to my own value (actually "archive").

Next up is another little helper method. This one uses the values in the current GraffitiContext to create a PostCollection containing the posts for that particular page. We'll be returning that page of posts to the view.

    private static PostCollection GetPageOfPosts(PostCollection posts, GraffitiContext gc) {
      int start = (gc.PageIndex - 1) * gc.PageSize;
      int end = start + gc.PageSize;
      if (end > gc.TotalRecords) end = gc.TotalRecords;

      PostCollection postsPage = new PostCollection();
      for (int i = start; i < end; i++) {
        postsPage.Add(posts[i]);
      }
      return postsPage;
    }

Nothing too difficult.

Now we can write the improved Chalk extensions to return a page of posts instead of the full list.

    public PostCollection GetPostsForMonth(string yearAsString, string monthAsString) {
      int year, month;
      GetDateParts(yearAsString, monthAsString, out year, out month);
      PostCollection posts = PostsReader.GetFilteredPostsForMonth(year, month);

      GraffitiContext gc = GetContext(posts);
      gc["title"] = "Archives for " + new DateTime(year, month, 1).ToString("MMMM yyyy") + " : " + SiteSettings.Get().Title;
      gc[archiveQuery] = string.Format("?year={0}&amp;month={1}", year, month);

      return GetPageOfPosts(posts, gc);
    }

    public PostCollection GetPostsForDay(string yearAsString, string monthAsString, string dayAsString) {
      int year, month, day;
      GetDateParts(yearAsString, monthAsString, dayAsString, out year, out month, out day);
      PostCollection posts = PostsReader.GetFilteredPostsForDate(year, month, day);

      GraffitiContext gc = GetContext(posts);
      gc["title"] = "Archives for " + new DateTime(year, month, day).ToShortDateString() + " : " + SiteSettings.Get().Title;
      gc[archiveQuery] = string.Format("?year={0}&amp;month={1}&amp;day={2}", year, month, day);

      return GetPageOfPosts(posts, gc);
    }

They both work in the same way: validate the input, get the full list of filtered posts, get and set the context (we add a better title, and we add the current query string as a special property of the context), and then we call the method to return the correct page of posts. (You can tell I'm tired, by the way. I can't be bothered at the moment to work out how to extract out the duplication.)

That's it for the generation of the pages of posts, for both a given month and for a specific date; now onto the pager.

Unfortunately $macros.pager won't do here. The archive calendar is already adding query strings to the URL and the standard pager doesn't expect that. So I had to write a simple pager, based on the interface of the  normal one.

    public static string Pager(string cssClass, string prevText, string nextText) {
      GraffitiContext gc = GraffitiContext.Current;
      return Util.Pager(gc.PageIndex, gc.PageSize, gc.TotalRecords, cssClass, (string)gc[archiveQuery], prevText, nextText);
    }

The main difference is the extraction of the current query string from the context where I'd stored it in either GetPostsForMonth() or GetPostsForDay(). If there is a current query string, Util.Pager() will just tack the page parameter bit on to the end.

The only change I had to the archive.view file was to call my pager instead of $macros.pager.

        $JmbChalk.Pager("pager", "&laquo; older posts", "newer posts &raquo;")

And that's it. A paged archive list.

(Part 1 is here, part 2 here, part 3 here, part 4 here, part 4a here, part 4b here.)

Now playing:
Pet Shop Boys - The Boy Who Couldn't Keep His Clothes On
(from Further listening 1995-1997)



Posts on similar topics...

Share it: Digg It!  StumbleUpon  Reddit  Del.icio.us  NewsVine  Furl  BlinkList  Ma.gnolia  Technorati

1 Response

  • Sat 17 Jan 2009
  • 7:26 PM
  •  avatar #1

Dew Drop – January 17, 2009 | Alvin Ashcraft's Morning Dew said...

Pingback from Dew Drop – January 17, 2009 | Alvin Ashcraft's Morning Dew

Leave a Response

About Me

I'm Julian M Bucknall, the M because it's my middle initial and because I and the other Julian Bucknall (the movie guy) would like to differentiate ourselves.

I'm a programmer by trade, an actor by ambition, and an algorithms guy by osmosis. I write articles for PCPlus in my spare time, not that there's much of that.

Julian M Bucknall Apart from that, an ex-pat Brit, atheist, microbrew enthusiast, Pet Shop Boys fanboy, slide rule and HP calculator collector, amateur photographer, Altoids muncher.

DevExpress

I'm Chief Technology Officer at Developer Express, a software company that writes some great controls and tools for .NET and Delphi. I'm responsible for the technology oversight and vision of the company.

The OUT Campaign

The OUT Campaign

Validation

Valid XHTML 1.0 Transitional     Valid CSS!

Bottom swirl

Archives

February 2010 (2)
SMTWTFS
« Jan  
123456
78910111213
14151617181920
21222324252627
28

Like this Archive Calendar widget? Download it here.

Search

Google ads

My Tweets

  • It seems replacing all our windows will take a mere couple of days. Next Monday/Tuesday will be interesting: it'll drive me crazy.
  • Every now and then, you have a day where nothing goes right. That was today. Bah.
  • White paper written. Now to write the exemplar application
Bottom swirl