My epic journey from Blogger to Orchard

July 21, 2014

Latest Post The Hound of the Baskervilles by Owen Soule

My epic journey from Blogger and a home rolled portfolio site to a single Orchard site is finally complete. It started months ago when I decided to update the look and feel of my site. I wanted to go with something more mobile friendly and a bit more modern, after researching the options I landed on Twitter’s Boostrap front end framework because it is easy to use and works well. Also, I wasn’t trying to get into a ton of coding because I do enough of that in my day job ;-) Somehow my research into Bootstrap lead me to Orchard, likely because a Boostrap theme already existed for use within Orchard, and I hatched a plan to centralize all my content on one site! MUHAHAHA!

Orchard Logo
Peep that logo, yo!

So what the heck is Orchard? Orchard is a content management system (CMS) built using Microsoft’s ASP.net MVC technology. This seemed like a perfect fit because my aforementioned day job uses this exact technology, so in theory it should have been a quick learning curve. As it turned out the curve wasn’t so quick, and my attempts to avoid reading any Orchard documentation only lead to more issues, so I had to bite the bullet and really immerse myself in the software. Without going into too much detail I’ll try to outline the process of importing Blogger data into the Orchard system, converting my Blogger images into Orchard Media Library files, and finally building my customized Boostrap/Orchard theme. Let’s look at Blogger data conversion first, shall we?

Converting Blogger Data

Turns out there is an Orchard module to import data into the blog called External Import/Export, hurrah! However, it doesn’t work with the Blogger Atom export format, boo!

Export Blogger Blog as Blogger Atom
Exporting data from Blogger is easy, open the Settings > Other panel and click Export Blog

There are tools around to convert Blogger Atom to Blog ML format, which the External Import/Export module will gladly accept, however when I tried it my data always got jacked up. Since I was already down the Orchard hole, having spent some portion of my life reading documentation, I decided to “modify” (read “hack”) the External Import/Export module to work directly with Blogger Atom format.   

After much frustration and many database restores I got a Blogger provider written and working. There are a fair amount of kludges in the blog assembler, like hardcoded “www.souledesigns.com” and “Owen Soule”s peppered around, but it does work. Anyone interested in the code can find the updated module here. Obviously updates will be required to get it working with your data (replacing hard coded crap, etc.). It requires Google’s GData C# runtime too which can be found here.

Ok, while that was a bit of a pain, still not too bad, right? Well, wrong. Even with the data imported, there was a fair amount of back end tweaking to get back dated posts working, below is the basics though there may have been a bit more that I didn’t save:   

DECLARE @BlogContentTypeId INT  DECLARE @ContainerId INT
-- Get blog container, will only get the first one! SELECT TOP 1 @BlogContentTypeId = Id FROM dbo.Orchard_Framework_ContentTypeRecord WHERE Name = 'Blog' SELECT TOP 1 @ContainerId = Id FROM dbo.Orchard_Framework_ContentItemRecord WHERE ContentType_id = @BlogContentTypeId
--Blog Date updates on common part UPDATE dbo.Common_CommonPartRecord SET CreatedUtc = ModifiedUtc, PublishedUtc = ModifiedUtc WHERE Container_Id = @ContainerId
--Blog Date updates on common part version (overkill?) UPDATE CPVR SET CreatedUtc = CPR.ModifiedUtc, PublishedUtc = CPR.ModifiedUtc FROM dbo.Common_CommonPartVersionRecord CPVR INNER JOIN dbo.Common_CommonPartRecord CPR ON CPR.Id = CPVR.ContentItemRecord_Id WHERE Container_Id = @ContainerId
-- Make Blog comments active, this is optional UPDATE CPR SET CommentsActive = 1, CommentsShown = 1 FROM dbo.Orchard_Comments_CommentsPartRecord CPR INNER JOIN dbo.Common_CommonPartRecord CPR2 ON CPR.Id = CPR2.Id AND CPR2.Container_id = @ContainerId

This whole backed dated create date thing seems like an issue when initially publishing a post, either from the Import module or in the UI. Regardless of the create date entered it will publish as the current date. Then subsequent publishes use the create date manually entered. Meh, it was easier for me to hack the database than to investigate in code, however this was in Orchard 1.7 before the introduction of InfoSets so there may be additional steps required to refresh data.

Ok, the last main item was setting up an IIS redirect in the Orchard web.config so any references to my old blog content wouldn’t break. Blogger formats URLs like this (I was using a custom URL instead of the default blogspot one):

http://blog.souledesigns.com/2014/06/peck-peck-peck-progress.html  

In Orchard I opted to use this format for my blog URLs:

https://www.souledesigns.com/blog/2014/06/peck-peck-peck-progress  

