Calling all developers! We invite you to provide your input on Feature Experimentation by completing this brief survey.
Calling all developers! We invite you to provide your input on Feature Experimentation by completing this brief survey.
This document describes the content model when working with different content types in EPiServer CMS.
The structure of the website is made up of pages, where the names of the pages automatically form structures and menus. You can create reusable smaller content parts for editing on the pages across your website, also called blocks.
Blocks defines a set of properties. For example a teaser block with a title, a description and maybe an URL to an image. You can define a block both as a property on a page or create a shared block.
Shared blocks are structured using folders in a library. The folder structure is also used to be able to set access rights for blocks. When creating and editing shared blocks you will get a similar experience as when working with pages.
Content areas are properties that are used for displaying a set of content objects such as shared blocks. An editor can manage what content to display by dragging pages and blocks to it. Items that have been added to a content area can be rearranged and deleted. You can also see on which pages each block is used, for example, if you are deleting a block you will be prompted to a dialog that shows you which pages are affected.
Content is separated from their presentation, so pages and blocks can be accessed using the API and rendered on a site using different templates in different context.
A template is responsible for rendering a page type. In EPiServer CMS a template defines which page types it can render, not the other way around, which gives a clean separation of model and presentation and allows for easier extensibility. Several templates (both Web Form and MVC ) can be registered for any content type (typically pages or blocks even if the underlying templating system supports any .NET type).
The template choosen to render a content instance depends on the specific context such as channel and tagging. For a template to be automatically registered it has to implement EPiServer.Web.IRenderTemplate<T> (where T states which model it can render). If you use a base classe for your template like PageBase<T>, ContentControlBase<T>, BlockControlBase<T>, PageController<T>, PartialContentController<T> or BlockController<T>, then you do not need to explicitly implement the interface since that is done by the base class. In addition the TemplateDescriptorAttribute can be used to specify more details about the template such as tags and inheritance, more information on that topic later.
A model is defined as shown in the following example:
[ContentType] public class MyPage : PageData { public virtual string Heading { get; set; } public virtual string MainIntro { get; set; } public virtual XhtmlString MainBody { get; set; } }
Given that there are templates defined as follows:
public partial class MyPageTemplate : TemplatePage{} [TemplateDescriptor(Tags = new string[] { RenderingTags.Mobile })] public partial class MyPageMobileTemplate : TemplatePage {} [TemplateDescriptor(Inherited = true)] public partial class MyFallbackTemplate : TemplatePage {} public partial class MyPageTeaser : ContentControlBase {} [TemplateDescriptor(Inherited = true)] public partial class PageTeaser : ContentControlBase {}
All of the above templates will be registered as templates for MyPage, which one that actually gets selected is dependent on the context as mentioned earlier.
It is possible to register a template for both a base type and an interface (see MyFallbackTemplate and PageTeaser above which are registered for PageData, not the specific type). In the case you want the template to be available for all subtypes as well, you need to mark it with Inherited=true on the TemplateDescriptor attribute. This can be useful, for example, if you want to have a fallback template for content types that still not have a specific template.
The algorithm to select a template is handled by TemplateResolver and the default implementation in the following way:
With “closest” above means the template model with shortest “inheritance chain”. That means that a template that is registered direct for the model will be preferred before a template registered for a base class or interface. For interfaces the length of the “inheritance chain” is defined by walking the inheritance chain upwards and see where the interface is implemented.
There is also possible in admin mode (on PageType/BlockType) to “override” which template that should be used by default. In that case the check for “closest” template will be skipped and instead that template model will be used.
So in the example above a request in a “ordinary web browser” for a page of type MyPage (assuming no DisplayChannel is active) the template MyPageTemplate will be selected since then no tag is active (disqualifies MyPageMobileTemplate) and MyPageTemplate has shorter inheritance chain than MyFallbackTemplate (MyPageTeaser and PageTeaser is partial renderers and will be filtered away when selecting a page renderer).
And in case there is a DisplayChannel named “Mobile” active for a request, for example, an implementation that check if request is from a mobile device, then the template MyPageMobileTemplate will be selected, since when selecting templates we will prefer templates that have a tag matching active channels.
The selection of a template for a block is similar to the selection for a page with the difference that since a block is rendered in the context of a parent page (either it is a property on a typed page or a shared block instance rendered in a content area) the selection of the block template is done in context of the page template. This means you can have both a User Control and a MVC Controller (for example, a partial view or a partial controller) then depending on if the block is rendered in the context of a Web Form page or a MVC page the suitable template will be selected automatically.
When rendering a ContentArea or a block property on a page you can also specify a tag to have specific templates for some areas on the site as follows:
<EPiServer:Property runat="server" PropertyName="MyContentArea"> <RenderSettings Tag="SideBar" /> </EPiServer:Property> <%: Html.PropertyFor(m => m.MyBlockProperty, new { Tag = RenderingTags.Sidebar })%> <%: Html.PropertyFor(m => m.MyContentArea, new { Tag = RenderingTags.Sidebar })%>
When specifying a tag then templates with a matching tag will always be preferred. Tags can also be used for pages.
Normally a block is displayed in the context of a page (for example, rendered inside a ContentArea). Therefore a block template is typically a partial template like a UserControl or Partial MVC controller/Partial View. However when a shared block instance is edited then we need to render the shared block instance with a page renderer. So for editing purposes it is useful to register a page renderer that can be used when editing shared block instances, for that purpose a reserved tag “Preview” is used.
A Web Form template is shown in the following example:
[TemplateDescriptor(Inherited=true, Tags= new string[]{RenderingTags.Preview})] public partial class PreviewBlock : SimplePage, IRenderTemplate
A MVC page controller for editing of shared block could be defined as shown in the following example:
[TemplateDescriptor(Inherited = true, Tags = new string[] { RenderingTags.Preview })] public class PreviewBlockController : ActionControllerBase, IRenderTemplate
As mentioned before instances implementing EPiServer.Web.IRenderTemplate
If there is a partial view in /View/Shared/TeaserBlock.cshtml that has a model
set to TeaserBlock, that partial view will also be automatically registered.
However since it is not possible to set TemplateDescriptor attribute on a partial
view it is also possible to implement
EPiServer.Web.Mvc.IViewTemplateModelRegistrator to register a partial view. A registration of a partial view through that interface is
shown in the following example: For Web Forms and User Controls the virtual path to the aspx or ascx has to be known to be able to load the page/user control. There is a possibility to specify the path on the
TemplateDescriptor attribute. If the path is not specified then we will try to resolve the path according to the namespace. That means that if your folder structure follows the namespace then it
is
not required to explicitly specify the path.[ContentType]
public class TeaserBlock : BlockData
{
public virtual string Heading { get; set; }
public virtual XhtmlString MainIntro { get; set; }
}
viewTemplateModelRegistrator.Add(typeof(TeaserBlock),
new DataAbstraction.TemplateModel()
{
Name = "SidebarTeaser",
Description = "Displays a teaser of a page.",
Path = "~/Views/Shared/SidebarTeaserBlock.cshtml",
Tags = new string[]{RenderingTags.Sidebar}
}
);
Path Resolving
Display Channels
Display channels can be used to
select template based on context, allowing the
editor to switch channels when previewing content.
A display channel is class that for each request
is matched to see if it is considered active or not (similar to a visitor
groups). There is a similar concept called Display Mode introduced in MVC 4, you
can use display modes in MVC 4 to create templates for display channels which is
very powerful integration.
Whenever a display channel is active templates with a tag that matches the channel will be preferred.
For example, if there is a display channel implementation that returns true for mobile devices (named “Mobile”) and then whenever that channel is active templates with tag “Mobile” will be preferred. That means you can choose to have duplicate templates for certain models, and then the one tagged with the name of a channel will be rendered when that channel is active. See Display Channels for more information.
See Also
Last updated: Mar 25, 2013