Skip to main content

Outline

At a glance
  • Automation vs Reactivity: Scheduled Jobs enable time-based background automation, while Content Events provide an event-driven mechanism that reacts instantly to content lifecycle changes.
  • Key developer capability: Scheduled jobs are typically used for background data synchronization, search re-indexing, reporting, and maintenance operations that should not run during a web request.
  • Event-driven architecture: The IContentEvents service acts as a central hub for subscribing to events such as SavingContent, PublishedContent, and DeletedContent.
  • Cloud environment considerations: In Optimizely PaaS environments, job execution depends on active application instances and correct scheduler configuration to avoid duplicate executions across multiple servers.
  • Integration pattern: When implementing IContentProvider solutions, scheduled jobs are typically used to refresh external data, while content events notify external systems about changes occurring within the CMS.

Introduction

In Optimizely CMS 12, enterprise content platforms often require automation and integration beyond manual editorial workflows. Two essential mechanisms that support this capability are Scheduled Jobs and Content Events.

Scheduled Jobs allow developers to implement background automation that runs periodically or on demand. These jobs execute outside the standard request pipeline and are ideal for tasks such as synchronizing external data sources, generating reports, refreshing search indexes, or performing maintenance activities.

Content Events, by contrast, enable an event-driven programming model. Developers can subscribe to content lifecycle events and execute custom logic whenever content is created, updated, published, moved, or deleted.

When building integrations with external systems—especially when implementing custom IContentProvider solutions—these mechanisms become essential for maintaining data consistency, cache integrity, and synchronization between Optimizely and external services.

Section 1: Scheduled Jobs

Scheduled Jobs are automated background processes that run at predefined intervals or can be triggered manually through the Optimizely administration interface.

They are designed for tasks that do not require immediate user interaction and can safely execute asynchronously without blocking editorial workflows.

Overview of Scheduled Jobs

  • Execution Context: Scheduled jobs typically execute under an anonymous security context. If operations require elevated permissions (for example updating content through IContentRepository), the job must explicitly set the user context.
  • Server Dependency: At least one application instance must be running for scheduled jobs to execute. In PaaS environments (such as Azure Web Apps), configurations like Always On ensure that the scheduler remains active even when the site receives no traffic.
  • Management and Monitoring: Jobs are configured and monitored from the Optimizely CMS Admin interface, where administrators can manually trigger execution and view job logs.
  • Built-in Maintenance Jobs: A standard Optimizely installation includes several predefined jobs for system maintenance tasks such as clearing the trash bin, publishing scheduled content, and maintaining internal indexes.

Common Use Cases

  • External Data Synchronization Scheduled jobs can periodically fetch data from external APIs and update CMS caches or data structures used by IContentProvider implementations.
  • Content Lifecycle Automation Jobs can automatically archive outdated content, move expired pages, or apply business rules to content structures.
  • Search Index Maintenance External content exposed via IContentProvider may require scheduled re-indexing to ensure search platforms (e.g., Optimizely Search & Navigation or Elasticsearch) reflect the latest data.
  • Analytics and Reporting Jobs can generate aggregated reports from content data and send them to analytics platforms or internal reporting systems.

Implementing a Scheduled Job

Custom scheduled jobs are created by implementing a class that inherits from EPiServer.Scheduler.ScheduledJobBase. The job must be decorated with ScheduledPlugInAttribute so it can be registered and managed within the CMS administration interface.

