Moving from DateFolders to Umbraco's new ListView

Note: this post is over a year old, it's very likely completely outdated and should probably not be used as reference any more. You have been warned. :-)

This site is currently still running on Umbraco 6.1.6 but I'm moving it over to Umbraco 7, has moved from Umbraco 6.16 to v7 and I don't didn't want to have my blog posts listed in date folders any more.

DateFolders were a bit of a hack to make sure that the tree in Umbraco could load quickly. Too many children under one single node would cause the expansion of the tree to slow down. Not only that, it's hard to find items if there's a long list under just one child.

The ListView in Umbraco 7 solves all that, it's a nice sortable, searchable and paged list of child items under the current node. In this screenshot, for example, there's three children under the currently selected node and they're shown in a list on the right side instead of in the tree on the left:

Container _content _type _640x 332

So there were two challanges: moving all items and making sure the old URLs wouldn't start failing. 

Redirecting the old URLs was actually the difficult part (since I suck at regex..). I used IIS' URL Rewrite 2.0 extension for this. This extension is not installed by default on all webservers, but in Umbraco as a Service (which is where this site lives) it is installed, nice!

So now to figure out the pattern: 

  • each blog post url starts with blog/
  • then a 4 digit year
  • then a 1 or 2 digit month
  • then a 1 or 2 digit day
  • then the blog title

This leads to one group that includes the date and one group that contains the rest of the URL (groups are made by surrounding some arguments in parenthesis):


So once I got that one working, I added it to the web.config, in the rewrite/rules section. So: anything that starts with this regex pattern, do a permanent redirect to /blog/{whatever-was-here-as-"the rest"-of-the-url} (R:2 refers to the second regex group, which is "(.*)" ):

<rule name="Blog" stopProcessing="true">
	<match url="^(blog/\d{4}/\d{1,2}/\d{1,2}\/)(.*)$" />
	<action type="Redirect" url="/blog/{R:2}" />

Of course now all my URLs are failing because there's no actual content there, well let's fix that with a super simple UmbracoApiController

Each of my blog posts has a content type with the alias of "BlogPost", these all need to be moved under the /blog node which has an Id of 1207. I can go through the descendants of anything under /blog and check the content type to see if it matches. 
I also want to make sure that they're sorted from oldest to newest as that's the default sorting of Umbraco. With that done, I just move all posts directly under /blog. The new API in v6 make this so incredibly easy, awesome:

using System.Linq;
using Umbraco.Core.Models;
using Umbraco.Web.WebApi;

namespace Temporary.Controllers
    public class BlogPostsController : UmbracoApiController
        public void PostMovePosts()
            const int blogNodeId = 1207;

            var contentService = Services.ContentService;
            var blogNode = contentService.GetById(blogNodeId);

            foreach (var post in blogNode.Descendants().OrderBy(p => p.CreateDate))
                if (post.ContentType.Alias == "BlogPost")
                    contentService.Move(post, blogNodeId);

So: inherit from UmbracoApiController, that will create a route for us to go to: /umbraco/api/{ControllerName}/{MethodName} - So in this case: /umbraco/api/BlogPosts/PostMovePosts

By convention the BlogPostsController needs it's suffix "Controller" stripped off, so that turns in to "BlogPosts" and the method "PostMovePosts" starts with the verb "Post" which translates to the HTTP verb POST. That means I'll be using PostMan with that verb:


And there we have it, everything nicely moved and sorted, ready for the v7 upgrade.


Sebastiaan Janssen

Dutch guy living in (and loving) Copenhagen, working at Umbraco HQ. Lifehacker, skeptic, music lover, cyclist, developer.


7 comments on this article

Avatar for Stefan Kip Stefan Kip | January 19 2014 18:17
Is there a reason why you didn't install the Url Tracker, so redirects would've been create automatically? :)

Avatar for Sebastiaan Janssen Sebastiaan Janssen | January 19 2014 18:52
Sure, that's another option.
I do like the simple, single catch-all though, no need to do lookups for each content item either.

Avatar for martin griffiths martin griffiths | January 27 2014 11:38
Why did you go to the trouble of putting all posts in the root of the blog? For housekeeping would it not have made sense to at least retain the year folders and switch those to listview?

Avatar for Sebastiaan Janssen Sebastiaan Janssen | January 27 2014 11:45
@martin What housekeeping?

I have a super simple list now, which is nicely paged and easily queryable. See

What more could I wish for?

Avatar for JMK JMK | January 27 2014 21:33
Sebastiaan - just curious as to what you would do for an events list, where there is a chance that an event might have the same name as a previous event (eg. an event that occurs annually). I'm currently using DateFolders for events, which provides a unique URL for annual events with the same name (as the date is different). I'm thinking I won't be able to use the ListView in this situation? Also - for the ListView it would be great if we could display other properties in the list (eg. blog post date) - that would be very useful in your example above. I'll try to find some time to see if I can play around and modify the datatype - not sure how easy/difficult that would be.

Avatar for Sebastiaan Janssen Sebastiaan Janssen | January 27 2014 22:16
@JMK Yep, it would be good to change up the view in such cases, shouldn't be too difficult, but if it is then we should make it easy! So your problem would be totally solved if the event date was in the list. Curious to see what you can come up with.

Avatar for Elias Elias | February 3 2014 21:41
This would work on media items too ?