Blog archives stuff everything imported in month of import, not actual month (Orchard 1.6)

Mar 7, 2013 at 8:54 PM
Anyone else try to use this module to import content from an existing blog on Orchard 1.6 ? I'm successfully importing everything, but Orchard reports everything was posted in the month of the import rather than the correct month. Unpublishing/publishing the post fixes that specific post, but when you're importing hundreds of posts, this isn't a feasible option.
Mar 12, 2013 at 1:48 PM
Scenario: Running Orchard 1.6 and migrating from another engine (SubText) using the BlogML format & the BlogML module.

The module was written for Orchard 1.4 and from a few I talked to that used it, it worked fine. However I'm assuming something changed in the blog logic from Orchard 1.4 > 1.6. Basically when a blog post is published, it fires a handler that uses the current timestamp as the publish date which also is used for updating the archive month counters. Immediately after this, the BlogML module changes the Publish date so it looks correct in the UI & DB, but the counters are already set.

Changeset b486d94960b6 that added a command to the Orchard.Blogs module, but I had a bunch of issues with it. Not entirely sure how to comment / update a changeset, so posting my findings & fix here. First, here are the issues I found and how to address them. Then I'm posting the code from my replaced ArchiveService.cs file:
  • In the original file, lines 25 & 31 were getting the oldest & news blog posts in the DB, but not filtering based on the actual blog specified (see my introduction of a WHERE clause on lines 26 & 32).
  • For some reason, I was getting a ton of errors on line 61 in the changeset… NHibernate/LINQ couldn’t evaluate the x.CreatedUtc.Value property. Thus, I changed it and created (my file, lines 57-66) a generic list that had all the dates of each blog post & updated that line in the where clause to look at my generic list rather than querying the DB for counts. For me, this will result in 1 big call to the DB and the creation of a 1,500 item list (# of blog posts) vs. 120 DB count calls (I’ve been blogging for 10 years). Since it’s only done one time updating the archive… I’m not concerned with perf.
  • Issue in calculating the FROM & TO date range (original changeset lines 59 & 60). You’ll see my change in lines 70-72. Basically adding a month to the UTC converted date does just that… adds a month… but it might not jive with the timezone. For instance I had posts being missed on 12/31/2003. The reason is that it would use the start range of timezone formatted (I’m GMT -0500) of 11/30/2003 19:00:00 (-5hrs from 11/31/2003 00:00:00). When the changeset figured the end range (line 60), it resulted in 12/30/2003 19:00:00… but it should have been the 31st. The FIX: do the calculation before converting from UTC (see my line 72).
  • Another issue was that I didn’t want leading/trailing zero months. For instance, I started blogging in September 2003. The changeset would have put zero counters for January – August 2003. Same is true on the back side (I’m importing to March 2013, but I don’t want zero’d counters from April – December 2013). I addressed this in lines 74-81.
  • Finally, the way values were added to the DB caused some off by one dates. For instance using the year & month from the TO & FROM variables meant that it was off due to the timezone change. I fixed this by using the indexes of the for loops.
My ArchiveService.cs fixed file (this should overwrite the file in the changeset:
using System;
using System.Collections.Generic;
using System.Linq;
using Orchard.Blogs.Models;
using Orchard.ContentManagement;
using Orchard.Core.Common.Models;
using Orchard.Data;

namespace Orchard.Blogs.Services {
  public class ArchiveService : IArchiveService {
    private readonly IRepository<BlogPartArchiveRecord> _blogArchiveRepository;
    private readonly IContentManager _contentManager;
    private readonly IWorkContextAccessor _workContextAccessor;

    public ArchiveService(
        IRepository<BlogPartArchiveRecord> blogArchiveRepository,
        IContentManager contentManager,
        IWorkContextAccessor workContextAccessor) {
      _blogArchiveRepository = blogArchiveRepository;
      _contentManager = contentManager;
      _workContextAccessor = workContextAccessor;

    public void RebuildArchive(BlogPart blogPart) {

      var first = _contentManager.Query<BlogPostPart>().Where<CommonPartRecord>(bp => bp.Container.Id == blogPart.Record.Id).OrderBy<CommonPartRecord>(x => x.CreatedUtc).Slice(0, 1).FirstOrDefault();

      if (first == null) {

      var last = _contentManager.Query<BlogPostPart>().Where<CommonPartRecord>(bp => bp.Container.Id == blogPart.Record.Id).OrderByDescending<CommonPartRecord>(x => x.CreatedUtc).Slice(0, 1).FirstOrDefault();

      DateTime? start = DateTime.MaxValue;
      if (first.As<CommonPart>() != null) {
        start = first.As<CommonPart>().CreatedUtc;

      DateTime? end = DateTime.MinValue;
      if (last.As<CommonPart>() != null) {
        end = last.As<CommonPart>().CreatedUtc;

      // delete previous archive records
      foreach (var record in _blogArchiveRepository.Table.Where(x => x.BlogPart == blogPart.Record)) {

      if (!start.HasValue || !end.HasValue) {

      // get the time zone for the current request
      var timeZone = _workContextAccessor.GetContext().CurrentTimeZone;

      // build a collection of all the post dates
      List<DateTime> blogPostDates = new List<DateTime>();
      var blogPosts = _contentManager.Query<BlogPostPart>().Where<CommonPartRecord>(bp => bp.Container.Id == blogPart.Record.Id);
      foreach (var blogPost in blogPosts.List()) {
        if (blogPost.As<CommonPart>() != null)
          if (blogPost.As<CommonPart>().CreatedUtc.HasValue) {
            DateTime timeZoneAdjustedCreatedDate = TimeZoneInfo.ConvertTimeFromUtc(blogPost.As<CommonPart>().CreatedUtc.Value, timeZone);

      for (int year = start.Value.Year; year <= end.Value.Year; year++) {
        for (int month = 1; month <= 12; month++) {
          DateTime fromDateUtc = new DateTime(year, month, 1);
          var from = TimeZoneInfo.ConvertTimeFromUtc(fromDateUtc, timeZone);
          var to = TimeZoneInfo.ConvertTimeFromUtc(fromDateUtc.AddMonths(1), timeZone);

          // skip the first months of the first year until a month has posts
          //  for instance, if started posting in May 2000, don't write counts for Jan 200 > April 2000... start May 2000
          if (from < TimeZoneInfo.ConvertTimeFromUtc(new DateTime(start.Value.Year, start.Value.Month, 1), timeZone))
          // skip the last months of the last year if no posts
          //  for instance, no need to have archives for months in the future
          if (to > end.Value.AddMonths(1))

          //var count = _contentManager.Query<BlogPostPart>().Where<CommonPartRecord>(x => x.CreatedUtc.Value >= from && x.CreatedUtc.Value < to).Count();
          var count = blogPostDates.Where(bp => bp >= from && bp < to).Count();

          var newArchiveRecord = new BlogPartArchiveRecord { BlogPart = blogPart.Record, Year = year, Month = month, PostCount = count };