Exam Area: Content Area 5 – Content Models (25%)
Reference: https://docs.developers.optimizely.com/content-management-system/docs/event-management
using EPiServer.ServiceLocation;
// Legacy approach (avoid in CMS 12)
var service = ServiceLocator.Current.GetInstance<IContentRepository>();
// Preferred in CMS 12: Constructor Injection
public class MyController : Controller
{
private readonly IContentRepository _repo;
public MyController(IContentRepository repo)
{
_repo = repo;
}
}
// When you MUST use ServiceLocator (static context):
public static class StaticHelper
{
public static IContentRepository GetRepo()
{
return ServiceLocator.Current.GetInstance<IContentRepository>();
}
}
using EPiServer;
using EPiServer.Core;
// IContentEvents has the main events:
public interface IContentEvents
{
// Publishing lifecycle
event EventHandler<ContentEventArgs> PublishingContent;
event EventHandler<ContentEventArgs> PublishedContent;
// Saving lifecycle
event EventHandler<ContentEventArgs> SavingContent;
event EventHandler<ContentEventArgs> SavedContent;
// Deleting
event EventHandler<ContentEventArgs> DeletingContent;
event EventHandler<ContentEventArgs> DeletedContent;
// Moving to wastebasket
event EventHandler<ContentEventArgs> MovingContent;
event EventHandler<ContentEventArgs> MovedContent;
// Loading
event EventHandler<ContentEventArgs> LoadingContent;
event EventHandler<ContentEventArgs> LoadedContent;
// Children loading
event EventHandler<ChildrenEventArgs> LoadingChildren;
event EventHandler<ChildrenEventArgs> LoadedChildren;
// Version status changes
event EventHandler<ContentEventArgs> RequestingApproval;
event EventHandler<ContentEventArgs> RejectingContent;
event EventHandler<ContentEventArgs> CheckingInContent;
}
[InitializableModule]
[ModuleDependency(typeof(EPiServer.Web.InitializationModule))]
public class ContentEventHandler : IInitializableModule
{
private IContentEvents _contentEvents;
public void Initialize(InitializationEngine context)
{
_contentEvents = context.Locate.Advanced.GetInstance<IContentEvents>();
// Subscribe
_contentEvents.PublishedContent += OnPublished;
_contentEvents.SavingContent += OnSaving;
_contentEvents.DeletingContent += OnDeleting;
}
public void Uninitialize(InitializationEngine context)
{
// MUST unsubscribe!
_contentEvents.PublishedContent -= OnPublished;
_contentEvents.SavingContent -= OnSaving;
_contentEvents.DeletingContent -= OnDeleting;
}
private void OnPublished(object sender, ContentEventArgs e)
{
var content = e.Content;
var contentRef = e.ContentLink;
// Example: Purge CDN cache
if (content is PageData page)
{
PurgeCdnCache(page);
}
}
private void OnSaving(object sender, ContentEventArgs e)
{
// Validate before saving
if (e.Content is ArticlePage article)
{
if (string.IsNullOrEmpty(article.Heading))
{
e.CancelAction = true;
e.CancelReason = "Heading is required!";
}
}
}
private void OnDeleting(object sender, ContentEventArgs e)
{
// Log deletion
var contentRef = e.ContentLink;
// Log...
}
}
public class ContentEventArgs : EventArgs
{
public ContentReference ContentLink { get; set; } // Content being acted on
public IContent Content { get; set; } // The content object
public SaveAction Action { get; set; } // What action
public bool CancelAction { get; set; } // Cancel the action
public string CancelReason { get; set; } // Reason for cancel
public IContent RequiredRole { get; set; } // Required role
public string TargetLink { get; set; } // Target (for moves)
}
// Core repositories injected via DI
// Content CRUD
IContentRepository // Full CRUD
IContentLoader // Read-only
IContentVersionRepository // Version management
// Content type management
IContentTypeRepository // Content types
IPropertyDefinitionRepository // Properties
// Security
IContentSecurityRepository // ACL management
// Language
ILanguageBranchRepository // Languages
// Scheduled Jobs
IScheduledJobRepository // Job management
// Notification
INotificationRepository // Notifications
// URL and content resolution
IUrlResolver // Content → URL
IContentRouteHelper // Route helpers
ISiteDefinitionRepository // Site management
ISiteDefinitionResolver // Current site
// Template resolution
ITemplateResolver // Find templates
IContentRenderer // Render content
// Language resolution
ILanguageSelector // Language selection
LanguageSelector // Concrete implementations
// Cancel action in a before event
private void OnPublishing(object sender, ContentEventArgs e)
{
if (e.Content is ArticlePage article)
{
if (!ValidateArticle(article))
{
e.CancelAction = true;
e.CancelReason = "Article validation failed: Missing required fields";
}
}
}
// For multi-instance: use Azure Service Bus to broadcast events
builder.Services.AddAzureServiceBusEventProvider(options =>
{
options.ConnectionString = "your-service-bus-connection";
options.TopicName = "cms-events";
options.SubscriptionName = Environment.MachineName;
});
IContentEvents provide? (Events for the content lifecycle: publish, save, delete, etc.)CancelAction = true? (In "before" events such as PublishingContent, SavingContent)Uninitialize()? (Avoids memory leaks and duplicate handler calls)IContentRepository differ from IContentLoader? (Repository: full CRUD; Loader: read-only, faster)