Automatic Date & Time Ordered Content in the Umbraco Back Office
- Matt Bliss
- 18 Feb 2025
In the Umbraco back-office I would ideally like to see the articles from this blog listed in the tree in reverse order of their publish date, the same order that they appear in the front end of the website with the most recent article listed first.
Also, while I'm being particular, I would like this to be a fully automatic, hands-off process. It turns out it doesn't take much code to achieve this.
How Umbraco handles ordering
First of all, every content node in Umbraco has a SortOrder
property, and the value of that property is stored in an int
. When Umbraco sets the SortOrder
it begins at 1 and increments by 1 each time, but any int
value is valid and they don't need to be sequential; the nodes get ordered by the values in SortOrder
property.
Creating our own order
Now the range of an int
is enormous[1] so if we were to use half the range, just the positive values for example, and allocate one value for every second, we would have a range that covered 68 years. Realistically, I won't publish more than two articles a month, so a granularity of days would be fine for my use-case, but as we have so much range I'll go for minutes instead. This takes us into the realm of thousands of years, still very granular, and could comfortably be used for a site publishing many articles per day.
Setting our sort order automatically
To set our sort order based on the timestamp of our article we need to do the following:
- Capture the saving event for our articles pages with a Notification Handler[2]
- Set the
SortOrder
to an integer value based on the timestamp of our article.
DateTime
values increase over time, but I want a lower int
value for a more recent timestamp, so we just need to do some subtraction from a fixed time point in time. As we are dealing with a range of thousands of years, the Unix Epoch[3] is as good as any 'recent' time point to work from, then we can get the negative number of minutes between the two dates.
(int)(DateTime.UnixEpoch - articleDate).TotalMinutes;
More recent articles will have a 'larger' negative number of minutes, which is a smaller number for our SortOrder
. This is the code needed to order my 'Article' pages by the 'Article Date' property, and if that's not set, by the 'Created Date':
public class SetSortOrder : INotificationHandler<ContentSavingNotification>
{
public void Handle(ContentSavingNotification notification)
{
foreach (var node in notification.SavedEntities)
{
if (!node.ContentType.Alias.Equals("article")) continue;
var articleDate = node.GetValue<DateTime>("articleDate");
if (articleDate == DateTime.MinValue) articleDate = node.CreateDate;
node.SortOrder = (int)(DateTime.UnixEpoch - articleDate).TotalMinutes;
}
}
}
Note: In my case, I'm using ModelsBuilder[4] as I prefer the strongly typed approach to the 'stringly' typed one, this adds a few lines of code, mainly for the DI of IPublishedContentTypeCache
to support the GetModelPropertyType
method. To demonstrate the process I've included the above code with string constants for the IDs to keep to the code as simple as possible.
Either way, strongly or 'stringly' typed, our SetOrder
class needs a composer to tell Umbraco to trigger our notification handler:
public class SetOrderComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.AddNotificationHandler<ContentSavingNotification, SetSortOrder>();
}
}
That's it, every time we save an article node the sort order will be automatically set based on the article date and failing that the created date.
Preventing the order being changed
This is half the solution, once we have all our items individually ordered on save, we need to ensure that the order of the whole group cannot be reset. So the next step is to prevent the changing of the sort order in the back office. A click of the 'Sort Children' option on our containing node allows us to reorder the nodes and Umbraco will reset the sort order for every child 'Article' sequentially starting from 1 child as described above.
Ideally, I would have liked to be able to remove the 'Sort Children' option from the context menu for the Blog node preventing the child articles from being sorted in the UI. While this maybe possible, I haven't yet identified how to go about this. However, once nodes have been sorted by an editor in the back office UI and submitted, it is possible to prevent the new sort orders being applied to those article nodes by hooking into another of the Umbraco Notifications[2].
public class PreventSorting : INotificationHandler<ContentSortingNotification>
{
public void Handle(ContentSortingNotification notification)
{
var first = true;
const string type = "article";
var nodes = notification.SortedEntities.Where(n => n.ContentType.Alias.Equals(type)).ToList();
foreach (var node in nodes)
{
if (first)
{
notification.CancelOperation(new EventMessage($"Sort of {nodes.Count} \"{type}\" items canceled",
$"Content of type \"{type}\" is ordered automatically by date",
EventMessageType.Error));
first = false;
}
else
{
notification.Cancel = true;
}
}
}
}
The above code is slightly different to our earlier Notification Handler. The notification handler returns an enumeration of all the SortedEntities
which in reality is every child node; in our case, every article. If we were to process every article with a notification.CancelOperation
then we would get a red notification box for every article in the UI, not a great user experience. So the above code only creates a visible notification to the user for the first item, telling the user how many items have been cancelled. For subsequent nodes we set notification.Cancel
to true
which also prevents the sort order for the node being updated, but does so invisibly to the user.
Again our PreventSorting
class needs a composer to tell Umbraco to trigger our notification handler:
public class PreventSortingComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
builder.AddNotificationHandler<ContentSortingNotification, PreventSorting>();
}
}
The above code was written for Umbraco 15, but should apply equally well to the current LTS Umbraco 13 version with little or no adjustment. I hope you find it a useful approach for date-based content.
Footnotes
Integers have a range of -2,147,483,648 to 2,147,483,647 https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/integral-numeric-types
Umbraco Notifications https://docs.umbraco.com/umbraco-cms/reference/notifications
Unix Epoch: January 1, 1970 https://en.wikipedia.org/wiki/Unix_time
Umbraco Models Builder https://docs.umbraco.com/umbraco-cms/reference/templating/modelsbuilder/introduction