How to Move from .Text (Dottext) to BlogEngine.NET

by Erik Lane 2. November 2008 18:54

Back story

My wife and I's blogs have been running on an archaic customized version of .Text for much longer than it should have.  It did the job so it kept getting moved down the to-do list; not to mention I was not looking forward to the data conversion.  David upgraded the server to x64 and after a few tests it was clear that the blog wasn't going to run.  Briefly, I thought I could just recompile in .NET 2.0 and fix a couple of the stored procedures and be done with it.  Thankfully, I came to my senses and decided it was time to bite the bullet and make the jump to something else.

GraffitiCMS and BlogEngine.NET (BE.NET) were the two that I considered because they each could accept a BlogML file of all existing posts.  That would be awesome if my posts where in a BlogML formatted file.  I checked out Rss2BlogML but it kept failing...something with the formatting of my RSS feed.  I realized that I was spending time on getting my blog into BlogML and I'm not even sure it will produce the results I want (read where Dave Donaldson mentioned an issue with redirects for old links if they are not "named" posts in GraffitCMS).  None of my posts are "named" and I wanted a way to keep/update my old links.  So I started down the path of doing the conversion in a more manual fashion.

BE.NET has a pretty slick BlogImporter web service that abstracts away the data repository and it would've done the trick with just a little bit of coding on my part (highly recommend looking at that option).  I have and understand the .Text database and so hitting the database directly seemed like a logical way to go for me.  When I jumped onto this path I felt that GraffitiCMS was out of the picture because of the redirect issues and, having the code to BE.NET, I felt I could get BE.NET to produce the results I was wanting.

Everything after this assumes that you already run the BE.NET database setup scripts and have the BE.NET source code.

Files

Here are the files that I added or modified as part of this process including the SQL script.

Updating BE.NET Database

Thanks to Dave Burke's migration script most of the work was already done for me.  I will summarize it here but please go read it since he explains the script in detail.  Generally speaking, we are going to add the .Text postID and .Text CategoryID to BE.NET so we can use them to link posts, comments, and categories back together once the data is in the BE.NET database.  I told you you needed to go and read Dave's post.  :-)
Following along like a kid in school, I took good notes and updated the CommunityServer references to the .Text equivalent.  In my script, these are the items that you need to update for your personal settings:

  •     BlogID
  •     Author
  •     Default email address for the comments.

I ran the db script a few different times for each of our blogs so I think it is pretty solid.

Updating BE.NET Post Class

In BlogEngine.Core project I added a DottextPostID property to the Post class.

   1: private int _dottextPostId;
   2: ///<summary>
   3: /// Dottext Post Id to handle migrated links.
   4: ///</summary>
   5: public int DottextPostId
   6: {
   7:     get{ return _dottextPostId; }
   8:     set { _dottextPostId = value; }
   9: }

Update DbBlogProvider Class

In BlogEngine.Core project I modified the DbBlogProvider.SelectPost(Guid id) method to populate the Post object with the DottextPostId value from the database.

   1: string sqlQuery =
   2: "SELECT PostID, Title, Description, PostContent, DateCreated,"
   3: + "DateModified, Author, IsPublished, IsCommentEnabled,"
   4: + "Raters, Rating, Slug, DottextPostID "
   5: + "FROM " + tablePrefix + "Posts "
   6: + "WHERE PostID = " + parmPrefix + "id";
   7: ///
   8: /// unchanged code left out
   9: ///
  10: if (rdr.HasRows)
  11: {
  12:     rdr.Read();
  13:     post.Id = rdr.GetGuid(0);
  14:     post.Title = rdr.GetString(1);
  15:     post.Content = rdr.GetString(3);
  16:     if (!rdr.IsDBNull(2))
  17:         post.Description = rdr.GetString(2);
  18:     if (!rdr.IsDBNull(4))
  19:         post.DateCreated = rdr.GetDateTime(4);
  20:     if (!rdr.IsDBNull(5))
  21:         post.DateModified = rdr.GetDateTime(5);
  22:     if (!rdr.IsDBNull(6))
  23:         post.Author = rdr.GetString(6);
  24:     if (!rdr.IsDBNull(7))
  25:         post.IsPublished = rdr.GetBoolean(7);
  26:     if (!rdr.IsDBNull(8))
  27:         post.IsCommentsEnabled = rdr.GetBoolean(8);
  28:     if (!rdr.IsDBNull(9))
  29:         post.Raters = rdr.GetInt32(9);
  30:     if (!rdr.IsDBNull(10))
  31:         post.Rating = rdr.GetFloat(10);
  32:     if (!rdr.IsDBNull(12))
  33:         post.DottextPostId = rdr.GetInt32(12);
  34:         post.Slug = !rdr.IsDBNull(11) ? rdr.GetString(11) : "";
  35: }

Update UrlReWrite HttpModule

In BlogEngine.Core project I updated the UrlReWrite module to catch any old .Text links that are coming to the site.  By snagging the archive regex expression from the .Text web.config I saved myself some time.

   1: private static readonly Regex DOTTEXT = new Regex(@"/archive/\d{4}/\d{2}/\d{2}/(?<postId>\d+)\.aspx$", RegexOptions.IgnoreCase | RegexOptions.Compiled);

