A demonstration project showcasing content event logging in Optimizely CMS 12, featuring a custom Google Cloud Storage blob provider.
- Content Events Logging: Comprehensive logging of all content lifecycle events (Create, Save, Publish, Delete, Move, etc.)
- API Endpoints: REST API to save and publish StartPage names and trigger content events
- Event Monitoring: Real-time logging of content operations with user information
- GCS Blob Provider: Custom implementation for storing media files in Google Cloud Storage
The ContentEventsLoggingModule
logs the following events:
- Creating/Created
- Saving/Saved
- Publishing/Published
- Deleting/Deleted
- Moving/Moved
- CheckingIn/CheckedIn
- CheckingOut/CheckedOut
- Rejecting/Rejected
Endpoint: PUT /api/StartPageAPI/save/{contentId}
Example:
PUT /api/StartPageAPI/save/5
Content-Type: application/json
{
"pageName": "My Updated Page Name"
}
Endpoint: PUT /api/StartPageAPI/publish/{contentId}
Example:
PUT /api/StartPageAPI/publish/5
Content-Type: application/json
{
"pageName": "My Published Page Name"
}
To access the CMS admin interface:
- Username:
sysadmin
- Password:
Sysadmin@1234
- Start the application
- Login to CMS admin (if needed) using credentials above
- Access Swagger UI at
/swagger
- Use either:
PUT /api/StartPageAPI/save/5
to save as draftPUT /api/StartPageAPI/publish/5
to publish immediately
- Update with any text (e.g., "Test Page Update")
- Check logs to see content events being triggered
[CONTENT EVENT] Saving - Type: StartPage, Name: 'Test Page Update', ID: 5, User: admin
[CONTENT EVENT] Saved - Type: StartPage, Name: 'Test Page Update', ID: 5, User: admin
[START PAGE EVENT] Saved - StartPage specific data logged for 'Test Page Update'
[CONTENT EVENT] Publishing - Type: StartPage, Name: 'Test Page Update', ID: 5, User: admin
[CONTENT EVENT] Published - Type: StartPage, Name: 'Test Page Update', ID: 5, User: admin
[START PAGE EVENT] Published - StartPage specific data logged for 'Test Page Update'
- Optimizely CMS 12
- .NET 6+
- Valid StartPage content with ID 5 (or adjust the test ID accordingly)
- Google Cloud Project with Storage API enabled (for GCS provider)
A custom blob storage implementation that allows Episerver CMS to store media files and blobs in Google Cloud Storage instead of the local file system or database.
Simple implementation with only 5 files:
- GcpBlobProvider (
GcsBlobProvider.cs
) - Main provider with direct GCS client integration - GcpBlob (
GcsBlob.cs
) - Individual blob representation - GcsSettings (
GcsSettings.cs
) - Configuration class - GcpBlobWriteStream (
GcpBlobWriteStream.cs
) - Write stream for GCS uploads - GcpBlobFileInfo (
GcpBlobFileInfo.cs
) - File metadata implementation
GCSProvider/
├── GcsBlobProvider.cs # Main provider
├── GcsBlob.cs # Individual blob representation
├── GcsSettings.cs # Configuration class
├── GcpBlobWriteStream.cs # Write stream implementation
└── GcpBlobFileInfo.cs # File info implementation
public class GcsSettings
{
public string BucketName { get; set; }
public int SignedUrlDurationMinutes { get; set; } = 60;
public bool UseSignedUrls { get; set; } = false;
}
{
"GcsSettings": {
"BucketName": "storage-bucket-u-version",
"UseSignedUrls": false,
"SignedUrlDurationMinutes": 60,
"GcpProjectId": "rebuild-cms-sandbox"
}
}
// Register GCS settings
services.Configure<GcsSettings>(Configuration.GetSection("GcsSettings"));
// Register the GCS blob provider as default
services.AddBlobProvider<GcpBlobProvider>("GcsBlobProvider", defaultProvider: true);
The implementation requires the following NuGet packages:
Google.Cloud.Storage.V1
(v4.13.0) - Google Cloud Storage client libraryEPiServer.CMS
(v12.29.0) - Episerver CMS framework
The provider uses Google Cloud Storage client with default authentication:
_storageClient = StorageClient.Create();
This relies on:
- Service account key files
- Environment variables (GOOGLE_APPLICATION_CREDENTIALS)
- Google Cloud SDK default credentials
- Google Cloud Project: Active GCP project with Storage API enabled
- Service Account: Service account with Storage Admin or Object Admin permissions
- Authentication: Set
GOOGLE_APPLICATION_CREDENTIALS
environment variable or use default credentials - Bucket Configuration: Configure bucket name in
appsettings.json
- Initialization: Provider initializes and creates GCS bucket if needed
- Upload: Media files uploaded through CMS are stored in GCS with URI-based object names
- Metadata Storage: CMS stores blob URI in content database, linking to GCS object
- Retrieval: CMS requests blobs by URI, provider maps URI to GCS object name and downloads
- Delete: Blob deletion removes objects from GCS bucket and cleans up metadata references
- Direct Integration: No factory patterns or abstractions - direct Google Cloud Storage client usage
- Automatic Bucket Creation: Creates GCS bucket automatically if it doesn't exist
- URI Mapping: Converts Episerver blob URIs to GCS object names seamlessly
- Error Handling: Graceful handling of missing objects and network issues
- Metadata Support: Preserves content type and file information
- Signed URLs: Optional support for time-limited direct access to GCS objects
The provider supports generating signed URLs for direct client access to GCS objects, useful for:
- Direct file downloads from frontend applications
- CDN integration with temporary access
- Mobile apps accessing files directly from GCS
- Large file downloads without server bandwidth usage
- ✅ Use when: Frontend needs direct blob URLs, CDN integration, large downloads, mobile apps
- ❌ Don't use when: Server-side processing, always-public content, high-frequency access, simple setups
Set UseSignedUrls: true
in your configuration:
{
"GcsSettings": {
"BucketName": "your-bucket-name",
"UseSignedUrls": true,
"SignedUrlDurationMinutes": 60
}
}
The implementation automatically uses your GOOGLE_APPLICATION_CREDENTIALS
environment variable for signing.