using System; using System.Security.Principal; using System.Threading; using EPiServer.Core; using EPiServer.PlugIn; using EPiServer.Scheduler; using EPiServer.ServiceLocation; using EPiServer.Security; using EPiServer.Web; // For IPrincipalAccessor namespace MyOptimizelySite.Jobs { // Decorate with ScheduledPlugInAttribute to register the job in CMS Admin [ScheduledPlugIn( DisplayName = "My External Content Sync Job", Description = "Synchronizes external content data on a schedule.", GUID = "d6619008-3e76-4886-b3c7-9a025a0c2603", // Unique GUID is highly recommended SortIndex = 100, // Optional: for ordering in Admin UI InitialDisplay = ScheduledPlugInDisplay.Minimized, // Optional: default display state IntervalLength = 1, // Default interval, can be changed in Admin IntervalType = ScheduledIntervalType.Hours, // Default interval type Restartable = true // Allows the job to resume if the application restarts )] public class ExternalContentSyncJob : ScheduledJobBase { private bool _stopSignaled; private readonly IContentRepository _contentRepository; private readonly IPrincipalAccessor _principalAccessor; private readonly ExternalApiService _externalApiService; // Your external data service // Constructor for dependency injection public ExternalContentSyncJob( IContentRepository contentRepository, IPrincipalAccessor principalAccessor, ExternalApiService externalApiService) { IsStoppable = true; // Makes the job stoppable from Admin UI _contentRepository = contentRepository; _principalAccessor = principalAccessor; _externalApiService = externalApiService; } public override void Stop() { _stopSignaled = true; } public override string Execute() { OnStatusChanged("Starting execution of My External Content Sync Job..."); using (AclBypass.Create()) { var originalPrincipal = _principalAccessor.Principal; try { _principalAccessor.Principal = new GenericPrincipal( new GenericIdentity("ScheduledJobUser"), new[] { "WebAdmins", "WebEditors" } ); var updatedCount = 0; try { var externalItems = _externalApiService.GetAllExternalItems(); foreach (var item in externalItems) { if (_stopSignaled) { return "Job stopped by user."; } updatedCount++; OnStatusChanged($"Processed item: {item.Id}. Total processed: {updatedCount}"); } } catch (Exception ex) { OnStatusChanged($"Error during job execution: {ex.Message}"); return $"Job failed with error: {ex.Message}"; } } finally { _principalAccessor.Principal = originalPrincipal; } } return $"My External Content Sync Job completed. Processed {updatedCount} items."; } private Guid GetContentGuid(string externalId) { return Guid.Parse(new Guid(externalId.GetHashCode().ToString("X8") + "000000000000000000000000").ToString()); } } }

Key Considerations for Scheduled Jobs

  • User Context Management In CMS 12, PrincipalInfo.CurrentPrincipal is read-only. Developers must use IPrincipalAccessor to manipulate the current user context when elevated permissions are required.
  • Multi-Server Environments When running Optimizely in scaled environments, scheduler configuration must ensure that only one instance executes scheduled jobs. This can be controlled through EpiServer:Cms:Scheduler configuration.
  • Restartable Jobs Setting Restartable = true allows jobs to resume if the application restarts unexpectedly. This requires idempotent logic so partially executed tasks can safely continue.
  • Performance Management Long-running operations should be optimized through batching, streaming, or background processing to prevent resource contention.

Section 2: Content Events

Content Events provide an event-driven mechanism for responding to lifecycle changes affecting IContent objects.

Through the IContentEvents service, developers can subscribe to events triggered when content is created, updated, published, moved, or deleted. These hooks allow custom logic to run before or after core CMS operations occur.

Overview of Content Events

  • Event-Driven Architecture The IContentEvents interface acts as a centralized event dispatcher for content lifecycle events.
  • Synchronous Execution Most event handlers execute synchronously within the request pipeline. Heavy operations should therefore be delegated to background services.
  • Global Scope Event subscriptions apply across the entire CMS instance, making them ideal for cross-cutting integration logic.

Subscribing to Content Events

using System; using EPiServer.Core; using EPiServer.ServiceLocation; using EPiServer.Framework; using EPiServer.Framework.Initialization; using Microsoft.Extensions.Logging; using Microsoft.Extensions.DependencyInjection; namespace MyOptimizelySite.Events { [InitializableModule] [ModuleDependency(typeof(EPiServer.Web.InitializationModule))] public class ExternalContentEventHandler : IInitializableModule { private IContentEvents _contentEvents; public void Initialize(InitializationEngine context) { _contentEvents = context.Locate.Advanced.GetInstance(); _contentEvents.PublishedContent += OnPublishedContent; _contentEvents.SavingContent += OnSavingContent; } public void Uninitialize(InitializationEngine context) { if (_contentEvents != null) { _contentEvents.PublishedContent -= OnPublishedContent; _contentEvents.SavingContent -= OnSavingContent; } } public void Preload(string[] parameters) {} private void OnPublishedContent(object sender, ContentEventArgs e) { if (e.Content == null) return; } private void OnSavingContent(object sender, ContentEventArgs e) { if (e.Content == null) return; } } }

Conclusion

Scheduled Jobs and Content Events form two complementary automation mechanisms within Optimizely CMS 12.

Scheduled Jobs provide time-based automation for background processing tasks such as synchronization, indexing, and reporting. Content Events enable reactive integration patterns by triggering logic in response to content lifecycle changes.

When used together—particularly in architectures involving IContentProvider integrations—these mechanisms enable developers to build scalable, reliable content systems that remain synchronized with external platforms while maintaining high performance within the CMS environment.