📋 Content Models
Container Pages
📖 Docs

Container Pages - Optimizely CMS 12

Exam Area: Content Area 5 – Content Models (25%)
Reference: https://docs.developers.optimizely.com/content-management-system/docs/container-pages


1. What Is a Container Page?

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.


2. Creating a Container Page

// 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 { }

3. AvailableContentTypes Attribute

// 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 { }

4. IContentRepository - Setting Default Values

// 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;
        }
    }
}

5. Container Page vs Regular PageData

FeatureContainer PageRegular Page
Template (Controller/View)NoneYes
Content organizationPrimary purposeSecondary
Visible to visitorsNo (redirect/404)Yes
AvailableContentTypesTypically usedRarely used
ExampleArticles HubArticle Detail

6. Redirect from Container Page

// 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();
    }
}

7. Page Tree Organization

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)

8. Combining Container with ContentArea

[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; }
}

Review Questions

  1. What is a container page used for? (Organizing content in the tree, not rendered directly for visitors)
  2. What does AvailableContentTypes(Availability.Specific) do? (Only allows creating specific types inside)
  3. Does a container page need a controller/view? (Not required – typically has none)
  4. What does Availability.None mean? (No child content can be created)
  5. Where is the AvailableContentTypes attribute applied? (On the class of the parent/container page)