Modify context_BeginRequest
Added an else statement to look for any .Text links.

   1: /// unchanged code left out
   2: ///
   3: else if (url.Contains("/AUTHOR/"))
   4: {
   5:     string author = ExtractTitle(context, url);
   6:     context.RewritePath(Utils.RelativeWebRoot + "default" + BlogSettings.Instance.FileExtension + "?name=" + author + GetQueryString(context), false);
   7: }
   8: else
   9: {
  10:     Match match = DOTTEXT.Match(url);
  11:     if (match.Groups.Count > 0)
  12:     {
  13:         int postId;
  14:         if (int.TryParse(match.Groups["postId"].ToString(), out postId))
  15:         {
  16:             RewriteDottextPost(context, postId);
  17:         }
  18:     }
  19: }

Added ReWriteDottextPost Method

This method locates the right BE.NET post from the .Text post Id in the url, creates the new link, and sends back a response code of 301 so sites know that the link has been permanently moved.

   1: private static void RewriteDottextPost(HttpContext context, int postId)
   2: {
   3:     Post post = Post.Posts.Find(delegate(Post p)
   4:     {
   5:         return p.DottextPostId == postId;
   6:     });
   7:     if (post != null)
   8:     {
   9:         string redirUrl = string.Format("{0}://{1}{2}", context.Request.Url.Scheme, context.Request.Url.Authority, post.RelativeLink);
  10:         context.Response.AppendHeader("location", redirUrl);
  11:         context.Response.StatusCode = 301;
  12:         context.Response.End();
  13:     }
  14: }
  

Update Links in Content

This step is optional because the updated UrlReWriter will catch any of the old links, to my own content, in my posts but I did it for a clean break from .Text.  Beware that depending on your URL structure (blog.domain.com, www.domain.com/blog, etc.) you'll need to update the code so the regex used will catch your specific URL's.  Be sure and backup your BE.NET database or be prepared to do the whole conversion again (like I did) if the links don't turn out the way you want.

Created ConvertDottextLinks.aspx admin page.
This page can go anywhere but I put it in the admin section so it's not open for everyone.  I load up all of the posts in the database, find those which have old .Text links to my own content, find the new BE.NET post for that link, and create the new link for that post.

It is one aspx page with a button and literal on it and here is the code-behind:

   1: public partial class admin_Pages_ConvertDottextLinks : Page
   2: {
   3:     private static readonly List<Post> postList = BlogService.FillPosts();
   4:     protected void Page_Load(object sender, EventArgs e)
   5:     {
   6:     }
   7:     protected void btnGo_Click(object sender, EventArgs e)
   8:     {
   9:         // update this regex with your URL structure.
  10:         Regex regex = new Regex("href\\s*=\\s*(?:\\\"http://blog.eriklane.com/archive/\\d{4}/\\d{2}/\\d{2}/(?<postId>\\d+)\\.aspx)",
  11:             RegexOptions.IgnoreCase
  12:             | RegexOptions.Multiline
  13:             | RegexOptions.Compiled
  14:         );
  15:         int count = 0;
  16:         postList.ForEach(delegate(Post post)
  17:         {
  18:             if (regex.IsMatch(post.Content))
  19:             {
  20:                 string updatedContent = regex.Replace(post.Content, new MatchEvaluator(convertLink));
  21:                 post.Content = updatedContent;
  22:                 post.Save();
  23:                 count++;
  24:             }
  25:         });
  26:         listResult.Text = string.Format("Finished! - {0} links were converted.", count);
  27:     }
  28:     private static string convertLink(Match match)
  29:     {
  30:         int dottextPostId;
  31:         if (int.TryParse(match.Groups["postId"].ToString(), out dottextPostId))
  32:         {
  33:             Post linkPost = postList.Find(delegate(Post post) { return post.DottextPostId == dottextPostId; });
  34:             return "href=\"/post/" + linkPost.Slug + BlogSettings.Instance.FileExtension;
  35:         }
  36:         return match.ToString();
  37:     }
  38: }

Copy Images

This will be totally dependent on how how you store your images.  I've kept all of mine in one folder so mine "just worked" by copying the image folder over.  However, you could update your image links to use BE.NET's image HttpHandler to serve them up.  If it were me, I would do it similar to how I updated the content links.

Update Feedburner


You will need to update Feedburner with the new BE.NET feed URL (http://yourdomain.com/syndication.axd).

Conclusion


After all of this I have:

  • All of the posts.
  • All of the post comments.
  • All posts are in the correct categories.
  • All old links are caught and redirected.

These steps pretty much sum up what I did to convert a our .Text blogs over to BE.NET.  It's not a point and click operation but it wasn't as painful as I was expecting.
Tags:

Comments

The Wife
The Wife United States on 10/26/2008 4:46:10 AM

Thank you for taking care of this for us.  You're awesome Smile

Dave Burke
Dave Burke United States on 10/26/2008 5:38:17 AM

Wow, you did a phenomenal job, Erik.  And this was an excellent post! The blog looks fantastic!  I hope you and "The Wife" Smile enjoy BlogEngine.NET as much as I do. Congrats, my friend.

eriklane
eriklane United States on 10/26/2008 1:06:31 PM

@Julie - my pleasure..I'm just sorry it took so long but now we get to work on new themes.  Smile

@Dave - Thanks for the compliments but I a really big thank you is in line for your db script...it was a real time saver.

trackback
ToDotNet on 3/12/2009 10:58:19 AM

Trackback from ToDotNet

Migrated to BlogEngine.NET

Jason Haley
Jason Haley United States on 3/25/2009 6:56:30 AM

Thanks for this post ... I think it is exactly what I need to FINALLY take a weekend and upgrade my site.

eriklane
eriklane United States on 3/26/2009 4:56:29 PM

@Jason - Thanks for the feeback and hope it helps you with the transition.

Jason Haley
Jason Haley United States on 3/27/2009 11:24:26 AM

Worked great!

Comments are closed