HomeDev GuideRecipesAPI Reference
Dev GuideAPI ReferenceUser GuideGitHubNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev Guide

Custom properties

Shows how to implement a custom property by selecting a suitable base type.

Most common Optimizely Content Management System (CMS) property types have built-in persisting and rendering support. In most cases, creating custom properties is unnecessary. Change the editorial experience without creating a custom property. However, if a custom property is needed, select a proper base type to inherit from, depending on your data type. Create a template for the data type to make the property visible on templates.

When defining a custom property:

  • PropertyDefinitionTypePlugInAttribute is now sealed and cannot be inherited.
  • Use PropertyDefinitionTypeAttribute via composition to register your custom property.
  • For types directly inheriting from PropertyData, you must specify the DataType property.

Use ContentReference<T> for typed references

ContentReference<T> stores references to a specific content type. The generic type ensures compile-time safety and better clarity when the property is bound to a known content type.

For example:

public IEnumerable<ContentReference<MyPageType>> LinkedPages { get; set; }

The following example implements a custom property by selecting a suitable base type (for example, PropertyString, PropertyNumber). The property holds an enumerable of ContentReference and stores it as a serialized string.

[PropertyDefinitionType]
public class LinkingProperty: PropertyLongString {
  public IEnumerable<ContentReference> LinkedReferences {
    get {
      if (!String.IsNullOrEmpty(LongString)) {
        var entries = LongString.Split(';');
        return entries.Select(e => ContentReference.Parse(e));
      }
      return null;
    }
    set {
      LongString = String.Join(";", value.Select(r => r.ToString()));
    }
  }

  public override object Value {
    get {
      return LinkedReferences;
    }
    set {
      IEnumerable<ContentReference> links = value as IEnumerable<ContentReference>;
      if (links != null) {
        LinkedReferences = links;
      } else {
        base.Value = value;
      }
    }
  }

  public override Type PropertyValueType {
    get {
      return typeof (IEnumerable<ContentReference>);
    }
  }

  public override object SaveData() {
    return LongString;
  }
}

PropertyDefinitionType for blocks

Block properties use the common PropertyDefinitionType. The block type is now defined using PropertyDefinition.ItemTypeReference, rather than directly on the property type.

For example:

[PropertyDefinitionType]
public class BlockLinkingProperty : PropertyContentReference
{
    public ItemTypeReference BlockType { get; set; }
}
📘

Note

This standardizes block references across CMS 13 and allows consistent binding in the Visual Builder.

Index references to other content from a custom property

A soft link indexer ensures data integrity when the custom property stores references to other content instances. When an editor tries to delete a content item, a warning displays if another content item holds a reference to it. If the custom property inherits PropertyContentReference, PropertyUrl, or PropertyXhtmlString, the base type indexer handles soft indexing. The following example shows a soft link indexer for the previous custom property:

[ServiceConfiguration(typeof (IPropertySoftLinkIndexer))]
public class LinkingPropertyIndexer: IPropertySoftLinkIndexer<IEnumerable<ContentReference>> {
  public IEnumerable<SoftLink> ResolveReferences(IEnumerable<ContentReference> propertyValue, IContent owner) {
    var softLinks = new List<SoftLink> ();
    foreach(var link in propertyValue) {
      var softLink = new SoftLink {
        OwnerContentLink = owner.ContentLink.ToReferenceWithoutVersion(),
          OwnerLanguage = (owner as ILocalizable)?.Language,
          ReferencedContentLink = link,
          SoftLinkType = ReferenceType.PageLinkReference
      };
      softLinks.Add(softLink);
    }
    return softLinks;
  }
}

The indexer registers with the IOC container for the IPropertySoftLinkIndexer interface. It implements the generic IPropertySoftLinkIndexer<T> interface where the generic argument is the PropertyValueType for the property implementation.