Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
How to create and publish your own custom notifications
There may be many reasons why you would like to create your own custom notifications, in this article we'll use the CleanUpYourRoom recurring hosted service as an example, which empties the recycle bin every 5 minutes. You might want to publish a notification once the task has started, and maybe once the task has successfully cleared the recycle bin.
For a notification to be publishable there's only one requirement, it must implement the empty marker interface INotification
, the rest is up to you. For instance, we might want to create a notification that just signals that the clean your room task has started and nothing else, in this case, we'll create an empty class implementing INotification
This notification can now be published, and we can create a notification handler to receive it with, see MediaService-Notifications for an example of how to implement a notification handler. But this notification alone might not be super helpful, we might want to be able to send some additional information with the notification, however, since this is, in essence, just a normal class, we can include whatever information we want. Let's try and create a RoomCleanedNotification
which contains the number of nodes removed from the recycle bin:
Now you can create a handler that receives the amount of items deleted through the notification.
Just creating the notification classes is not enough, we also want to be able to publish them. There's two ways of publishing notifications:
IEventAggregator
- Notifications published with IEventAggregator
will always be published immediately.
IScope.Notifications
- Notifications published with a scope will only be published once the scope has been completed and disposed.
The method you use to publish notifications depends on what your needs are, the benefits of publishing notifications with a scope is that the notification will only be published if you complete the scope, and then only once the scope is disposed of. This can be useful if you access the database, or do some other operation that might fail causing you to do a rollback, disposing of the scope without completing it, in this case, you might not want to publish a notification that signals that the operation was a success, using scopes will handle this for you. On the other hand, you might want to publish the notification immediately no matter what, for instance with the CleanYourRoomStartedNotification
, for this, the IEventAggregator
is the right choice.
In this case, the CleanYourRoomStartedNotification
will always be published immediately, however, RoomCleanedNotification
will only be published once the operation is done, and if you remove the scope.Complete();
line it will never be published, the recycle bin won't be emptied either.
Find out more about ContentService Notifications and explore some example of how to use it
The ContentService class is the most commonly used type when extending Umbraco using notifications. ContentService implements IContentService. It provides access to operations involving IContent.
Example usage of the ContentPublishingNotification:
The handler will also need to be registered. See the Notifications article for specifics on how to do this.
Umbraco V8 introduced the concept of Variants for Document Types, initially to allow different language variants of particular properties within a Document Type to be edited/translated based on the languages configured in your instance of Umbraco.
These variants can be saved, published, and unpublished independently of each other. (Unpublishing a 'mandatory language' variant of a content item - will trigger all culture variants to be unpublished).
This poses a problem when handling notifications from the ContentService - eg which culture got published? Do I want to run my 'custom' code that fires on save if it's only the Spanish version that's been published? Also, if only the Spanish variant is 'unpublished' - that feels like a different situation than if 'all the variants' have been 'unpublished'. Depending on which event you are handling there are helper methods you can call to find out.
When handling the ContentSavingNotification which will be published whenever a variant is saved. You can tell 'which' variant has triggered the save using an extension method on the ContentSavingNotification called 'IsSavingCulture'
As an example, you could check which cultures are being saved (it could be multiple if multiple checkboxes are checked)
With the Saved notification you can similarly use the 'HasSavedCulture' method of the 'ContentSavedNotification' to detect which culture caused the Save.
When handling the Unpublishing notification, it might not work how you would expect. If 'all the variants' are being unpublished at the same time (or the mandatory language is being unpublished, which forces this to occur) then the Unpublishing notification will be published as expected.
However, if only one variant is being unpublished, the Unpublishing event will not be triggered. This is because the content item itself is not fully 'unpublished' by the action. Instead, what occurs is a 'publish' action 'without' the unpublished variant.
You can therefore detect the Unpublishing of a variant in the publishing notification - using the IsUnpublishingCulture extension method of the ContentPublishingNotification
Again, the Unpublished notification does not get published when a single variant is Unpublished, instead, the Published notification can be used, and the 'HasUnpublishedCulture' extension method of the ContentPublishedNotification can determine which variant being unpublished triggered the publish.
When handling the ContentPublishingNotification which will be triggered whenever a variant is published (or unpublished - see note in the Unpublishing section above).
You can tell 'which' variant has triggered the publish using a helper method on the ContentPublishingNotification called IsPublishingCulture.
For example, you could check which cultures are being published and act accordingly (it could be multiple if multiple checkboxes are checked).
In the Published notification you can similarly use the HasPublishedCulture and HasUnpublishedCulture methods of the 'ContentPublishedEventArgs' to detect which culture caused the Publish or the UnPublish if it was only a single non-mandatory variant that was unpublished.
In each of these notifications, the entities being Saved, Published, and Unpublished are IContent
entities. There are some useful helper methods on IContent to discover the status of the content item's variant cultures:
Get started with Notifications.
Umbraco uses Notifications (similar to the Observer pattern) to allow you to hook into the workflow process for the backoffice. For example, notifications allow you to execute some code every time a page is published.
All notifications reside in the Umbraco.Cms.Core.Notifications
namespace and are postfixed with Notification
.
Available notifications typically exist in pairs, with "before" and "after" notifications. For example, the ContentService class has the concept of publishing and published notifications. So, there is both a ContentPublishingNotification
and a ContentPublishedNotification
notification.
The notification to use depends on what you want to achieve. If you want to be able to cancel the action, you would use the CancelOperation
method on the "before" notification. See the sample in ContentService Notifications. If you want to execute some code after the publishing has succeeded, then you would use the "after" notification.
Check the Notification Handler article to learn more about notification handlers lifetime, async notification handler and how to register the notification handlers.
Below you can find a list of most used object notifications.
You can find a list of all supported notifications in the API Docs.
See Tree Notifications for a list of the tree notifications.
See EditorModel Notifications for a list of the EditorModel events.
Useful for manipulating the model before it is sent to an editor in the backoffice. It could be used to set a default value of a property on a new document.
Umbraco uses notifications to allow people to hook into different workflow processes. This notification pattern is extensible, allowing you to create and publish custom notifications, and other people to observe and hook into your custom processes. This approach can be useful when creating Umbraco packages. For more information on how you create and publish your own notifications, see the creating and publishing notifications article.
Below you can find some articles with some examples using Notifications.
Many of the Umbraco services publishes a 'Saved' notification (or similar). In some cases, it is beneficial to know if this entity is a brand new entity that has been persisted in the database. This is how you can determine this.
We know that if an entity is new and hasn't been persisted that it will not have an ID. Therefore we know if an entity has been newly persisted to the database by checking if its ID was changed before being persisted.
Here's the snippet of code that does that:
To check if an entity is new in the ContentSavingNotification use the following:
Since the IContent has not been saved yet, it's not necessary to cast it to IRememberBeingDirty
. It won't have an identity if it's new, since it hasn't been committed yet.
This is all possible because of the IRememberBeingDirty
interface. Indeed the name of this interface is hilarious but it describes exactly what it does. All entities implement this interface which is really handy. It tracks not only the property data that has changed because it inherits from yet another hilarious interface called ICanBeDirty
. It also tracks the property data that was changed before it was committed.
Learn about notification handlers lifetime, async notification handler and how to register the notification handlers.
It's important to note that the handlers you create and register to receive notifications will be transient, this means that they will be initialized every time they receive a notification, so you cannot rely on them having a specific state based on previous notifications. For instance, you cannot create a list in a handler and add something when a notification is received, and then check if that list contains what you added in an earlier notification, that list will always be empty because the object has just been initialized.
If you need persistence between notifications, we recommend that you move that functionality into a service or similar, and register it with the DI container, and then inject that into your handler.
As previously mentioned a lot of notifications exist in pairs, with a "before" and "after" notification, there may be cases where you want to add some information to the "before" notification, which will then be available to your "after" notification handler, in order to support this, the notification "pairs" are stateful. This means that the notifications contain a dictionary that is shared between the "before" and "after" notification that you can add values to, and later get them from like this:
Once you've made your notification handlers you need to register them with the AddNotificationHandler
extension method on the IUmbracoBuilder
, so they're run whenever a notification they subscribe to is published. There are two ways to do this: In the Startup class, if you're making handlers for your site, or a composer if you're a package developer subscribing to notifications.
Registering notification handlers in the startup class
In the Startup class register your notification handler in the ConfigureServices
after AddComposers()
but before Build()
:
The extension method takes two generic type parameters, the first ContentPublishingNotification
is the notification you wish to subscribe to, the second DontShout
is the class that handles the notification. This class must implement INotificationHandler<>
with the type of notification it handles as the generic type parameter, in this case, the DontShout class definition looks like this:
For the full handler implementation see ContentService Notifications.
If you're writing a package for Umbraco you won't have access to the Startup class, you can instead use a composer which gives you access to the IUmbracoBuilder
, the rest is the same as when doing it in the Startup class:
You may want to subscribe to a lot of notifications, in this case, your ConfigureServices
method or composer might end up being quite cluttered. You can avoid this by creating your own IUmbracoBuilder
extension method for your events, keeping everything neatly wrapped up in one place, such an extension method can look like this:
You can then register all these notifications by calling AddDontShoutNotifications
in ConfigureServices
or your composer, just like you would AddNotificationHandler
:
Example of how to use a CacheRefresher Notification
Before starting with cache refresher notifications it's a good idea to ensure you need to use them. If you want to react to changes in content, for instance, there's no real reason to use these notifications. This is due to the content service notifications being easier to work with. If you need to react to changes in the cache, then these are the notifications for you.
Cache refresher notifications are sent when the cache has refreshed. There are multiple different types of cache refresher notifications. These types are based on what type has been updated in the cache, for instance, content or media. All these notifications inherit from the same base notification: CacheRefresherNotification
.
The base notification is implemented in the following way:
As you can see this notification contains two properties, a MessageObject
and a MessageType
. The MessageType
specifies what kind of cache operation was performed, for example RemoveById
. The possible message types is as follows:
The other parameter MessageObject
will depend on what type of cache refresher notification you're handling. If you for instance handle the ContentCacheNotification
, the message object will be ContentCacheRefresher.JsonPayload[]
.
This object contains the Id and key of the item being updated, as well as an enum specifying how the tree is updated:
An example of working with the ContentCacheNotification
can be seen here:
Example of how to use a MediaService Notification
The MediaService class implements IMediaService. It provides access to operations involving IMedia.
Example usage of the MediaService notifications:
You can return a custom message to the user. Use this to show information, a warning or maybe an error. This is achieved using the Messages
property of the notification and a composer.
This example returns an informational message to the user when a Media item is saved.
Represents an Umbraco application lifetime (starting, started, stopping, stopped) notification
Umbraco application lifetime notifications are published for the starting, started, stopping, and stopped events of the Umbraco runtime. These events implement the IUmbracoApplicationLifetimeNotification
interface that contains a single IsRestarting
property.
An Umbraco application is restarted after an install or upgrade has been completed. You can use this property to prevent running code twice: on initial boot and restart. To prevent running code when the application is in the install or upgrade state, inject an IRuntimeState
instance in your notification and inspect the Level
property instead.
Example usage of the UmbracoApplicationLifetime notifications:
For a content item, Umbraco will show a Links box within the Info content app. By default, this box will show one or more links to content item.
With the SendingContentNotification
event, we can manipulate the links in the Urls
property. This could be by replacing it with custom links although a URL provider would be more suitable:
or remove the box entirely by providing an empty list of links:
The SendingAllowedChildrenNotification
enables you to manipulate the Document Types that will be shown in the create menu when adding new content in the backoffice.
With the example below we can ensure that a Document Type cannot be selected if the type already exists in the Content tree.
You also need to register this notification handler. You can achieve this by updating the Startup
class like:
EditorModel notifications enable you to manipulate the model used by the backoffice before it is loaded into an editor. For example the SendingContentNotification
is published right before a content item is loaded into the backoffice for editing. It is therefore the perfect notification to use to set a default value for a particular property, or perhaps to hide a property/tab/Content App from a certain editor.
Example usage of the SendingContentNotification
- e.g. set the default PublishDate for a new NewsArticle to be today's Date:
Another example could be to set the default Member Group for a specific Member Type using SendingMemberNotification
:
A model representing a content item to be displayed in the backoffice
TemplateAlias
Urls
AllowPreview - Determines whether previewing is allowed for this node, By default this is true but by using notifications developers can toggle this off for certain documents if there is nothing to preview
AllowedActions - The allowed 'actions' based on the user's permissions - Create, Update, Publish, Send to publish
IsBlueprint
Tabs - Defines the tabs containing display properties
Properties - properties based on the properties in the tabs collection
And more...
A model representing a media item to be displayed in the backoffice
Alias
Tabs - Defines the tabs containing display properties
Properties - properties based on the properties in the tabs collection
And more...
A model representing a member to be displayed in the backoffice
Username
Tabs - Defines the tabs containing display properties
Properties - properties based on the properties in the tabs collection
And more...
The EditorModel notifications gives you a lot of options to customize the backoffice experience. You can find inspiration from the various samples provided below:
The MediaSavingNotification and MediaSavedNotification will always be published before and after an entity has been persisted. You can determine if an entity is brand new with either of those notifications. With the Saving notification - before the entity is persisted - you can check the entity's HasIdentity property which will be 'false' if it is brand new. In the Saved event you can
:
:
For more information about registering notifications read the article.
Notification | Members | Description |
---|
SendingContentNotification |
| Published right before the editor model is sent for editing in the content section. NOTE: Content is a Umbraco.Cms.Core.Models.ContentEditing.ContentItemDisplay type which contains the tabs and properties of the elements about to be loaded for editing. |
SendingMediaNotification |
| Published right before the editor model is sent for editing in the media section NOTE: Media is a Umbraco.Cms.Core.Models.ContentEditing.MediaItemDisplay type which in turn contains the tabs and properties of the elements about to be loaded for editing. |
SendingMemberNotification |
| Published right before the editor model is sent for editing in the member section. NOTE: Member is a Umbraco.Cms.Core.Models.ContentEditing.MemberDisplay type which in turn contains the tabs and properties of the elements about to be loaded for editing. |
SendingUserNotification |
| Published right before the editor model is sent for editing in the user section. NOTE: User is a Umbraco.Cms.Core.Models.ContentEditing.UserDisplay type which in turn contains the tabs and properties of the elements about to be loaded for editing. |
SendingDashboardsNotification |
| Published right before the a dashboard is retrieved in a section. NOTE: Dashboards is a collection of IDashboardSlim, each object gives you access to Label, Alias, Properties, whether it's expanded, and whether it IsActive. |
SendingAllowedChildrenNotification |
| Published right before the allowed children of the selected Content Type are sent back during content creation in the Content Section. NOTE: Children is a collection of ContentTypeBasic, each object gives you access to Alias, Description, Thumbnail and more. You can remove or add new children to the list in the notification. |