Let's talk about the differences between Umbraco 8 & 9! Today we will be taking a look at the Events in Umbraco 8, and the Umbraco 9 equivalent called Notifications. How do they work, what has changed between the two versions, and what can we do with them? Let's talk about that!
Umbraco 8 - Events
Umbraco 8 uses .Net events to allow you to hook into the workflow processes for the backoffice. For example you might want to execute some code every time a page is published, moved, or deleted. Events allow you to do that. To hook into these events, Umbraco 8 uses Compositions and Components to access & execute code for the various kinds of events available.
In a nutshell, Components (classes that inherit from IComponent) implement two methods: Initialize() and Terminate(). Within these methods you are able to subscribe & unsubscribe to events. Umbraco 8 has several services available that support the registration of events, including but not limited to:
These services allow for the extension of various backoffice events. These include but are not limited to:
- Saving & Saved
- Publishing & Published
- Unpublishing & Unpublished
- Copying & Copied
- Moving & Moved
- Trashing & Trashed
- Deleting & Deleted
- ... and the list goes on!
As you can see, most of these events have two variants; Before and After an action has taken place. For example, the 'Published' event is fired after an item is published in Umbraco, and the 'Publishing' event is fired when publishing the item in Umbraco, before the item gets published. This will allow you to manipulate what happens with the item before it gets published, and even cancel the publishing all together if you wish to do so!
Let's take a look at a code snippet from the official Umbraco documentation.
What we can see in the snippet above is that we create a new Composer called SubscribeToPublishEventComposer, inheriting from ComponentComposer<T> with our IComponent implementation as T. We also add the RuntimeLevel attribute to our Composer so that it automatically gets registered in our application thanks to Umbraco!
Within our component we register a new method to the ContentService.Publishing event. Within this event, we get a lot of information about the sender of the event, and the entities that are queued to get published. In the example we loop over all the items that we are publishing, check if it's an item of type "CorporateNewsAnnouncement", check if the newsTitle field is in all capital letters, and if so we cancel the publishing event! It also shows a friendly message for our editors stating why we are cancelling the publish.
Umbraco 9 - Notifications
Umbraco 9 no longer uses events to allow us to hook into the workflow process for the backoffice, but Notifications, which are very similar to the Observer pattern. The events themselves haven't changed all that much in between versions, but the way we register them has changed quite significantly. We no longer access the Service we wish to extend directly, but we register a new Notification in which we specify which type of Umbraco notification we wish to extend.
Let's take a look at the example of a notification from the Umbraco documentation, functioning similar to the one we used in Umbraco 8!
Just like events, most notifications exist in pairs, with a "before" and "after" notification. As you can see in the snippet above we create a new class that implements the interface INotificationHandler<T>, where T is the notification we wish to extend! This will implement the Handle method for us, with the parameters depending on which Notification we wish to handle. To give a couple of examples of Notifications specifically involving the ContentService:
- ContentSavingNotification & ContentSavedNotification
- ContentPublishingNotification & ContentPublishedNotification
- ContentUnpublishingNotification & ContentUnpublishedNotification
- ContentCopyingNotification & ContentCopiedNotification
- ContentMovingNotification & ContentMovedNotification
- ... and the list goes on!
We are also no longer forced to register out notifications using a Composers either! There are two main options for registering our new notification handler:
- Directly (or indirectly) in the ConfigureServices method of our Startup class.
- Using a Composer implementing the IComposer interface, giving us access to the IUmbracoBuilder
Let's take a look a the first way of registering our new notification handler. Within the ConfigureServices method of our startup class, we register a new notification using the .AddNotificationHandler extension method. With this extension method, we first have to specify which Umbraco notification we which to handle (which in our case is the ContentPublishingNotification) and second we specify our class that implements from INotificationHandler with the same notification type, just like in the previous snippet!
It is important to register our notification handler after AddComposers() but before Build()!
Notification handlers lifetime
It is important to note that the handlers we create and register to receive notification will be transient. This means that they will be initialized every time they receive a notification (compared to singleton), so we cannot rely on them having a specific state based on previous notifications!
If we do wish to have persistence between notifications, the Umbraco documentation recommends that we move that functionality into a service or similar, and register it with the Dependency Injection container to inject that into our handler.
While the functionality of the Events/Notifications stayed mostly the same, the way you create and handle notifications has significantly changed (for the better, if I may say so myself!). Events are no longer automatically registered by ways of a Component & Composer registering the event to a service, but get explicitly registered to the IUmbracoBuilder with the Notification of our choosing!
While the naming of the Umbraco 9 Notifications has changed compared to the Events in Umbraco 8, the way we interact with the notification has barely changed at all! 🔥
If you have any further questions, feel free to contact me over at my socials available at the Contact page, and I'd love to hear about the amazing creations you're able to come up with, with this setup! 😄
Sources used in this article: