An Alternative Archives Web Part to Solve the Pre-dated Posts Provisioning in the OOTB Blog Site Archives Web Part

imageThe OOB SharePoint 2010 site templates are very useful in different scenarios. The guys at Microsoft were very generous to spare some time and build a site template for blogs. After provisioning the site, I was so happy with it! but then stumbled on the fact that the Archives Web Part did not generate links for my old posts, apparently by design – after doing a decent search over forums – pre dated posts were not provisioned by the Blog Site Template Archives Web Part. I had to remove the Web Part and place a link to the All Posts view on the top link bar and called it Archives. On the long run, it did not sound like a good alternative, I had to have that Web Part.


I spent couple of hours today to code the Archives Web Part, and I can’t express how happy I am with it. The approach was very simple: I had to get all published posts; find out in which years the posts were authored; find out in which months the posts were authored, and finally push all this to a Tree View control.

After populating the Tree View nodes, I had to handle the SelectedNodeChanged event to redirect to the Date.aspx page and pass the correct StartDateTime and EndDateTime query string parameters. The query string structure is fairly easy, you needed to supply the time span parameters and the title. For example, to get all the posts in December 2011 the URL should look like this:,%202011. Notice that the start and end times are passed in ISO8601 formats.

Web Part Download (WSP)
If you are not a technical blogger and just want to download the Web Part, click here. You don’t have to complete this post. On the other hand, if you are a SharePoint developer like me, get your fingers cracking for some code.
Web Part Development
I created a Visual Web Part. The Web Part had 3 controls:
   1: <SharePoint:CssRegistration 
   2:     Name="<% $SPUrl:~sitecollection/_layouts/styles/SharePointStack/SPBlogTemplate/SPBlogTemplate.css %>" 
   3:     After="CoreV4.css" runat="server"></SharePoint:CssRegistration>
   4: <div id="SPBlogContainer">
   5:     <div id="ArchiveSummaryTitle"><a id="ViewArchive" title="Click to view Archive" runat="server">Archives</a></div>
   6:     <asp:TreeView ID="ArchiveSummaryTree" runat="server" ExpandDepth="0" 
   7:         NodeIndent="0" onselectednodechanged="ArchiveSummaryTree_SelectedNodeChanged" ></asp:TreeView>
   8:     <asp:Literal ID="ArchiveSummaryErrors" runat="server"></asp:Literal>
   9: </div>

The markup is straight forward: I place a title, Tree View, and a Literal. I had to apply some CSS formatting to make sure that the Web Part looks like a SharePoint control. I created a CSS file and placed it under the STYLES mapped folder. The CSS helped me adjust the padding and text formatting.

   1: #ArchiveSummaryTitle
   2: {
   3:     color: #0072bc;
   4:     font-size: 1.2em;
   5:     font-weight: normal;
   6: }
   8: #SPBlogContainer
   9: {
  10:     padding-left: 11px;
  11: }
Before moving to the code behind, I needed to create a class to help handle the posts using a Generic List. More details on this later on.
   1: namespace SharePointStack.SPBlogTemplate
   2: {
   3:     class SPBlogPost
   4:     {        
   5:         public SPBlogPost(string title, string month, string year)
   6:         {
   7:             postTitle = title;
   8:             publishingMonth = month;
   9:             publishingYear = year;
  10:         }
  12:         private string postTitle;
  14:         public string PostTitle
  15:         {
  16:             get { return postTitle; }
  17:             set { postTitle = value; }
  18:         }
  20:         private string publishingMonth;
  22:         public string PublishingMonth
  23:         {
  24:             get { return publishingMonth; }
  25:             set { publishingMonth = value; }
  26:         }
  28:         private string publishingYear;
  30:         public string PublishingYear
  31:         {
  32:             get { return publishingYear; }
  33:             set { publishingYear = value; }
  34:         }
  35:     }
  36: }