With a little research I was able to get the following redirect rule configured and working:

<rule name="2014 Redesign - Blog Subdomain Posts" enabled="true" stopProcessing="true">  <match url=".*" />  <conditions logicalGrouping="MatchAll" trackAllCaptures="true">  <add input="{HTTP_HOST}" ignoreCase="true" pattern="^blog\.souledesigns\.com$" />  <add input="{PATH_INFO}" ignoreCase="true" pattern="^/([0-9]{4})/([0-9]{2})/([a-zA-Z0-9\-]+)\.html$" />  </conditions>  <action type="Redirect" url="https://www.souledesigns.com/blog/{C:1}/{C:2}/{C:3}" />  </rule>  <rule name="2014 Redesign - Blog Subdomain" enabled="true" stopProcessing="true">  <match url=".*" />  <conditions logicalGrouping="MatchAll" trackAllCaptures="false">  <add input="{HTTP_HOST}" ignoreCase="true" pattern="^blog\.souledesigns\.com$" />  </conditions>  <action type="Redirect" url="https://www.souledesigns.com/blog" />  </rule>  

And that is it! It’s that simple to import Blogger data into Orchard. So basically it is on par with configuring an Ubuntu server when you have no Linux experience, or building a Hackintosh with unsupported parts, or any other technical undertaking that winds up sucking away your soul. Yay! Now onto the images, which fortunately were a little easier.

Migrating Images from Blogger to Orchard

I could have retained images hosted on Google+ in my Orchard blog posts and just used the Orchard Media Library moving forward, but that would have been too easy. Speaking of easy, getting media into the library was incredibly easy, simply export the blogger photo album from Google+ and upload the files into Orchard. Literally a 5 minute job.   

download-blogger-photos.png
All your blog photos are belong to us!

The tricky part is updating content to point to the new image locations. In fact I had a few content updates on my plate:

  1. Convert images and anchors that point to Google+ hosted images to point to their new locations within the Orchard Media Library
  2. Remove image dimensions so the images could be responsive and add the Boostrap img-fluid css class
  3. I often link back to blog posts within posts, especially when doing multipart series, so I wanted to fix up those links to avoid relying on on the redirect setup above (not really necessary, but heck I’ve come this far, why not?!)

All this really comes down to parsing HTML content in the dbo.Common_BodyPartRecord table in the Orchard database. To do this I quickly whipped together a C# console app that reads the html record by record, loads it into an HtmlAgilityPack document, and then does some Regular Expression magic on the src and href attributes. Of course! One gotcha I ran into with this was InfoSets do not get updated when you merely update the BodyPartRecord. To hack my way around it I discovered NULL-ing out the Data column in dbo.Orchard_Framework_ContentItemVersionRecord table forces InfoSets to reload from their various tables. This solved my InfoSet dilemma:

/* Clear info sets to force refresh, don't judge me! */  UPDATE C SET Data = NULL  FROM  dbo.Orchard_Framework_ContentItemVersionRecord C  WHERE  Data LIKE '%blog%' AND Data LIKE '%BodyPart%' AND Data IS NOT NULL  

And like that magically my blog was running off locally hosted images. If anyone wants the C# code I can certainly upload to Github, I haven’t upload due to laziness and also authoring this sprawling blog post.

Creating an Orchard Theme

As I mentioned at the start of this post, one bonus with Orchard and my desire to use Boostrap was the fact that a theme already existed that married the two. This theme is called PJS Boostrap and can be found here. Never one to simply use a module as a normal user (see above for proof) I decided to duplicate the theme and make it my own!

Soule Designs Style Guide
Theme style guide crafted in Photoshop, final styles varied slightly

This process is actually pretty straight forward once a little time is invested in understanding Orchard placement.info and playing around with the shape tracing tool. Without any coding I was able to define my Portoflio Piece, Home Page Rotator, and the various projections necessary to get everything working. I was also able to customize my blog tags and archives widgets so they very closely resembled the look of Blogger, guess I wasn’t quite ready to let go? Anyway, my current theme and all the various custom view can be seen on Github repo here.

soule-designs-orchard-theme.png
The shiny new Soule Designs theme nestled in the Orchard themes area.

In Conclusion

Moving Blogger data into Orchard is a royal pain in the ass. Getting media centralized and links converted over isn’t too bad and theming is pleasant and seemingly benign compared to the other two steps. Would I do it again? Maybe. It was definitely a lot more work than I hoped for, but I am pretty pleased with the end result.   

That about wraps it up, for completeness here is the full rundown of 3rd party Orchard modules I'm currently using on the site:

Now maybe I can get back to more artistic endeavors!

Owen Soule

Published July 21, 2014