Skip to main content

Outline

At a glance:
  • Code-first foundation: Architecture is defined in C#, enabling version control and strongly typed models
  • Inheritance model: Core functionality relies on inheriting from PageData, BlockData, or MediaData base classes
  • Data integrity: Explicit GUIDs in the [ContentType] attribute prevent data loss during class renaming or refactoring
  • Property requirements: Properties must be virtual with public accessors to support CMS runtime proxies and database persistence

Introduction

In Optimizely CMS 12, content architecture is established by defining content types in code. This code-first approach ensures that the content model is version-controlled, strongly typed, and easily deployable. Content types define the schema for data - such as pages, blocks, and media - specifying which properties are available to editors and how the data is structured within the CMS database. This module explores the technical implementation of page and block types, focusing on inheritance, property definition, and best practices for developing a robust content model.

Fundamental Base Classes

All content types in Optimizely CMS 12 must inherit from specific base classes provided in the EPiServer.Core namespace. These base classes provide built-in functionality for content management, including versioning, status handling, and common metadata.

  • PageData: Used for creating page types. A page represents a standalone URL and a node in the hierarchical site structure.
  • BlockData: Used for creating block types. Blocks are modular components that can be used as properties on other content or placed within a ContentArea.
  • MediaData: Used for defining media types, such as images, videos, or documents.

Implementation Pattern: The Common Base Class

It is a best practice to define an abstract base class for a site to encapsulate common properties such as SEO metadata or site-wide configuration. All concrete page types then inherit from this base rather than directly from PageData.

C#
using EPiServer.Core; using EPiServer.DataAbstraction; using EPiServer.DataAnnotations; using System.ComponentModel.DataAnnotations; namespace MyProject.Models.Pages { public abstract class SitePageData : PageData { [CultureSpecific] [Display( Name = "Meta Title", GroupName = "SEO", Order = 100)] public virtual string MetaTitle { get; set; } [CultureSpecific] [Display( Name = "Meta Description", GroupName = "SEO", Order = 200)] [UIHint(UIHint.Textarea)] public virtual string MetaDescription { get; set; } } }

The [ContentType] Attribute

Classes intended to be content types must be decorated with the [ContentType] attribute. This attribute allows the CMS to discover the class and register it as a content type in the database on application startup.

Key Parameters

  • GUID: A unique identifier for the content type. This is critical for refactoring - if the class is renamed or moved to a different namespace, Optimizely uses the GUID to maintain the connection to existing data in the database.
  • DisplayName: The name of the content type as it appears to editors in the CMS UI.
  • GroupName: Categorizes the content type in the "New Page" or "New Block" creation dialogs.
  • Description: Provides helper text for editors explaining the purpose of the type.
  • Order: Determines the sorting order in creation dialogs.

Important: Always generate and assign a GUID when creating a new content type. Never rely on class names alone for synchronization - renaming a class without a GUID will cause the CMS to treat it as a new type and orphan any existing content.

C#
[ContentType( DisplayName = "Standard Article Page", GUID = "B2A123BC-4567-8901-DEF0-ABCDEF123456", Description = "A standard page for news or blog internal articles.", GroupName = "Content")] public class ArticlePage : SitePageData { // Local properties defined here }

Defining Properties

Properties defined within a content type class become editable fields in the CMS UI. To allow Optimizely to manage data persistence correctly, all properties must follow two strict rules.

Requirements for property definitions:
  1. Public accessors: Properties must have public get and set accessors.
  2. The virtual keyword: Properties must be declared as virtual. This allows the CMS to create a proxy class at runtime to handle database operations efficiently.

Common Property Attributes

  • [Display]: Configures the UI label, ordering, and grouping (tabs) in edit mode.
  • [CultureSpecific]: Enables the property to hold unique values for different language branches.
  • [Required]: Ensures that editors must provide a value before saving or publishing.
  • [UIHint]: Provides a hint to the CMS about which editor control to render (e.g., textarea, image picker, color picker).

Property Type Mapping

Optimizely CMS 12 supports various .NET types, mapping them to specific CMS property types and UI controls:

.NET Type CMS Property Type Typical UI Element
string PropertyString (max 255) Textbox
string with [UIHint] PropertyLongString Textarea
XhtmlString PropertyXhtmlString TinyMCE (Rich Text)
int PropertyNumber Integer input
double PropertyFloatNumber Decimal input
bool PropertyBoolean Checkbox
DateTime PropertyDate Date/Time picker
ContentReference PropertyContentReference Content selector
Url PropertyUrl URL selector / link
ContentArea PropertyContentArea Drag-and-drop area
IList<string> PropertyStringList List of text strings

Refactoring Content Types

Maintaining content types over time often requires renaming classes or properties. Because Optimizely is code-first, specific strategies must be employed to avoid data loss:

  • GUID-based Renaming: If a class is renamed but the GUID remains the same, the system synchronizes the change automatically on next startup. No data migration is needed.
  • Migration API: For complex schema changes, developers can implement a class inheriting from MigrationStep to programmatically alter the content type schema in a controlled, repeatable way.
  • Data Preservation: When a content type class is deleted, the CMS retains database entries that contain data, marking them as "missing its code" to prevent accidental loss. Entries can be recovered by restoring the class.

Best Practices

Pro tip: Generate all GUIDs before your first deployment. Retrofitting GUIDs onto existing content types in production requires a careful migration step - it is far easier to include them from the start.

  • Always assign GUIDs: Never rely on class names alone for synchronization.
  • Use GroupNames: Organize properties into named tabs (e.g., "Content," "SEO") for a clean editing experience that scales as the content model grows.
  • Leverage UIHints: Ensure editors have the correct tools for each field, such as UIHint.Image for image references.
  • Consistent Naming: Adopt a naming convention that aligns with the business domain and remains readable to both developers and editors.
  • Limit Inheritance Depth: High levels of inheritance can make the content model difficult to maintain and reason about. Prefer composition using blocks over deep page type hierarchies.

Conclusion

Creating content types and page types is the foundational starting point for any Optimizely CMS 12 project. By defining clear, strongly-typed models in code, developers establish a solid foundation for both the editorial workflow and the rendering logic. Understanding the interplay between base classes, attributes, and property types allows for the creation of sophisticated digital experiences that are both scalable and editor-friendly.