ContentService Events
The ContentService class is the most commonly used type when extending Umbraco using events. ContentService implements IContentService. It provides access to operations involving IContent.
Are you using Umbraco 9?
Note that in Umbraco 9, ContentService Events have been renamed to ContentService Notifications.
Find more information about notifications in Umbraco 9 in the Notifications section.
Usage
Example usage of the ContentService events:
using System;
using Umbraco.Core;
using Umbraco.Core.Composing;
using Umbraco.Core.Services.Implement;
namespace Umbraco8.Components
{
[RuntimeLevel(MinLevel = RuntimeLevel.Run)]
public class SubscribeToPublishEventComposer : ComponentComposer<SubscribeToPublishEventComponent>
{ }
public class SubscribeToPublishEventComponent : IComponent
{
public void Initialize()
{
ContentService.Publishing += ContentService_Publishing;
}
private void ContentService_Publishing(Umbraco.Core.Services.IContentService sender, Umbraco.Core.Events.ContentPublishingEventArgs e)
{
foreach (var node in e.PublishedEntities)
{
if (node.ContentType.Alias == "CorporateNewsAnnouncement")
{
var newsArticleTitle = node.GetValue<string>("newsTitle");
if (newsArticleTitle.Equals(newsArticleTitle.ToUpper()))
{
// Stop putting news article titles in upper case, so cancel publish
e.Cancel = true;
// Explain why the publish event is cancelled
e.Messages.Add(new Umbraco.Core.Events.EventMessage("Corporate style guideline infringement", "Don't put the news article title in upper case, no need to shout!", Umbraco.Core.Events.EventMessageType.Error));
}
}
}
}
public void Terminate()
{
//unsubscribe during shutdown
ContentService.Publishing -= ContentService_Publishing;
}
}
}
Events
Event | Signature | Description |
---|---|---|
Saving | (IContentService sender, ContentSavingEventArgs e) |
Raised when ContentService.Save is called in the API. NOTE: It can be skipped completely if the parameter "raiseEvents" is set to false during the Save method call (true by default). "sender" will be the current IContentService object. "e" will provide: NOTE: If the entity is brand new then HasIdentity will equal false.
|
Saved | (IContentService sender, ContentSavedEventArgs e) |
Raised when ContentService.Save is called in the API and after data has been persisted. NOTE: It can be skipped completely if the parameter "raiseEvents" is set to false during the Save method call (true by default). "sender" will be the current IContentService object. "e" will provide: NOTE: See here on how to determine if the entity is brand new
|
Publishing | (IPublishingStrategy sender, ContentPublishingEventArgs> e) |
Raised when ContentService.Publishing is called in the API. NOTE: It can be skipped completely if the parameter "raiseEvents" is set to false during the Publish method call (true by default). "sender" will be the current IPublishingStrategy object. "e" will provide: NOTE: If the entity is brand new then HasIdentity will equal false.
|
Published | (IPublishingStrategy sender, ContentPublishedEventArgs e) |
Raised when ContentService.Publish is called in the API and after data has been published. NOTE: It can be skipped completely if the parameter "raiseEvents" is set to false during the Publish method call (true by default). "sender" will be the current IPublishingStrategy object. "e" will provide: NOTE: See here on how to determine if the entity is brand new
|
UnPublishing | (IPublishingStrategy sender, PublishEventArgs<Umbraco.Core.Models.IContent> e) |
Raised when ContentService.UnPublishing is called in the API. "sender" will be the current IPublishingStrategy object. |
UnPublished | (IPublishingStrategy sender, PublishEventArgs<Umbraco.Core.Models.IContent> e) |
Raised when ContentService.UnPublish is called in the API and after data has been published. |
Copying | (IContentService sender, CopyEventArgs<IContent> e) |
Raised when ContentService.Copy is called in the API. The event is fired after a copy object has been created and had its parentId updated and its state has been set to unpublished. "sender" will be the current IContentService object. "e" will provide:
|
Copied | (IContentService sender, CopyEventArgs<IContent> e) |
Raised when ContentService.Copy is called in the API. The event is fired after the content object has been copied. "sender" will be the current IContentService object. "e" will provide:
|
Moving | (IContentService sender, MoveEventArgs<IContent> e) |
Raised when ContentService.Move is called in the API. NOTE: If the target parent is the Recycle bin, this event is never fired. Try the Trashing event instead. "sender" will be the current IContentService object. "e" will provide:
|
Moved | (IContentService sender, MoveEventArgs<IContent> e) |
Raised when ContentService.Move is called in the API. The event is fired after the content object has been moved. NOTE: If the target parent is the Recycle bin, this event is never fired. Try the Trashed event instead. "sender" will be the current IContentService object. "e" will provide:
|
Trashing | (IContentService sender, MoveEventArgs<IContent> e) |
Raised when ContentService.MoveToRecycleBin is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
Trashed | (IContentService sender, MoveEventArgs<IContent> e) |
Raised when ContentService.MoveToRecycleBin is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
Deleting | (IContentService sender, DeleteEventArgs<IContent> e) |
Raised when ContentService.DeleteContentOfType, ContentService.Delete, ContentService.EmptyRecycleBin are called in the API. "sender" will be the current IContentService object. "e" will provide:
|
Deleted | (IContentService sender, DeleteEventArgs<IContent> e) |
Raised when ContentService.Delete, ContentService.EmptyRecycleBin are called in the API. "sender" will be the current IContentService object. "e" will provide:
|
DeletingVersions | (IContentService sender, DeleteRevisionsEventArgs e) |
Raised when ContentService.DeleteVersion, ContentService.DeleteVersions are called in the API. "sender" will be the current IContentService object. "e" will provide:
|
DeletedVersions | (IContentService sender, DeleteRevisionsEventArgs e) |
Raised when ContentService.DeleteVersion, ContentService.DeleteVersions are called in the API. "sender" will be the current IContentService object. "e" will provide:
|
RollingBack | (IContentService sender, RollbackEventArgs<IContent> e) |
Raised when ContentService.Rollback is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
RolledBack | (IContentService sender, RollbackEventArgs<IContent> e) |
Raised when ContentService.Rollback is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
SendingToPublish | (IContentService sender, SendToPublishEventArgs<IContent> e) |
Raised when ContentService.SendToPublication is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
SentToPublish | (IContentService sender, SendToPublishEventArgs<IContent> e) |
Raised when ContentService.SendToPublication is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
EmptyingRecycleBin | (IContentService sender, RecycleBinEventArgs e) |
Raised when ContentService.EmptyingRecycleBin is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
EmptiedRecycleBin | (IContentService sender, RecycleBinEventArgs e) |
Raised when ContentService.EmptiedRecycleBin is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
SavedBlueprint | (IContentService sender, SaveEventArgs<IContent> e) |
Raised when ContentService.SavedBlueprint is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
DeletedBlueprint | (IContentService sender, DeleteEventArgs<IContent> e) |
Raised when ContentService.DeletedBlueprint is called in the API. "sender" will be the current IContentService object. "e" will provide:
|
Variants and Events
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 'events' of the ContentService - eg Which culture just got published? do I want to run my 'custom' code that fires on save if it's just the Spanish version that's been published? Also, if only the Spanish variant is 'unpublished' - that feels like a different situation to if 'all the variants' have been 'unpublished'. Depending which event you are handling there are helper methods you can call to find out:
Saving
When handling the 'ContentService.Saving' event this will be triggered whenever a variant is saved.
You can tell 'which' variant has triggered the save using a helper method on the ContentSavingEventArgs
called 'IsSavingCulture'
public bool IsSavingCulture(IContent content, string culture);
For example you could check which cultures are being saved (it could be multiple, if multiple checkboxes are checked)
private void ContentService_Saving(IContentService sender, Events.ContentSavingEventArgs e)
{
foreach (var entity in e.SavedEntities)
{
//cultures being saved
var savingCultures = entity.AvailableCultures.Where(f => e.IsSavingCulture(entity, f)).ToList();
//or
if (e.IsSavingCulture(entity,"en-GB") {
// do things differently if the UK version of the page is being saved!
}
}
Saved
In the Saved event you can similarly use the 'HasSavedCulture' method of the 'ContentSavedEventArgs' to detect which culture caused the Save.
public bool HasSavedCulture(IContent content, string culture);
Unpublishing
When handling the Unpublishing event, this 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 event will be fired 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 variant that has been unpublished.
You can therefore detect the Unpublishing of a variant, in the Publishing event - using the IsUnpublishingCulture
helper of the ContentPublishingEventArgs
private void ContentService_Publishing(IContentService sender, Events.ContentPublishingEventArgs e)
{
foreach (var entity in e.PublishedEntities)
{
if (e.IsUnpublishingCulture(entity, "en-GB"))
{
// bye bye UK!
}
}
}
Unpublished
Again the Unpublished event does not get fired when a single variant is Unpublished, instead the Published event can be used, and the 'HasUnpublishedCulture' method of the ContentPublishedEventArgs can determine which variant being unpublished triggered the publish.
public bool HasUnpublishedCulture(IContent content, string culture);
Publishing
When handling the 'ContentService.Publishing' event this will be triggered whenever a variant is published (or unpublished - see note in Unpublishing section).
You can tell 'which' variant has triggered the publish using a helper method on the ContentPublishingEventArgs
called IsPublishingCulture
public bool IsPublishingCulture(IContent content, string culture);
For example you could check which cultures are being published and act accordingly (it could be multiple, if multiple checkboxes are checked)
private void ContentService_Publishing(IContentService sender, Events.ContentPublishingEventArgs e)
{
foreach (var entity in e.PublishedEntities)
{
var publishingCultures = entity.AvailableCultures.Where(f => e.IsPublishingCulture(entity, f)).ToList();
var unPublishingCultures = entity.AvailableCultures.Where(f => e.IsUnpublishingCulture(entity, f)).ToList();
//or
if (e.IsPublishingCulture(entity,"en-GB"))
{
// welcome back Britain!
}
}
}
Published
In the Published event 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.
public bool HasPublishedCulture(IContent content, string culture);
public bool HasUnpublishedCulture(IContent content, string culture);
IContent Helpers
In each of these events, 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:
bool IsCultureAvailable(string culture);
bool IsCultureEdited(string culture);
bool IsCulturePublished(string culture);
What happened to Creating and Created events?
Both the ContentService.Creating and ContentService.Created events have been removed. Why? Because these events are not guaranteed to trigger and therefore should not be used. This is because these events only trigger when the ContentService.CreateContent method is used which is an entirely optional way to create content entities. It is also possible to construct a new content item - which is generally the preferred and consistent way - and therefore the Creating/Created events will not execute when constructing content that way.
Further more, there's no reason to listen for the Creating/Created events. They are misleading since they don't trigger before and after the entity has been persisted. They trigger inside the CreateContent method which never persists the entity, it constructs a new content object.
What do we use instead?
The ContentService.Saving and ContentService.Saved events will always trigger before and after an entity has been persisted. You can determine if an entity is brand new in either of those events. In the Saving event - 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 check to see if the entity 'remembers being dirty'