Properties
Describes how to access and use properties in Optimizely Content Management System (CMS).
Properties store and present data for content such as pages and blocks. When you define a property on a content type, Optimizely Content Management System (CMS) stores the property as a PropertyDefinition
 class and backs runtime instances of the property type by types inheriting from PropertyData
. These classes deal with the data inside the property, such as protecting read-only state, preparing data from saving and loading, declaring value type, and so on. PropertyData
classes also can provide a default user interface.
Access a property on a read-only page
CMS returns content items in read-only mode from the API. The following code shows how to access a property that resides on the StandardPage
property on PageData
:
public class StandardPageController: PageControllerBase<StandardPage> {
public ActionResult Index(StandardPage currentPage) {
Object propertyValue = currentPage["PropertyName"];
// do something else and return view
}
}
The accessor determines whether the property with the name PropertyName
is a property on the page by checking Property
defined on IContentData
 implemented by PageData
(which inherits PageController
). It returns the property value if one exists. If the property is not on the page, a delegate is called to see if the property is found elsewhere. The default delegate looks for the property from the fetch data from function (if activated on the current page). The delegate checks the dynamic properties of the page. You can write your own delegate and change this behavior.
Access a property on a write-enabled page
If the page you are working with is write-enabled, the Property
accessor returns properties defined on the page; you never get dynamic properties or fetch data from properties when the page is in write mode.
Access properties on strongly typed content
When you access properties on a strongly typed page, work with that page as you work with an ordinary .NET object. Given a strongly typed page with the following signature:
[ContentType]
public class StandardPage: SitePageData {
public virtual XhtmlString MainBody {
get;
set;
}
}
Note
Why is the property declared as virtual? In the background, a proxy class is created for the page type, where the typed property on your class is connected to the backing
PropertyData
instance. This only works if properties are declared as virtual otherwise they cant be overridden. If the properties are not declared virtual, you need to implement get/set so they read/write data to the underlying property collection instead.
If you are using the generic EPiServer.Web.Mvc
in your page inheritance, you can access the MainBody
property like this:
public class StandardPageController: PageController<StandardPage> {
public ActionResult Index(StandardPage currentPage) {
var mainBody = currentPage.MainBody;
// do something else and return view
}
}
Extension methods for strongly typed content
Use the GetPropertyValue
 and SetPropertyValue
extension methods to take advantage of strongly typed models and also override the get and set behavior for the property. Add a using
 statement referencing the EPiServer.Core
 namespace to your class to use these extension methods.
The following example shows a strongly typed content model that has a fallback behavior for its PageHeading
property. If the editor sets the PageHeading
page
property, the PageHeading
property on the model returns the set value. If the editor does not set it, the property on the model returns the PageName
property instead:
[ContentType]
public class StandardPage: SitePageData
{
public virtual string PageHeading
{
get
{
var heading = this.GetPropertyValue(p => p.PageHeading);
// If heading is not set, fall back to PageName
return String.IsNullOrWhiteSpace(heading) ? PageName : heading;
}
set { this.SetPropertyValue(p => p.PageHeading, value); }
}
}
Access properties on non-strongly typed content
If a property is unknown or if you want to access several properties (for example, in a loop), you can access the value directly through its object representation.
public ActionResult Index(StandardPage currentPage) {
object propertyValue = currentPage["PropertyName"];
// do something else and return view
}
If you know the type of your value, you can cast it directly.
public ActionResult Index(StandardPage currentPage) {
string propertyValueString = (string) currentPage["PropertyName"];
// do something else and return view
}
If you are unsure of the actual type and want to avoid an exception, assign the property value with the as keyword:
public ActionResult Index(StandardPage currentPage) {
string propertyValueAsString = currentPage["PropertyName"] as string;
if (!String.IsNullOrEmpty(propertyValueAsString)) {
DoSomething();
}
// do something else and return view
}
Do not assume the value type for a property type. For example, the values of PropertyXhtmlString
are of type XhtmlString
, not string
. So, casting the value of a PropertyXhtmlString
to string causes an exception, and using the as string operation yields null.
Use GetPropertyValue for non-strongly typed content
The EPiServer.Core
 namespace has extension methods for ContentData
. If you import the EPiServer.Core
 namespace (for example, with a using statement in code behind), it extends ContentData
with the GetPropertyValue
 method can simplify property access by reducing the amount of code. For example, use GetPropertyValue