Now let’s get some Code Behind love Smile I commented wherever needed to explain the code as much as possible.
   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Web.Caching;
   5: using System.Web.UI;
   6: using System.Web.UI.WebControls;
   7: using Microsoft.SharePoint;
   8: using Microsoft.SharePoint.Utilities;
  10: namespace SharePointStack.SPBlogTemplate.ArchiveSummary
  11: {
  12:     public partial class ArchiveSummaryUserControl : UserControl
  13:     {
  14:         static object _lock =  new object();
  15:         List<SPBlogPost> postsBuffer = new List<SPBlogPost>();
  16:         List<int> years = new List<int>();
  17:         string[] months = { "January", "February", "March", "April", "May", "June", "July", "August", 
  18:                               "September", "October", "November", "December" };
  19:         bool duplicates;
  21:         protected void Page_Load(object sender, EventArgs e)
  22:         {
  23:             if (!Page.IsPostBack)
  24:             {
  25:                 try
  26:                 {
  27:                     //I chose "using" to automatically dispose SharePoint objects
  28:                     using (SPSite blogSiteCollection = SPContext.Current.Site)
  29:                     {
  30:                         using (SPWeb blogWeb = blogSiteCollection.OpenWeb(SPContext.Current.Web.ServerRelativeUrl))
  31:                         {
  32:                             SPList blogPosts = blogWeb.Lists["Posts"];
  34:                             //Set the header link
  35:                             ViewArchive.HRef = blogPosts.DefaultViewUrl;
  37:                             SPQuery queryPosts = new SPQuery();
  39:                             //CAML Query that returns only published posts and orders them by date
  40:                             queryPosts.Query = "<OrderBy><FieldRef Name='PublishedDate'/></OrderBy>" + 
  41:                                                 "<Where><Eq><FieldRef Name='_ModerationStatus' />" + 
  42:                                                 "<Value Type='ModStat'>0</Value></Eq></Where>";
  44:                             //Pull the query results directly from the Cache, we don't want to query
  45:                             //SharePoint everytime the Archives Summary Web Part runs. This practice increases 
  46:                             //the performance and comes very handy if the user is an active blogger.
  47:                             SPListItemCollection publishedPosts = (SPListItemCollection)Cache["PublishedPosts"];
  49:                             //If the query results are not avilable in the Cache, then we need to execute the query and
  50:                             //store the results in the Cache for the next request.
  51:                             //The Cache is set with Sliding Expiration of 1 day.
  52:                             if (publishedPosts == null)
  53:                             {
  54:                                 //Since SPWeb is not thread safe, we need to place a lock.
  55:                                 lock (_lock)
  56:                                 {
  57:                                     //Ensure that the data was not loaded by a concurrent thread while waiting for lock.
  58:                                     publishedPosts = blogPosts.GetItems(queryPosts);
  59:                                     Cache.Add("PublishedPosts", publishedPosts, null, Cache.NoAbsoluteExpiration, 
  60:                                         TimeSpan.FromDays(1), CacheItemPriority.High, null);
  61:                                 }
  62:                             }
  64:                             //Load all published posts into the postsBuffer. The query results will not be available
  65:                             //outside the "using" block.
  66:                             foreach (SPListItem post in publishedPosts)
  67:                                 postsBuffer.Add(new SPBlogPost(post["Title"].ToString(), 
  68:                                     DateTime.Parse(post["PublishedDate"].ToString()).Month.ToString(), 
  69:                                     DateTime.Parse(post["PublishedDate"].ToString()).Year.ToString()));
  70:                         }
  71:                     }
  73:                     //Provision years
  74:                     foreach (SPBlogPost post in postsBuffer)
  75:                         years.Add(int.Parse(post.PublishingYear));
  77:                     //Make sure we only have distinct years that are sorted in descending order
  78:                     var yearsList = years.Distinct().ToList();
  79:                     yearsList.Sort();
  80:                     yearsList.Reverse();
  82:                     //Add the years to the Tree View
  83:                     foreach (int year in yearsList)
  84:                         ArchiveSummaryTree.Nodes.Add(new TreeNode(year.ToString()));
  86:                     //Find out which months have posts in each year and add them to the Tree View as ChildNodes
  87:                     foreach (TreeNode year in ArchiveSummaryTree.Nodes)
  88:                     {
  89:                         for (int i = 12; i >= 1; i--)
  90:                         {
  91:                             foreach (SPBlogPost post in postsBuffer)
  92:                             {
  93:                                 if (post.PublishingMonth == i.ToString() && post.PublishingYear == year.Text)
  94:                                 {
  95:                                     duplicates = false;
  96:                                     foreach (TreeNode item in year.ChildNodes)
  97:                                     {
  98:                                         //Check for any duplicate month entries.
  99:                                         //This will become an issue if the user posts more than one post in a month time.
 100:                                         if (item.Text.ToLower() == months[int.Parse(post.PublishingMonth) - 1].ToLower())
 101:                                             duplicates = true;
 102:                                     }
 103:                                     if (!duplicates)
 104:                                         year.ChildNodes.Add(new TreeNode(months[int.Parse(post.PublishingMonth) - 1], 
 105:                                             post.PublishingMonth, null, null, null));
 106:                                 }
 107:                             }
 108:                         }
 109:                     }
 111:                     //Expand the latest year node
 112:                     if (ArchiveSummaryTree.Nodes[0] != null)
 113:                         ArchiveSummaryTree.Nodes[0].Expand();
 114:                 }
 115:                 catch (Exception ex)
 116:                 {
 117:                     //Print the error messege to the user using a Literal
 118:                     ArchiveSummaryErrors.Text = "<img src='" + SPContext.Current.Site + 
 119:                         "_layouts/images/SharePointStack/SPBlogTemplate/error.png' alt='Error' style='display: block;' />" + 
 120:                         "&nbsp;<font color='#ff0000' size='12'>" + ex.Message + "</font>";
 121:                 }
 122:                 finally
 123:                 {
 124:                     //Objects will be disposed during the next Garbage Collector run
 125:                     postsBuffer = null;
 126:                     years = null;
 127:                 }
 128:             }
 129:         }
 131:         protected void ArchiveSummaryTree_SelectedNodeChanged(object sender, EventArgs e)
 132:         {
 133:             TreeView summaryLinks = (TreeView)sender;
 135:             //Make sure that this is a month node, we don't want to handle a year node selection change
 136:             if (summaryLinks.SelectedNode.Parent != null)
 137:             {
 138:                 string month = string.Empty, year = string.Empty;
 139:                 month = summaryLinks.SelectedNode.Value;
 140:                 year = summaryLinks.SelectedNode.Parent.Value;
 142:                 //Build the date strings to be converted later on to ISO8601 dates
 143:                 string startDate = month + "/1/" + year;
 144:                 string endDate = startDate;
 146:                 //Convert the dates and set the range to be a one month time span
 147:                 startDate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Parse(startDate));
 148:                 endDate = SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Parse(startDate).AddMonths(1));
 150:                 //Build the URL and set the Query String parameters for redirection to the Date.aspx page
 151:                 string url = SPContext.Current.Site + SPContext.Current.Web.ServerRelativeUrl + 
 152:                     "/Lists/Posts/Date.aspx?StartDateTime=" + startDate + "&EndDateTime=" + endDate + 
 153:                     "&LMY=" + months[int.Parse(month) - 1] + ", " + year;
 155:                 summaryLinks.Dispose();
 157:                 //SharePoint will add "SPSite Url=" to the URL we built. I used Substring to remove it.
 158:                 Response.Redirect(url.Substring(11));
 159:             }
 160:         }
 161:     }
 162: }

