Exam Area: Content Area 5 – Content Models (25%)
Reference: https://docs.developers.optimizely.com/content-management-system/docs/container-pages
A container page is a page with no template, used to organize content in the page tree. It does not render directly for visitors.
Example: /articles-hub/ is a container for ArticlePage and NewsPage.
// Option 1: No template → CMS automatically treats as container
[ContentType(
DisplayName = "Articles Hub",
GUID = "f1a2b3c4-0000-0000-0000-000000000001")]
[AvailableContentTypes(
Availability = Availability.Specific,
Include = new[] { typeof(ArticlePage), typeof(NewsPage) })]
public class ArticlesHubPage : PageData
{
// No special properties needed
// No controller/view needed
}
// Option 2: Inherit PageData with [AvailableInEditMode(false)]
[ContentType(GUID = "...", AvailableInEditMode = false)]
public class SystemContainerPage : PageData { }
// Only allow specific types inside
[AvailableContentTypes(
Availability = Availability.Specific,
Include = new[] { typeof(ArticlePage) })]
public class ArticlesHubPage : PageData { }
// Allow all except certain types
[AvailableContentTypes(
Availability = Availability.All,
Exclude = new[] { typeof(StartPage) })]
public class ContentHubPage : PageData { }
// Allow nothing inside
[AvailableContentTypes(Availability = Availability.None)]
public class LeafPage : PageData { }
// When creating content in a container, set defaults
[InitializableModule]
public class DefaultsInit : IInitializableModule
{
private IContentEvents _events;
public void Initialize(InitializationEngine context)
{
_events = context.Locate.Advanced.GetInstance<IContentEvents>();
_events.CreatedContent += OnCreated;
}
public void Uninitialize(InitializationEngine context)
{
_events.CreatedContent -= OnCreated;
}
private void OnCreated(object sender, ContentEventArgs e)
{
if (e.Content is ArticlePage article)
{
if (article.PublishedDate == null)
article.PublishedDate = DateTime.Now;
}
}
}
| Feature | Container Page | Regular Page |
|---|---|---|
| Template (Controller/View) | None | Yes |
| Content organization | Primary purpose | Secondary |
| Visible to visitors | No (redirect/404) | Yes |
| AvailableContentTypes | Typically used | Rarely used |
| Example | Articles Hub | Article Detail |
// If container page is accessed directly → redirect
[ContentType(GUID = "...")]
public class ArticlesHubController : PageController<ArticlesHubPage>
{
private readonly IContentLoader _contentLoader;
public ArticlesHubController(IContentLoader contentLoader)
{
_contentLoader = contentLoader;
}
public ActionResult Index(ArticlesHubPage currentPage)
{
// Redirect to first child
var firstChild = _contentLoader
.GetChildren<ArticlePage>(currentPage.ContentLink)
.FirstOrDefault();
if (firstChild != null)
return RedirectToAction("Index", new { node = firstChild.ContentLink });
return NotFound();
}
}
Start Page
├── About Us (StandardPage)
├── Blog (ArticlesHubPage - container)
│ ├── Article 1 (ArticlePage)
│ ├── Article 2 (ArticlePage)
│ └── Article 3 (ArticlePage)
├── Products (ProductsHubPage - container)
│ ├── Product A (ProductPage)
│ └── Product B (ProductPage)
└── News (NewsHubPage - container)
└── News 1 (NewsPage)
[ContentType(GUID = "...")]
public class ArticlesHubPage : PageData
{
// Allows creating articles
// Just 1 intro ContentArea
[Display(Name = "Introduction")]
public virtual XhtmlString Introduction { get; set; }
[Display(Name = "Featured article")]
public virtual ContentReference FeaturedArticle { get; set; }
}
AvailableContentTypes(Availability.Specific) do? (Only allows creating specific types inside)Availability.None mean? (No child content can be created)AvailableContentTypes attribute applied? (On the class of the parent/container page)