Page types - SITE DEVELOPMENT - EPiServer 7 CMS Development (2014)

EPiServer 7 CMS Development (2014)

II. SITE DEVELOPMENT

Chapter 7. Page types

Following up on the previous chapter, in this chapter we’ll take a closer look at one of the core concepts in EPiServer CMS; page types. While we learned to create page types in the previous chapter there’s actually quite a lot more to working with page types than what we saw there. As we’ll see, using either attributes in code or through a graphical interface in EPiServer’s admin mode we can define and control a number of settings that can (and should!) be used to enhance the user experience for editors.

One thing to note before we dive into what page types are and how to configure them is that in EPiServer 7 there is a broader concept than page types, namely content types. We’ll come back to other types of content later on and discuss page types here as those are what we used in the previous chapter. However, for future reference keep in mind that most of what we’ll learn in this chapter applies to all content types.

What is a page type?

In the previous chapter we learned how to create a page type by creating classes that inherited from PageData and was annotated with a ContentType attribute. After doing so it was possible to create pages of a page type with the same name as the class. Once created such a page was returned as an instance of our class from EPiServer’s API. So, does that mean that a page type is synonymous to a class? The short answer is no.

With that said, it could be. When an editor selects to create a new page EPiServer could locate all classes that inherit from PageData within the application and list them as available page types in the New Page dialog. Then, when saving the page to the database it could save all of the created page object’s public properties along with type information for the object, such as the full name of the class. When a page is requested the API could find the saved data for the page in the database, create an object based on the stored type information and populate its properties.

While such an approach may theoretically work, it wouldn’t be very flexible as page types would be limited to what is defined by the class. Also, the data for a page would be tightly coupled to the class, making it hard to move it between different sites if needed. The only way to make sense of the data would be the class.

Leaving the parallel universe in which a page type is synonymous to a class, a page type in EPiServer is a sort of meta object that is stored in EPiServer’s database. As such, a page type is not a class but instead an object of type PageType. However, as we saw in the previous chapter, we can create such objects by creating classes. I call those classes “page type classes”. Further, PageType objects are aware of which one of our classes it was that created it, or rather caused its creation.

So, how does EPiServer create page types based on our classes? Here’s how it works:

1. We create a class that inherits from PageData and is annotated with a ContentType attribute.

2. The site starts up or restarts and EPiServer’s initialization module starts.

3. EPiServer scans the application domain (the assemblies loaded for the application) and looks for classes that inherit from a content type (such as PageData) and have a ContentType attribute.

4. Upon finding a page type class EPiServer checks if there is already a page type created for it. If not a PageType object is created and saved to the database.

Of course, this isn’t exactly how it works but a simplified version. Anyhow, having a rough idea of how it works helps understand the relationship between page types and page type classes. While we can use classes to define page types a page type isn’t a class but rather an object stored in the database.

Configuring page types in admin mode

This model, storing definitions for types of content in a data source, is common in many CMSes and has the benefit that non-technical users and developers alike can modify them at runtime. EPiServer has a UI in its admin mode for doing just that.

Viewing a page type in admin mode, with the Settings button highlighted.

Viewing a page type in admin mode, with the Settings button highlighted.

Editing settings for a page type.

Editing settings for a page type.

Using the edit page type settings functionality in admin mode we can change various settings for the page type that effects how it’s presented, and how it can be used, within the CMS. For instance, we can change its display name. The display name is used when it’s displayed to editors, unless it’s not specified in which case the page type’s name is used instead.

Creating page types through admin mode

The above picture should make it fairly obvious that a page type is an entity of its own that, while generated from one of our classes, lives its own life in EPiServer’s database. As page types are objects stored in the database it seems logical that there should be other ways of creating them than by means of adding a new class to the application. Indeed there is. In admin mode, under the page types tab there’s UI functionality for creating new page types.

The dialog for creating a new page type in admin mode.

The dialog for creating a new page type in admin mode.

Creating page types this way, or programmatically by accessing the EPiServer’s API, was actually the only supported2 way of creating page types in earlier version of the CMS. While there may in theory be some benefits of creating page types through a graphical user interface many EPiServer developers were happy to see the ability to create page types using classes, a functionality often referred to as “typed pages”, introduced in version seven. Using classes is now the recommended way, but, as we’ve just seen it’s still possible to create page types in admin mode.