Web Part Source Code Download

Well that’s about it Smile If you want the source code you can download it here.
Please don’t hesitate to contact me if you have any questions.
Comments (34)

  1. Anonymous says:

    Super 🙂

  2. Anonymous says:

    Just tried it and it works


  3. I am glad you like it Jorgan 🙂

  4. Anonymous says:

    any plans for implementing in sandbox?

  5. In VS11 you can build Visual WebParts to run with the Sandbox subset. No need for rewriting the WebPart 🙂

  6. Anonymous says:

    I installed your webpart and it is not working for me.  I receive a 404 error when clicking on the tree nodes for archived months.

  7. Hi Charles, Can you please post the blog site URL and the URL you are redirected to by the tree nodes (404)?  I think you are being redirected to a wrong URL.

  8. Anonymous says:

    Thanks for the response.  The blog site is on a site behind our company firewall.  The url of the blog is the same after clicking on the archive link as shown in the pics.…/photostream…/photostream

  9. Hi Charles, I need the URL or at least a hint on how it looks before you click and how it becomes after you click and get the 404 from the address bar, not the status bar please 🙂

  10. Anonymous says:

    I can't seem to get this to work. I installed the webpart and made posting dated "back in time". As I understand the tree view is then supposed to create the months automatically, but that doesn't happen. What am I doing wrong?

  11. Hi Kathrine, Can you please send a screenshot? As you can see in my code above, I query the Posts list for any Published Posts regardless when they where posted. Are these posts published?

  12. Anonymous says:

    Hello Bandar

    I just went in to look at bit more at the issue and found that it had picked up the postings I did yesterday. Do you have any idea would could cause that?

    Please let me know, if a screendump would still help after this piece of information.

    Thank you for the great work on this webpart and your help.

  13. No worries Kathrine 🙂

    When I query the Posts list, I check first if it is available in the cache:

    SPListItemCollection publishedPosts = (SPListItemCollection)Cache["PublishedPosts"];

    and If it is not then I execute the query and push it in the cache which lives for one day:

    publishedPosts = blogPosts.GetItems(queryPosts);

    Cache.Add("PublishedPosts", publishedPosts, null, Cache.NoAbsoluteExpiration, TimeSpan.FromDays(1), CacheItemPriority.High, null);

    So every time you add a post, you will have to wait till the next day, or you can execute an IISReset 🙂 and if you like coding you can always amend the source code and remove the cache logic or minimize the cache life. The code is well documented so it will be fairly an easy task.

  14. Anonymous says:

    Hello Bandar

    That makes sense – Thank you! 🙂



  15. You are most welcome 🙂

  16. Anonymous says:

    Hello again

    We have now deployed the webpart in our production environment and somehow it isn't working as it should. The webpart displays the months as it should, but when clicking on each month it still shows all posts, instead of the posts from that specific month. I have checked the url and compared it to the url in the test enviroment and the both look like this:

    Test environment (that works)


    Production (Shows all posts)


    I am really puzzled as to why this isn't working…

    Can you help out?

    Thank you


  17. Anonymous says:

    I am trying to deploy the web part but i do not know how can i use it

  18. Anonymous says:

    Sorry – scratch my last post – it seems to be working now. Thanks.


  19. Anonymous says:

    Sorry – me again! It seems to be working insofar that the list of years and months is produced. However when one clicks on an entry no posts are displayed – this is because the StartDateTime and EndDateTime URL paremeters appear to be wrong:


    Note that start is listed as 2012-01-11 and the end is 2012-02-11 whereas they should be 2012-11-01 and 2012-11-30. This looks like a localisation issue perhaps (I'm in the UK)?



  20. Anonymous says:

    Hi! I need help deploying this. Can this be deployed at the site level or must it be deployed at the farm level only? Thank you!

  21. Anonymous says:

    I am trying to deploy this webpart. Can it be deployed at the site level or must it be deployed at the farm level?

  22. Hi Dave,

    Does it work if you flip the day and month in the query string? If yes, then I guess its related to the culture settings, never thought if that when I wrote the web part loool.

    Please let me know what you find out.

  23. Hi Matt,

    Unfortunately no, this is a farm solution with a feature on the collection level to activate/deactivate. A quick work around would be using the VS2012 Visual WebPart template which can be deployed later as a Sandbox solution.

    Please let me know if you need support with it.

  24. Anonymous says:

    is it possible in share point 2013 by out of box

  25. Thank for this.

    I just fixed some translation issues with this webpart and submitted patch to codeplex. I hope you can apply patch and make it available as new version.

    I'm just noob  with c# and web parts, but lets hope that patch is fine. 😉  

  26. Anonymous says:

    does this one work on SP2013? If so, can someone post a guide how to install it.

  27. Anonymous says:


    This solution seems to be very useful. How can we add this code into SharePoint 2013? Is it possible to do theese thing in SharePoint 2013? İf possible how? Can you help me please?

  28. BALSHARFI says:

    Yes, it should work in SP2013. It's just aggregating list items and rendering the content. What do you need help with deployment? Do you have specific questions or just never deployed farm solutions before? In both cases I am here 🙂

  29. Anonymous says:

    Same issue here. SP13 seems to not like the wsp file. I am getting this message:

    "The file you imported is not valid. Verify that the file is a Web Part description file (*.webpart or *.dwp) and that it contains well-formed XML."

  30. BALSHARFI says:

    You can't use the same WSP file, you will have to download the source, reference the right assemblies, and then package. Code will work of course, not changes needed. If you can't do it, then I will do it over the weekend and add it to the post. Let me know if you want me to 🙂

    1. Sam Pinizzotto says:

      Any Chance you did this for SP2013 or SP Online?
      This is exactly what I need…
      Please help?

  31. Anonymous says:


    Did you ever got the chance to repackaged so it can be use with SharePoint 2013?  This can be very useful for those migrating blogs from different sources to SP.

    Great Work!


  32. Anonymous says:


    I want to create archive like this–

    November 2007 (2)

    February 2006 (1)… but i don't have Visual studio, so How can create this kind archive with SharePoint Designer only, please suggest.



  33. Anonymous says:

    Hi Bandar,

    your webpart looks exactly like what I've been looking for since ages. Unfortunately I only have Office365's sharepoint, and no clue on how to create a new webpart from code (and no Visual Studio either).

    Could you please please please 🙂 supply the SP13 packaged version you were talking about?

    Thank you very much