to get the string representation of a property without first checking against null or to provide a default value if the property is null.
The following usage examples show different overloads of GetPropertyValue
:
public ActionResult Index(StandardPage currentPage) {
// 1. Get as string, defaults to null and otherwise calls ToString for the value
string mainBody = currentPage.GetPropertyValue("MainBody");
// 2. Specify a fallback value
string mainBodyFallback = currentPage.GetPropertyValue("MainBody", string.Empty);
// 3. Which is equivalent to
string mainBodyFallback2 = currentPage.GetPropertyValue("MainBody") ?? string.Empty;
// 4. So a common usage is probably
string pageHeading = currentPage.GetPropertyValue("PageHeading", currentPage.PageName);
// 5. Get typed value, defaults to null if property is not set or defined
XhtmlString xhtml = currentPage.GetPropertyValue<XhtmlString>("MainBody");
// 6. Which supports fallback in the same way as the "ToString" overload
XhtmlString xhtmlWithFallback =
currentPage.GetPropertyValue<XhtmlString>("MainBody", new XhtmlString());
// 7. Advanced: Specify a conversion for the value
// Note: this example is very similar to 1. since we just call ToString, EXCEPT
// if MainBody is something else than an Xhtml property in which case 1. would still
// return the ToString representation while this would throw an exception since
// it tries to cast the value to an XhtmlString before handing it over to the
// conversion lambda
string convertedXhtml =
currentPage.GetPropertyValue<XhtmlString,string>("MainBody", x => x.ToString());
// 8. This is equivalent to 1. since it treats the property value as object
string mainBody2 =
currentPage.GetPropertyValue<object, string>("MainBody", x => x.ToString());
// do something else and return view
}
Use a property multiple times
If you access a property used multiple times in your code, you can save a reference to the property. Otherwise, the property is fetched from the internal collection for each call, which may also result in several unnecessary calls to the delegate if the property is not native to the page.
For example, use:
public ActionResult Index(StandardPage currentPage) {
PropertyData property = currentPage.Property["PropertyName"];
if (property != null && !property.IsNull) {
DoSomething();
}
// do something else and return view
}
Instead of:
public ActionResult Index(StandardPage currentPage) {
if (currentPage.Property["PropertyName"] != null && !currentPage.Property["PropertyName"].IsNull) {
DoSomething();
}
// do something else and return view
}
Check null values in properties
CMS does not store properties with an empty value in the database. If you access a property from code, it is null—not an empty string, 0, or false. This is because null is a convenient way to check if an editor does not set something or does not exist on this page. You have to compare with null regardless of the data type.
The following example throws a NullReferenceException
if the value is empty or missing:
public ActionResult Index(StandardPage currentPage) {
StringBuilder sb = new StringBuilder();
sb.Append(currentPage["PropertyName"].ToString());
// do something else and return view
}
StringBuilder.Append
 accepts null objects, so this example is better than the previous example:
public ActionResult Index(StandardPage currentPage) {
StringBuilder sb = new StringBuilder();
sb.Append(currentPage["PropertyName"]);
// do something else and return view
}
The following example throws a NullReferenceException
if the value is empty or missing:
<%= @ViewModel.CurrentPage.Property["PropertyName"].Value.ToString()%>
Markup accepts any type and converts to string, so you do not need to cast:
<%= @ViewModel.CurrentPage["PropertyName"] %>
The following example shows the fallback for src
attributes:
<img src='<%= @ViewModel.CurrentPage["ImageUrl"] ?? "/Missing.png" %>' />
To return a string, use the ??
operator as follows:
string s = currentPage["StringProperty"] as string ?? string.Empty;
The following example returns a value type:
DateTime date = (DateTime)(currentPage["DateProperty"] ?? DateTime.Now);
int i = (int)(currentPage["IntegerProperty"] ?? 0);
Another fallback in the markup using the ??
operator as follows:
<%= currentPage["Heading"] ?? currentPage.PageName %>
Update values for a page
If you want to update properties for an existing page, ensure you have a writable version of the page, as shown in the following example.
public ActionResult Index(StandardPage currentPage) {
PageData writablePage = currentPage.CreateWritableClone();
// do something else and return view
}
To set values, assign them directly to the writablePage
object by working against the properties for the most common page properties or by accessing the values directly:
writablePage.SomeProperty = "somevalue";
This is the same as:
writablePage["SomeProperty"] = "somevalue";
When you are done with the page, save it by using IContentRepository
(available in DI container):
_contentRepository.Save(writablePage, EPiServer.DataAccess.SaveAction.Publish);
Note
You cannot save dynamic properties or properties that reside on other pages when you are working against
IContentRepository.Save
.
HTML encoding and Optimizely CMS page properties
HTML has a special handling for characters such as < > and symbols ? & /. Some browsers may truncate or corrupt spaces, or problems arise when the browser displays these types of characters. For your website to be XHTML valid, use the HttpUtility.HtmlEncode method on strings that you inject with inline expressions. HttpUtility.HtmlEncode
is part of .NET Core. The HtmlEncode
method encodes a string that is displayed in a web browser.
When outputting strings directly in Razor views, normally, you do not need to encode strings. Razor, by default, encodes strings outputted inside a code block. The following example shows how to display the CMS Name
property:
<h2>@ViewModel.CurrentPage.Name</h2>
Updated 6 months ago