In the previous chapter we saw that pages of a page type created using one of our classes were returned from EPiServer’s API as instances of that class. What then happens with pages of a type created in admin mode? Such pages are returned as PageData objects (the base class for page type classes, remember?). Of course there won’t be any way to access properties that we’ve added to the page type (using functionality in admin mode) in a strongly typed manner. For instance, imagine that we’ve create a page type in admin mode with a string property named MainIntro and have created a page of this type. If we were to use code like the one below we’d see a compilation error.

PageData page = ...

string mainIntro = page.MainIntro; //Won't compile

So, how do we get the property’s value? The PageData class exposes property values in a couple of ways, all requiring us to supply the name of a property as a string. The most convenient is using indexer syntax, like this:

PageData page = ...

string mainIntro = page["MainIntro"] as string; //Will compile

So, what is a page type?

To summarize, a page type is an instance of EPiServer’s PageType class and it’s persisted in EPiServer`s database. A page type acts as a recipe for pages, defining what “EPiServer properties” or “page properties” a page of that type should have, as well as some other characteristics that we’ll look at soon.

When defining page types using classes, which is the recommended approach, EPiServer finds those classes during start up of the site and creates page types based on the code properties that it finds in the class (using reflection). Later, when retrieving a page of a page type that was created using a class EPiServer’s API creates an instance of that class and populates it’s properties.

The connection between page types and page type classes

During our earlier discussion about how EPiServer creates page types based on our classes I wrote that “upon finding a page type class EPiServer checks if there is already a page type created for it”. This is important. If EPiServer recognizes the class as an existing page type it doesn’t create a new page type but instead updates the existing one. If this mechanism wasn’t in place we would never be able to update existing page types using classes and we’d be seeing new page types added each time we compiled or otherwise restart the site.

So, how does EPiServer decide “if there is already a page type created for it”? For the classes that we created in chapter six it did so by matching the type information (class name, namespace and assembly name). This approach works, but only as long as that information doesn’t change. In other words, if we were to change the name of our StandardPage class to something else, such as “NormalPage”, we would see that we have a problem when looking at the site after having compiled the project. In edit mode we’d see that our page’s of the renamed type is marked as container pages, meaning that the CMS can’t find a template for them. As a consequence of this, and the fact that we’re filtering out pages that doesn’t have a template, no page except the start page is shown in the top menu.

In admin mode we’d see a new page type named “NormalPage” and a warning next to the “StandardPage” page type, indicating that the CMS expected to find a class matching this page type but can’t. In situations where there aren’t any pages of the type EPiServer simply removes it. But in this case there are, and EPiServer instead show us that there is an issue when we look in admin mode.

To avoid having issues like this we can connect our classes to the their corresponding page types in a more robust way. The fact is that comparing type information is only the CMS’ fall back method used when it can’t otherwise match a class with a page type. Prior to doing that it tries tomatch by GUID.

When a new page type is saved to EPiServer’s database it’s assigned a unique identifier in the form of a GUID3. We can specify this GUID in our classes by setting the GUID property in our ContentType attributes. When dealing with existing page types, like we’re currently doing, the GUID has already been generated and we’ll need to find the GUID value in admin mode. That’s easily done by navigating to the settings for a page type and looking at the “Advanced” section.

After having found the GUID in admin mode we can specify it in each page types class, like this:

usingEPiServer.Core;

usingEPiServer.DataAnnotations;

namespaceFruitCorp.Web.Models.Pages

{

[ContentType(

GUID = "1cfe38ca-025e-4f66-a237-226c44f586e9")]

publicclassStandardPage : PageData

{

publicvirtual string MainIntro { get; set; }

publicvirtual XhtmlString MainBody { get; set; }

}

}

warning

If you’re following the book by implementing the site that we’re building on your own computer it’s a good idea to set the GUID for each of the two page type classes now. However, be careful to copy the GUIDs from admin mode on your site and not from the book!

When creating new page type classes we can set the GUID property directly in the class prior to starting the site after having added the class. The synchronization that EPiServer does during initialization of the site will then used the GUID from the attribute instead of generating a new one. Of course, generating a GUID isn’t a task suitable for humans. Luckily, EPiServer’s Visual Studio integration has templates for content types that automatically add a ContentType attribute with a generated GUID.

The ContentType attribute

In the previous section we saw that the ContentType attribute class has a GUID property that we can set to more closely connect page type classes with their page types. In addition to that property the ContentType attribute also feature a number of other properties that we can use:

Name

Type

Default value

AvailableInEditMode

bool

true

Description

string

null

DisplayName

string

null

Order

int

0

GroupName

string

null

AvailableInEditMode

This property maps to the “Available in Edit mode” setting in admin mode and can be used to control whether editors should be able to create pages of this type, or more specifically whether it should show up in the list of page types in the New Page dialog.

For page types this property is usually left as its default value, true. There are typically two scenarios where we’d want to set it to false though. One is if the page type is only supposed to be used for pages that are created programmatically.

The other is that the page type is intended to be used for a limited set of pages and all of those pages already exists. For instance, we could set it to false in our StartPage class as we’ve already created the start page. Doing so helps limit the list of page types to choose from in the New Page dialog which can be a great help for editors in their daily work. By having fewer page types to choose from they can more easily find the one that they need and run less of a risk creating a page of the wrong type. However, as we’ll soon see there are also other ways to limit the list of page types to choose from.

Description and DisplayName

The DisplayName and Description properties map to the settings with the same name in admin mode.

Both of these control text that is displayed to editors in various situations, such as in the New Page dialog. For instance, we can specify them for our StandardPage class like this:

...

[ContentType(

GUID = "1cfe38ca-025e-4f66-a237-226c44f586e9",

DisplayName = "Standard",

Description = "Used for standard editorial content pages.")]

publicclassStandardPage : PageData

...

After compiling we’ll see the effect of these changes in the New Page dialog:

Using descriptive, helpful, text for these settings can be extremely useful for our users (the editors). Therefore one of the easiest way to check if an existing EPiServer site has been built with all the love and attention to detail that comes with taking pride in one’s craftsmanship is to look at the New Page dialog and see if the names and descriptions for content types have been set to help editors. However, while it’s possible to specify the display name and description through the ContentType attribute and in admin mode there is another, ofter better, way to do that, using “language files”. We’ll look at that later in this chapter.

Order

The Order property maps to the “Sort index” setting in admin mode.

This setting controls how the content type is sorted in a list together with other content types, for instance in the New Page dialog. A higher number means that the type should be sorted after types with a lower Order.

GroupName

Unlike the other properties of the ContentType attribute, except for GUID, the GroupName property doesn’t map to a setting in admin mode. When the property is set the value is used to group content types in the New Page dialog in edit mode. Let’s set it for both of our page types and see what happens.

Specifying GroupName in StartPage.cs.


...

[ContentType(

GUID = "3e06c3bd-4bdf-4bbd-b745-07df69567502",

GroupName = "Specialized")]

publicclassStartPage : PageData

...


Specifying GroupName in StandardPage.cs.


...

[ContentType(

GUID = "1cfe38ca-025e-4f66-a237-226c44f586e9",

GroupName = "Editorial")]

publicclassStandardPage : PageData

...


After compiling we’ll see that the setting indeed groups page types under different headlines in the New Page dialog. Of course, on our site there currently isn’t much to group as we only have two page types and they don’t share the same GroupName.

The GroupName property also has a minor but nice effect in admin mode. There, in the list of page types, it’s added as a prefix to the page type names wrapped in brackets.

The Revert to Default functionality

Looking at all of the screen shots of the page type settings UI you might have noticed a button at the bottom of the dialog with the text “Revert to Default”.

As we’ve seen, some settings such as the display name, is configurable both from code and from admin mode. Then what happens if both is used? If a setting has been specified in code its value will be seen in admin mode. From there it’s possible to change it and that change persists even if we later modify the same setting in code. In other words, settings in admin mode “wins” over settings in code. However, using the revert to default button it’s possible to remove all settings specified in admin mode and instead use settings from code. Or, as the button’s text says, revert to default values for settings.

What this means is that the values for settings that we configure in code isn’t actually the same as specifying the value for a setting. We’re specifying default values. This applies to the settings that we’ve looked at so far in the ContentType attribute as well as other settings that we’ll look at in a bit.

Localization

As we’ve seen it’s possible to customize names, descriptions and group names for page types by setting properties on the ContentType attribute. That approach works well as long as we only care about a single language. However, EPiServer’s UI is translated to several languages making it possible for users to use a language that suits them. If our content types appear in the UI with their names and descriptions in another language than the one the user is seeing the rest of the UI in they will stand out like a sore thumb.

The New Page dialog viewed in Spanish. You don't know English, now select the right page type ;-)

The New Page dialog viewed in Spanish. You don’t know English, now select the right page type ;-)

To address this issue we can use EPiServer’s localization system to translate many of the things we add to the CMS, including names, descriptions and groups for content types. By default the translation is done through the use of resource files known in the EPiServer community as “lang files”. These files are either placed in a folder named “lang” (hence their nickname in the community) in the web root or in some other place that has been configured in EPiServerFramework.config. In fact, the system doesn’t require us to use XML files at all but allow us to configure custom providers. However, using the convention over configuration approach and just placing XML files in a “lang” directory works well and also has the benefit that other developers instinctively know where to look.

To provide English translation for our two page types we can begin by creating a folder named “lang” in our project. Then we add a new XML file in the folder by right clicking on it, selecting AddNew Item and then select to add a new XML file (located under the Visual C# group of template in the Add New Item dialog). We’re free to give the file any name we want but seeing as we’ll put English translations for content types in it we’ll name it ContentTypesEN.xml.

The top level element in the file must be a languages element. Within that element we can define translations for one or more languages. To add an element for English translations we add an element name language with a name attribute set to “English” and an id attribute set to “en”. Here’s what it should look like:

<?xml version="1.0" encoding="utf-8" ?>

<languages>

<language name="English" id="en">

</language>

</languages>

Normally creating translations for one language is enough as someone else better suited for translating to other languages can create copies of the files that we have created, change the name and id attributes in the language element and then change the translations. However, if we also wanted to create translations for (for instance) Swedish right away we could add another file with the same structure as above but with a language element looking like this:

<language name="Svenska" id="sv">

</language>

Language files doesn’t have to follow any particular schema within language elements. However, when EPiServer looks for translations within its own UI, such as for page type names it does so using specific XPath4 expressions. For page types the XPath expression is “/pagetypes/pagetype[@name={page_type_name}]” followed by the specific thing that it wants to translate.

For name and description this means that we’ll need to add a pagetypes element containing one or more pagetype elements with name attributes matching the names of our page types. Following this syntax to add user friendly names and descriptions for our two page types the language file looks like this:

<?xml version="1.0" encoding="utf-8" ?>

<languages>

<language name="English" id="en">

<pagetypes>

<pagetype name="StartPage">

<name>Start page</name>

<description>

Used for the site's start page.

</description>

</pagetype>

<pagetype name="StandardPage">

<name>Standard page</name>

<description>

Used for standard editorial content pages.

</description>

</pagetype>

</pagetypes>

</language>

</languages>

Group names for page types are translated using the XPath expression “/pagetypes/groups/group[@name={group_name}]”. With translations for our two groups the language file looks like this:

<?xml version="1.0" encoding="utf-8" ?>

<languages>

<language name="English" id="en">

<pagetypes>

<pagetype name="StartPage">

<name>Start page</name>

<description>

Used for the site's start page.

</description>

</pagetype>

<pagetype name="StandardPage">

<name>Standard page</name>

<description>

Used for standard editorial content pages.

</description>

</pagetype>

<groups>

<group name="Editorial">Editorial</group>

<group name="Specialized">Specialized</group>

</groups>

</pagetypes>

</language>

</languages>

information

Localization of group names in the New Page dialog didn’t work in the first release of EPiServer 7. It’s however possible to validate that the language file is working by looking at the list of page types in admin mode where the translation does work. This bug (#90969) was fixed in Patch 1 for EPiServer 7.

As we’ll see later on in the book we can use these resource files to localize text in various other places, including in our templates using whatever XPath expressions we like.

Localization or ContentType attribute?

We’ve now seen no less than three ways to specify the display name and description for page types; using the ContentType attribute, using admin mode and using localization through language files. Which one should we use? As a general recommendation I’d say language files. By doing so we make it easy to extend the site with translations for additional languages and as compared to using the ContentType attribute changing text in a language file doesn’t require re-compilation. However, any way is better than not tending to these settings at all.

tip

Localize early

Specifying names, descriptions and the like isn’t the most fun you’ll have as a developer. However, it’s a whole lot less painful when done continuously as opposed to doing it all in one large batch. Doing it just after creating something, such as a new page type, also means that we specify these settings while our intention for how what we’ve just created will be used is fresh in memory.

The ImageUrl attribute

As I’m sure you’ve noted the New Page dialog’s list of page types that editors can select from shows each page type with an area with the text “No preview”. Using EPiServer’s ImageUrl attribute we can configure a relative path to an image that will be displayed there instead. The image should be 120 pixels wide and 90 pixels high.

But what pictures should we use? In theory it may seem like a good idea to use screenshots of a page of each respective page type. That’s also what the word “preview” in EPiServer’s “No preview” text indicates. However, given the relatively small dimension of the images it’s often hard to create such screen shots in a way that actually help editors distinguish between different types. Instead icons of some sort may be more helpful. Either way, these images can be a great help for editors when the list of types to choose from becomes long. Also, adding images makes the New Page dialog more attractive, thereby enhancing the user experience. Therefore, even if we can’t come up with good images for each type we can at least add some sort of default image, such as the company’s logotype.

Below are examples of using the ImageUrl attribute in our StartPage and StandardPage, here using icons from the Ecqlipse 2 icon set by Christian F. Burprich.

Specifying ImageUrl in StartPage.cs.


...

[ContentType(

GUID = "3e06c3bd-4bdf-4bbd-b745-07df69567502",

GroupName = "Specialized")]

[ImageUrl("~/Content/Icons/Home.png")]

publicclassStartPage : PageData

...


Specifying ImageUrl in StandardPage.cs.


...

[ContentType(

GUID = "1cfe38ca-025e-4f66-a237-226c44f586e9",

GroupName = "Editorial")]

[ImageUrl("~/Content/Icons/Standard.png")]

publicclassStandardPage : PageData

...


The result, when viewed in the New Page dialog, looks like this:

Inheritance

Page types, in terms of instances of the PageType class stored in EPiServer’s database, doesn’t support inheritance. However, C# classes do, meaning that page types classes can utilize inheritance. When EPiServer creates or updates a page type based on a class it iterates over all of the classes properties, no matter if they are defined in the specific class or somewhere up in its inheritance hierarchy. This means that our page type classes can inherit from other page type classes, or from classes that themselves aren’t page type classes. Of course, in order to be a page type class the class must in the end inherit from PageData so we can’t make them inherit from whichever class we want. But we can let them:

· Inherit from another page type class.

· Inherit from a class that inherits from PageData but doesn’t have a ContentType attribute.

· Inherit from an abstract class that inherits from PageData.

· Implement one or more interfaces.

There are a couple of scenarios in which we’d like to use inheritance for page types. One is reuse of properties. If several page types share a number of properties and also fulfill the “is a” criteria for inheritance they are a good fit for sharing a common base class. Another scenario is to utilize polymorphism. We may for instance want pages in the top menu to be able to influence how they are displayed there. Some may perhaps have an icon while other may be aligned to the right. We could define the interface for such characteristics in a common base class or in an interface which the top menu uses. That way the top menu doesn’t have to care what type of page it’s rendering a link to and can instead ask each individual page how it wants to be displayed.

While the latter, using polymorphism is perhaps the more interesting use case, the former is probably more common. For instance, on our site we want all sites to have a couple of meta data properties that we can render in the layout; title and meta description. In order to accomplish that we can add an abstract class that all of our page types can inherit from. We’ll call it BasePage.

Contents of BasePage.cs.


usingEPiServer.Core;

namespaceFruitCorp.Web.Models.Pages

{

publicabstractclassBasePage : PageData

{

publicvirtual string Title { get; set; }

publicvirtual string MetaDescription { get; set; }

}

}


Next we change the inheritance for both of our page type classes so that they inherit from BasePage instead of PageData:

Modified inheritance in StartPage.cs.


...

publicclassStartPage : BasePage

...


Modified inheritance in StandardPage.cs.


...

publicclassStandardPage : BasePage

...


After compiling we can take a look at one of our pages in forms editing mode and there see that it indeed has both of the properties that are defined in the base class.

Of course we have yet to render these properties anywhere. We’ll do that in our _Root layout. As all of our page types inherit from BasePage, and thereby all of our views have a model type that is a BasePage, we can begin by updating the _Root layout to use BasePage as its model type.

Using the model keyword in _Root.cshtml.


usingFruitCorp.Web.Helpers

@model FruitCorp.Web.Models.Pages.BasePage

...


With that done we can update the title element to use the Title property from the current page as content. We also add a meta tag for outputting the description.

Using the Title (line 7) and MetaDescription (line 3) properties in _Root.cshtml.


...

<head>

<meta name="viewport" content="width=device-width" />

<meta name="description" content="@Model.MetaDescription"/>

<link href="~/Content/bootstrap/bootstrap.min.css" rel="stylesheet">

<link href="~/Content/custom.css" rel="stylesheet">

<title>@Model.Title</title>

@Html.RenderEPiServerQuickNavigator()

</head>

...


At the moment the title and meta description on all of our pages will be empty unless we add values to the properties on each page. In the next chapter we’ll see how we can make the properties return default values fetched from other properties on the page if they haven’t been explicitly set.

warning

Use inheritance with caution

Being able to use inheritance for page types is very powerful. However, I’d advice you to use inheritance with caution. While you may produce less code by creating a complex inheritance hierarchy, that tends to produce an unwieldy mess later on. If you find yourself using inheritance for convenience reasons or to abide by the Don’t repeat yourself principle make sure you’re not violating the Liskov substitution principle in the process.

Available page types

As we’ve previously discussed, making it easy for editors to find the right page type in the New Page dialog is important. Both in terms of user experience for editors and in order to ensure that the site’s structure grows in an intended way. In order to facilitate that EPiServer supports specifying which page types it’s possible to create below a page of a given type. This can be done in admin mode by navigating to the settings for a page type and then selecting the “Available Page Types” tab.

In addition to specifying available page types in admin mode it can also be specified, or rather default values can be specified, in code. To do that we use the AvailablePageTypes attribute. The attribute has four properties; Include, Exclude, IncludeOn and ExcludeOn. All of these properties are of type Type[], meaning if we want to set any of them we do so by supplying an array of types.

Using the Include property we can specify what types of pages it should be possible to create below a page of the type we’re using the attribute on. The Exclude property works the other way around, meaning that Include works as a whitelist while Exclude works as a blacklist. IncludeOn andExcludeOn doesn’t effect the page type in which the attribute is used but instead either includes or excludes it from the list of available page types on the specified types.

With all four properties combined it’s possible to control the available page types setting in multiple ways. However, based on my experience I would recommend you to only use Include and Exclude. If we only use either Include and Exlude or IncludeOn and ExcludeOn it will be much easier to find where available page types is specified, and I’ve found that the two former works best. Therefore I normally use Include. On rare occasions I also use Exclude. Those occasions typically occur when I include a page type that other page types inherit from and, as the Include setting also includes subtypes, I need to exclude one or more of the subtypes.

Let’s try out the attribute. On our site there’s little point in editors being able to create pages of the StartPage type. This means that below pages of that type it should only be possible to create pages of type StandardPage. Also, below pages of type StandardPage it should also only be possible to create pages of the same type. To address that we can modify both page type classes by adding an AvailablePageTypes attributes looking like this:

[AvailablePageTypes(Include = new [] {typeof(StandardPage)})]

While we should do this in both StartPage.cs and StandardPage.cs, to exemplify here’s how the updated StartPage.cs should look after adding the attribute:

usingEPiServer.DataAnnotations;

namespaceFruitCorp.Web.Models.Pages

{

[ContentType(

GUID = "3e06c3bd-4bdf-4bbd-b745-07df69567502",

GroupName = "Specialized")]

[ImageUrl("~/Content/Icons/Home.png")]

[AvailablePageTypes(Include = new [] {typeof(StandardPage)})]

publicclassStartPage : BasePage

{

}

}

After compiling we’ll find that we’re only able to create pages of type StandardPage anywhere on the site except for directly below the root page. When looking in admin mode we’ll also see that the settings under the Available Page Types tab has been updated.

warning

Reverting do default

In case you aren’t seeing any effect after adding or updating the AvailablePageTypes attribute that may be because you have previously made some change to the page type in admin mode. Upon doing so the available page types setting was stored and the CMS now thinks that the setting has been explicitly set through admin mode and therefore doesn’t update it.

Given what we’ve previously learned about the relationship between attributes in page type classes and settings in admin mode it may seem natural to fix this by pressing the Revert to Default button. However, the Revert to Default button has no effect on the available page types setting. Instead, available page types can be reverted to the default values specified in code by first selecting the Use Default Settings radio button and then clicking the Save button.

In addition to the four type array properties the AvailablePageTypes attribute also has a constructor that requires a value from the Availability enum found in the EPiServer.DataAbstraction.PageTypeAvailability namespace. Using that constructor corresponds to selecting one of the radio buttons in admin mode and offers a less fine grained way of specifying available page types. The possible values are:

· Availability.All - All page types are available, no matter what the include/exclude properties say.

· Availability.None - No page types are available, no matter what the include/exclude properties say.

· Availability.Specific - Use the settings in the include/exclude properties.

· Availability.Undefined - The default value, leaving it up to the CMS to figure out the value, meaning that it will interpret it as All if none of the include/exclude properties are set, otherwise as Specific.

As an example, the below attribute will make it impossible to create a page of any type below pages of the type it’s used on:

//using EPiServer.DataAbstraction.PageTypeAvailability;

[AvailablePageTypes(Availability.None)]

I rarely use this constructor but instead use the Include and Exclude properties.

The Access attribute

In addition to controlling whether a page type can be created at all, using the ContentType attribute’s AvailableInEditMode property, and specifying which page types can be created below a specific page type it’s also possible to restrict creation of pages of a page type to specific users. As with most other settings for page types this can be done both through admin mode as well as by specifying (default) values in code.

As evident in the screenshot above, the default value for access rights for page types is that a special group named “Everyone” is able to create it, meaning that it’s unrestricted. In order to override the default value and specify what users, in terms of specific users and/or roles (groups), that should be able to create pages of a type we can use an attribute called Access. The Access attribute has the following string properties:

· Users - A comma separated list of user names that should be able to create pages.

· Roles - A comma separated list of role names who’s members should be able to create pages.

· VisitorGroups - A comma separated list of visitor group names who’s members should be able to create pages.

· NameSeparator - Can be used to override the default behavior of comma separating users/roles/visitor groups in the above properties and instead use something else a separator.

The attribute has a fifth property as well called Access (as in Access(Access = ...)) of type AccessLevel. We can however simply ignore that property as setting it to anything except AccessLevel.Create will cause a runtime exception during EPiServer’s synchronization of page types.

Using the Access attribute

On our site there doesn’t seem to be very much use for controlling which users should be able to create pages of either of our two page types. After all, the only user so far is ourselves. However, it would probably make sense to only allow administrators to create pages of the StartPage type. To do so we can use the Access attribute and set the Roles property to “CmsAdmins”, which is the name of a “virtual role” that members of groups named either “Administrators” or “WebAdmins” are automatically members of.

Adding the Access attribute to StartPage.cs


...

[Access(Roles = "CmsAdmins")]

publicclassStartPage : BasePage

...


Summary

In this chapter we’ve learned that page types are entities stored in EPiServer’s database. While they are stand alone entities and can be created in EPiServer’s admin mode the preferred way of creating them is by creating classes that inherit from PageData. When doing so the CMS locates these classes, our “page type classes”, during initialization of the site (on the first request to the site). As such page types and page type classes are closely related and we can use various attributes in page type classes to provide default values for settings that can later be overwritten in admin mode.

While it’s possible to change many settings, such as display name and description for non-developers in admin mode we, as developers, should tend to these settings. First of all some users may not feel comfortable working in admin mode. Second, some settings such as preview images and localization cannot be controlled from admin mode.

That is, an important part of EPiServer development is tending to the details that make or break a great user experience for editors. To illustrate that, imagine that you’re a non-technical user tasked with building up the structure and content on the site that we’re currently developing. As such you’ll be creating 300 pages of the standard page page type. The left screenshot in the image below shows the New Page dialog as it looked prior to this chapter. The right screenshot shows how the dialog looks after the changes that we’ve made during the chapter. Which one would you prefer to use 300 times?