📋 Content Models
Custom Properties
📖 Docs

Custom Properties - Optimizely CMS 12

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


1. Built-in Property Types

.NET TypeCMS Property TypeEditor
stringLongString/ShortStringText input
XhtmlStringXHtmlStringTinyMCE
intNumberNumber input
doubleFloatNumberFloat input
boolBooleanCheckbox
DateTimeDateDate picker
ContentReferencePageReferencePage selector
ContentAreaContentAreaDrag & drop area
UrlUrlURL input
IList<string>StringListMulti-text
CategoryListCategoryCategory selector
LinkItemCollectionLinkCollectionLink list

2. Property Attributes

Display:

[Display(
    Name = "Heading",
    Description = "Help text for editors",
    GroupName = SystemTabNames.Content,
    Order = 10)]
public virtual string Heading { get; set; }

CultureSpecific:

// Different values per language
[CultureSpecific]
public virtual string LocalizedTitle { get; set; }

// Shared (no attribute = shared)
public virtual int SortOrder { get; set; }

UIHint:

[UIHint(UIHint.Image)]
public virtual ContentReference MainImage { get; set; }

[UIHint(UIHint.Video)]
public virtual ContentReference Video { get; set; }

[UIHint(UIHint.Textarea)]
public virtual string LongDescription { get; set; }

[UIHint("MultiLineText")]
public virtual string Notes { get; set; }

ScaffoldColumn:

// Hidden from Edit UI
[ScaffoldColumn(false)]
public virtual string InternalData { get; set; }

3. Custom Property Type

// 1. Property value class
[PropertyDefinitionTypePlugIn(DisplayName = "Color Picker")]
public class PropertyColor : PropertyString
{
    public Color Color
    {
        get => IsNull ? Color.Empty : Color.FromName(String);
        set => String = value.Name;
    }

    public override IPropertyControl CreatePropertyControl()
    {
        return new ColorPickerControl();
    }
}

4. PropertyList (Generic List)

// List of a custom type
[ContentType(DisplayName = "Article Page", GUID = "...")]
public class ArticlePage : PageData
{
    public virtual IList<LinkItem> RelatedLinks { get; set; }
    
    public virtual IList<string> Tags { get; set; }
    
    public virtual IList<ImageItem> Gallery { get; set; }
}

// Custom item type
public class ImageItem
{
    public ContentReference Image { get; set; }
    public string Caption { get; set; }
}

5. Selection Factory (Dropdown)

// SelectOne - Single select
[SelectOne(SelectionFactoryType = typeof(ColorSelectionFactory))]
[Display(Name = "Text Color")]
public virtual string TextColor { get; set; }

// SelectMany - Multi select
[SelectMany(SelectionFactoryType = typeof(TagSelectionFactory))]
[Display(Name = "Tags")]
public virtual string[] Tags { get; set; }

// Selection factory
public class ColorSelectionFactory : ISelectionFactory
{
    public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
    {
        return new[]
        {
            new SelectItem { Text = "Red", Value = "red" },
            new SelectItem { Text = "Blue", Value = "blue" },
            new SelectItem { Text = "Green", Value = "green" }
        };
    }
}

6. Configure Common Editors

// Override editor for a property type globally
[EditorDescriptorRegistration(
    TargetType = typeof(string),
    UIHint = "ColorPicker")]
public class ColorPickerEditorDescriptor : EditorDescriptor
{
    public override void ModifyMetadata(
        ExtendedMetadata metadata, 
        IEnumerable<Attribute> attributes)
    {
        ClientEditingClass = "my.colorpicker.Editor";
        base.ModifyMetadata(metadata, attributes);
    }
}

7. Property Settings

// Use property settings to configure editor behavior
[ContentType(DisplayName = "My Page", GUID = "...")]
public class MyPage : PageData
{
    [BackingType(typeof(PropertyXhtmlString))]
    [Settings(typeof(TinyMcePropertySettings))]
    public virtual XhtmlString CustomEditor { get; set; }
}

// TinyMCE settings
public class SimpleTinyMceSettings : TinyMcePropertySettings
{
    public override string GetConfiguration()
    {
        return new TinyMceConfiguration()
            .AddToolbarRow("bold italic | link")
            .Build();
    }
}

8. Block as Property (Inline Block)

// Use block as property (inline - stored with page)
[ContentType(DisplayName = "Article Page", GUID = "...")]
public class ArticlePage : PageData
{
    [Display(Name = "Hero Section")]
    public virtual HeroBlock HeroSection { get; set; }
    
    [Display(Name = "Feature Block")]
    public virtual FeatureBlock Feature { get; set; }
}
@* Render inline block *@
@Html.PropertyFor(m => m.CurrentPage.HeroSection)

9. Content Metadata Properties (Built-in)

All content items have:

// ContentData.Property[] - raw property collection
var rawValue = content.Property["Heading"];

// IContent built-in
content.ContentLink    // ContentReference
content.ContentGuid   // Guid
content.Name           // string
content.ParentLink    // ContentReference
content.ContentTypeID // int
content.IsDeleted     // bool

Review Questions

  1. Which editor does XhtmlString use? (TinyMCE)
  2. What does the [CultureSpecific] attribute do? (Property has different values per language)
  3. What does [UIHint(UIHint.Image)] suggest to the CMS? (Use the image picker editor)
  4. How does SelectOne differ from SelectMany? (Single dropdown vs multi-select)
  5. How does an inline block property differ from a shared block? (Inline: stored with the page; Shared: stored separately, reusable)