Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
A guide to creating a custom property value converter in Umbraco
A Property Value Converter converts a property editor's database-stored value to another type. The converted value can be accessed from MVC Razor or any other Published Content API.
For example the standard Umbraco Core "Content Picker" stores a nodeId as String
type. However if you implement a converter it could return an IPublishedContent
object.
Published property values have four "Values":
Source - The raw data stored in the database, this is generally a String
Intermediate - An object of a type that is appropriate to the property, for example a nodeId should be an Int
or a collection of nodeIds would be an integer array, Int[]
Object - The object to be used when accessing the property using a Published Content API, for example UmbracoHelper's GetPropertyValue<T>
method
XPath - The object to be used when the property is accessed by XPath; This should generally be a String
or an XPathNodeIterator
PropertyValueConverters are automatically registered when implementing the interface. Any given PropertyEditor can only utilize a single PropertyValueConverter.
If you are implementing a PropertyValueConverter for a PropertyEditor that doesn't already have one, creating the PropertyValueConverter will automatically enable it. No further actions are needed.
If you aim to override an existing PropertyValueConverter, possibly from Umbraco or a package, additional steps are necessary. Deregister the existing one to prevent conflicts in this scenario.
The built-in PropertyValueConverters included with Umbraco, are currently marked as internal. This means you will not be able to remove them by type since the type isn't accessible outside of the namespace. In order to remove such PropertyValueConverters, you will need to look up the instance by name and then deregister it by the instance. This could be the case for other PropertyValueConverters included by packages as well, depending on the implementation details.
Implement IPropertyValueConverter
from the Umbraco.Cms.Core.PropertyEditors
namespace on your class
This method is called for each PublishedPropertyType (Document Type Property) at application startup. By returning True
your value converter will be registered for that property type and your conversion methods will be executed whenever that value is requested.
Example: Checking if the IPublishedPropertyType EditorAlias property is equal to the alias of the core content editor. This check is a string comparison but we recommend creating a constant for it to avoid spelling errors:
This method is called to determine if the passed-in value is a value, and is of the level specified. There's a basic implementation of this in PropertyValueConverterBase
.
This is where you can specify the type returned by this Converter. This type will be used by ModelsBuilder to return data from properties using this Converter in the proper type.
Example: Content Picker data is being converted to IPublishedContent
.
Here you specify which level the property value is cached at.
A property value can be cached at the following levels:
PropertyCacheLevel.Unknown
Do not use this cache level unless you know exactly what you're doing. We recommend using the PropertyCacheLevel.Element
level.
PropertyCacheLevel.Element
The property value will be cached until its element is modified. The element is what holds (or owns) the property. For example:
For properties used at the page level, the element is the entire page.
For properties contained within Block List items, the element is the individual Block List item.
This is the most commonly used cache level and should be your default, unless you have specific reasons to do otherwise.
PropertyCacheLevel.Elements
The property value will be cached until any element (see above) is changed. This means that any change to any page will clear the property value cache.
This is particularly useful for property values that contain references to other content or elements. For example, this cache level is utilized by the Content Picker to clear its property values from the cache upon content updates.
PropertyCacheLevel.Snapshot
The property value will only be cached for the duration of the current snapshot.
A snapshot represents a point in time. For example, a snapshot is created for every content request from the frontend. When accessing a property in a snapshot using this cache level, it gets converted, cached throughout the snapshot, and later cleared.
For all intents and purposes, think of this cache level as "per request". If your property value should only be cached per request, this is the cache level you should use. Use it with caution, as the added property conversions incur a performance penalty.
PropertyCacheLevel.None
The property value will never be cached. Every time a property value is accessed (even within the same snapshot) property conversion is performed explicitly.
Use this cache level with extreme caution, as it incurs a massive performance penalty.
There are a few different levels of conversion which can occur.
This method should convert the raw data value into an appropriate type. For example, a node identifier stored as a String
should be converted to an Int
or Udi
.
Include a using Umbraco.Extensions;
to be able to use the TryConvertTo
extension method.
This method converts the Intermediate to an Object. The returned value is used by the GetPropertyValue<T>
method of IPublishedContent
.
The below example converts the nodeId (converted to Int
or Udi
by ConvertSourceToIntermediate) into an 'IPublishedContent' object.
This method converts the Intermediate to XPath. The return value should generally be of type String
or XPathNodeIterator
.
In the example below, we convert the nodeId (converted by ConvertSourceToIntermediate) back into a String
.
Content Picker to IPublishedContent
using IPropertyValueConverter
interface
This guide is currently being re-evaluated, as it might not work as intended.
Before reading this document we highly recommend that you familiarise yourself with the basics of developing a custom Property Editor for Umbraco.
In order for your editor to become a Block Editor you must setup your property editor through C#. The constructor of the class can be auto generated by your Integrated Development Environment (IDE).
Notice how the PropertyEditorAsset
attribute is used to load the UnicornBlocks.controller.js
JavaScript file.
Your Property Editor will need a PropertyValueConverter
. Read more about Property Value Converters.
The Block Editor data structure consists of three main parts:
Layout: The Layout defines Blocks that each will reference (by UDI) a content item in the list of data. The Layout object pairs keys with Property Editor aliases and their value type varies based on setup.
ContentData: A list of content items based on ElementTypes (IPublishedElement).
SettingsData: A list of content items based on ElementTypes (IPublishedElement).
In the following example the layout object "MyOwn.UnicornBlocksEditor" is of type Array.
We created the BlockEditorModelObject to aid in managing the presented Data Structure in the Block Editor.
To get a better understanding of what the Model Object does for you, we need to look at some usages of the Model Object.
The layout
of a Block Editor can be any structure. Therefore the Model Object (BlockEditorModelObject) cannot maintain this data. Our usage of the Model Object becomes complex. We give it a reference to a layout
entry and perform an action that may need to reflect changes back to the layout
.
Since the origin of blocks is in the layout
the Model Object only can serve as a helper to maintain and create data. Therefore the Property Editor code will be using the layout
as origin, using the Model Object to help manage specific parts.
This is explained in more detail below.
Instantiate a Model Object and load dependencies. Provide the basic structure for the layout
property when receiving the reference to it:
Use the Model Object to create a Block and append the returned layout-entry to the layout
.
In the following example we will create a new block and append it at the appropriate location in the 'layout' object:
The layout-entries alone do not provide much value when displaying or editing Blocks.
Our Model Object allows obtaining a Block Object by parsing a block's layout-entry for a specific Block.
The Block Object provides data of interest. The most important of these properties are: Block configuration, a label and the Block content in the Element Type Data Model format. This Content-model is useful for building the UI for editing the Content of a Block.
This example uses the Model Object to retrive a Block Object for outputting its label in the console.
This similar example uses the Block Object for setting a value on the first property in the Blocks Content.
See blockEditorModelObject for the getBlockObject method for more information on the properties avaiable on a Block Object.
Removing a Block and destroying its data is done by calling the removeDataAndDestroyModel
method of the Model Object, which allows us to maintain the 'layout' object.
Your code will be based on working with Block Objects and therefore removal of a Block is be done by referring to a Block Object.
This example shows how to remove the first Block of our imaginary Block Editor and remove the block from our layout.
Block Objects are used extensively and should be available throughout your Block Editor's runtime, rather than created for each action.
You probably want to use BlockObjects for your property-editor view. We append them to layout entries, so you can access them as properties from the layout object.
The following example loops through a layout array to display the contentUdi of each block:
The package.manifest
JSON file format is used to describe one or more custom Umbraco property editors, grid editors or parameter editors. This page outlines the file format and properties found in the JSON.
This is a sample manifest, it is always stored in a folder in /App_Plugins/{YourPackageName}
, with the name package.manifest
You can also register your files by implementing a IManifestFilter
instead of creating a package.manifest
. Create a ManifestFilter.cs
file and implement the IManifestFilter
interface. Then define the composer using the IComposer
interface.
For a functional example, you will need to register the editor and create the HTML
, JS
, and CSS
files in the App_Plugins/Suggestions
folder. You can find some examples of registering the editor in the Suggestions.cs
file and within the files in the App_Plugins
folder. For more information, see the Creating a Property Editor article.
The manifest can contain seven root collections, none of them are mandatory:
From version 9.2, some additional root elements were added. Their purpose is to control and facilitate telemetry for the package but none of these are mandatory. The properties are:
name
- Allows you to specify a friendly name for your package that will be used for telemetry, if no name is specified the name of the folder will be used instead
version
- The version of your package, if this is not specified there will be no version specific information for your package
allowPackageTelemetry
- Allows you to entirely disable telemetry for your package if set to false, defaults to true.
Example package.manifest
propertyEditors
returns an array of property editor definitions, each object specifies an editor to make available to data types as an editor component. These editors are primarily property editors for content, media and members. They can also be made available as a macro parameter editor.
The basic values on any editor are alias
, name
and editor
. These three must be set. Furthermore the editor value is an object with additional configuration options, it must contain a view value.
alias
The alias of the editor, this must be unique, its recommended to prefix with your own "namespace".
name
The name visible to the user in the UI, should also be unique.
editor
Object containing editor configuration (see below).
isParameterEditor
enables the property editor as a macro parameter editor can be true
/false
.
prevalues
Configuration of editor prevalues (see below).
defaultConfig
Default configuration values (see below).
icon
A CSS class for the icon to be used in the 'Select Editor' dialog: e.g. icon-autofill
.
group
The group to place this editor in within the 'Select Editor' dialog. Use a new group name or alternatively use an existing one such as Pickers
.
defaultConfig
Provides a collection of default configuration values, in case the property editor is not configured or is using a parameter editor, which doesn't allow configuration. The object is a key/value collection and must match the prevalues
fields keys.
editor
Besides setting a view, the editor can also contain additional information.
view
Path to the HTML file to use for rendering the editor.
hideLabel
Turn the label on or off by using true
or false
, respectively.
valueType
Sets the database type the value is stored as, by default it's string
.
validation
Object describing required validators on the editor.
supportsReadOnly
Sets whether the editor supports read-only mode, if set to true, the editor is expected to have its own implementation of the read-only mode.
isReadOnly
Disables editing the value.
valueType
sets the kind of data the editor will save in the database, its default setting is string
. The available options are:
STRING
Stores the value as an nvarchar in the database
DATETIME
Stores the value as datetime in the database
TEXT
Stores the value as ntext in the database
INT
Stores the value as a bigint in the database
JSON
Stored as ntext and automatically serialized to a dynamic object
preValues
is a collection of prevalue editors, used for configuring the property editor, the prevalues object must return an array of editors, called fields
.
Each field contains a number of configuration values:
label
The label shown on the Data Type configuration screen
description
Help text displayed underneath the label
key
The key the prevalue is stored under (see below)
view
Path to the editor used to configure this prevalue (see below)
key
on a prevalue, determines where it's stored in the database. If you give your prevalue the key "wolf" then this key will be used in the prevalue table.
It also means when this property editor is used on a property, the prevalue will be exposed on the model's configuration object. This occurs inside the property editor's controller, as shown below:
view
config value points the prevalue editor to an editor to use. This follows the same concept as any other editor in Umbraco, but with prevalue editors there are a couple of conventions.
If you specify a name like boolean
then Umbraco will look at /wwwroot/umbraco/views/prevalueeditors/boolean/boolean.html
for the editor view. If you wish to use your own, you specify the path like /App_Plugins/{YourPackageName}/prevalue-editor.html
.
The defaultConfig object provides a collection of default configuration values in case the property editor is not configured or is using a parameter editor. This object is a key/value collection and must match the prevalue field keys.
Similar to how the propertyEditors
array defines one or more property editors, gridEditors
can be used to define editors specific to the grid. Setting up the default richtext editor in the Umbraco grid could look like:
However the default grid editors are already configured. You can see the Grid Editors page for more information on grid editors.
parameterEditors
returns an array of editor objects, each object specifies an editor to make available to macro parameters as an editor component. These editors work solely as parameter editors and will not show up on the property editors list.
The parameter editors array follows the same format as the property editors described above. However, it cannot contain prevalues since there are no configuration options for macro parameter editors.
javascript
returns a string[] of JavaScript files to load on application start
css
returns a string[] of css files to load on application start
bundleOptions
is an enumerable type that expects one of the following values:
Default
- The default bundling behavior for assets in the package folder where the assets will be bundled with the typical packages bundle.
None
- The assets in the package will not be processed at all and will all be requested as individual assets and will effectively be a bundle that has composite processing turned off for both debug and production.
Independent
- The packages assets will be processed as its own separate bundle. (In debug, files will not be processed)
The package.manifest JSON file has a hosted online JSON schema file. This allows editors such as Visual Studio, Rider, and Visual Studio Code to have autocomplete/intellisense support when creating and editing package.manifest files. This helps to avoid mistakes or errors when creating your package.manifest files.
To associate the hosted JSON schema file to all package.manifest files you will need to perform the following inside of Visual Studio 2015.
Tools -> Options
Browse down to Text Editor -> File Extension
Add manifest
into the Extension box
Select JSON Editor
from the dropdown and add the mapping
Open a package.manifest
file and ensure in the top left hand corner you see the schema with the URL set to http://json.schemastore.org/package.manifest
. You can also add the schema inline in the json file (see below).
To associate the hosted JSON schema file to all package.manifest files you will need to perform the following inside of Visual Studio 2015.
File -> Settings
Browse down to Editor -> File Types -> JSON
Add package.manifest
to the list of file pattern names.
Browse down to Languages & Frameworks -> Schemas and DTDs -> JSON Schema Mappings
Add new by clicking the +
symbol
Add package.manifest
as Name
Add https://json.schemastore.org/package.manifest
as the Schema File or URL, or choose package.manifest
from the Remote Schema URls
Add package.manifest
as File path pattern
Open a package.manifest
file and ensure in the bottom tool bar you see the schema is detected as package.manifest
.
To associate the hosted JSON schema file to all package.manifest files you will need to perform the following inside of Visual Studio Code editor.
File -> Preferences -> Settings. The Settings window opens.
In the User tab, go to Extensions -> JSON -> Schemas.
Select Edit in settings.json from the Schemas section.
Add the following snippet in the settings.json
file:
Editors like Visual Studio can use the $schema
notation in your file.
Generally Umbraco supports two different ways to declare a property editor. Most commonly one would create a package.manifest
file, and then use it for declaring one or more property editors. But as an alternative, property editors can also be declared using C#.
A property editor consists of a number of mandatory properties, and some optional ones as well. As such, the outer JSON object for the property editor has the following properties:
Name | Type | Required | Description |
---|---|---|---|
The editor
object then has the following properties:
Name | Type | Required | Description |
---|---|---|---|
A package manifest is a file specific to your package or custom code. This file is always stored in a folder in /App_Plugins/{YourPackageName}
, and with the name package.manifest
:
This example manifest specifies a Sir Trevor property editor via the propertyEditors
collection, and also adds a single JavaScript file via the javascript
property.
The actual Sir Trevor property editor has some additional configuration. It's a block based editor, so for instance it has a prevalue for setting the maximum amount of blocks allowed. In full, the package.manifest
file for the Sir Trevor package looks like:
The same property editor can be declared using C# instead using the DataEditor
class and decorating the class with the DataEditor
attribute:
Also notice how the PropertyEditorAsset
attribute is used to load the SirTrevor.controller.js
JavaScript file.
The DataEditor attribute shown in the example above is the primary component to declaring the property editor in C#. Notice that the first four properties must be set through the constructor.
As shown in the C# example, the PropertyEditorAsset attribute was used to make Umbraco load the specified JavaScript file.
The constructor of the attribute takes the type of the assets as the first parameter. Possible values are either AssetType.Javascript
or AssetType.Css
. The second parameter is the URL of the asset.
In the example above, the SirTrevorEditor
class doesn't really do much. For more basic property editors, the C# approach may require a bit more work compared to that of package.manifest
files. But as property editors grow in complexity, using C# becomes a bit more useful - and also lets you do things not possible with package.manifest
files.
The DataEditor class defines a virtual CreateConfigurationEditor
method. It returns a model which is used for the Angular view when editing the prevalues of a Data Type.
Virtual methods are methods declared in a parent class. These methods have a default implementation that can be overridden in classes that inherit from the parent class. For instance in the example below, we can override the method and provide our own SirTrevorConfigurationEditor
instead of what Umbraco returns by default.
In this case, the SirTrevorConfigurationEditor
class doesn't do much either - but notice that it inherits from ConfigurationEditor<SirTrevorConfiguration>
, meaning the configuration will be of type SirTrevorConfiguration
:
The referenced SirTrevorConfiguration
class is then what declares the configuration fields of when editing a Data Type using the Sir Trevor property editor:
A benefit of this approach (opposed to package.manifest
files) is that we can now refer to the configuration using a strongly typed model - eg. as in this example Razor view:
Both instances of IDataType
and PublishedDataType
have a Configuration
property. When looking across all data types and property editors, there is no common type for the configuration, so the return value is object
. To get the strongly typed model, you can either cast the configuration value on your own, or use the generic ConfigurationAs
extension method as shown above.
Like mentioned before, the SirTrevorConfigurationEditor
class doesn't really do much in this example with the Sir Trevor property editor. But the Multi Node Tree Picker and others of Umbraco's build in property editors also override the ToValueEditor
method.
This method is used when the strongly typed configuration value is converted to the model used by the Angular logic in the backoffice. So with the implementation of the MultiNodePickerConfigurationEditor class, some additional configuration fields are sent along. For instance that it's a multi picker and that the ID type should be URI's. These are configuration values that the user should not be able to edit, but the property editor may still rely on them.
Guide on how to work with and create Property Editors in Umbraco
This section describes how to work with and create Property Editors. A property editor is the editor used to insert content into Umbraco.
Reference for the package.manifest JSON file format to register one or more property editors for Umbraco.
Convert the stored property data value to a useful object returned by the Published Content APIs.
Use Property Actions to add additional functionaility to your custom property editors.
Learn how to build your own Block Editors.
Learn how to extend Property editors to track entity references inside the property editor.
Guide on how to implement Property Actions for Property Editors in Umbraco
Property Actions are a built-in feature that provide a generic place for secondary functionality for property editors.
Property Actions appear as a small button next to the label of the property, which expands to show the available actions. They are defined and implemented in the Property Editor, making it open as to what a Property Action is.
Property Editors are an array of objects defining each action. An action is defined by the following properties:
We use labelKey
and labelTokens
to retrieve a localized string that is displayed as the Actions label.
isDisabled
is used to disable an Action, which change the visual appearance and prevents interaction. Use this option when an action wouldn't provide any change. In the example above, the action remove all entries
would not have any impact if there is no entries.
The implementation of Property Actions varies depending on whether your Property Editor is implemented with a Controller or as a Component.
When your Property Editor is implemented with a Controller, use the following approach for the Property Action:
Follow this guide if your Property Editor is implemented as a Component. The Component must be configured to retrieve an optional reference to umbProperty
. The requirement must be optional because property-editors are implemented in scenarios where it's not presented.
See the following example:
See the following example for implementation of Property Actions in a Component, notice the difference is that we are parsing actions to this.umbProperty.setPropertyActions(...)
.
Name | Type | Required | Description |
---|---|---|---|
alias
string
Yes
A unique alias that identifies the property editor.
name
string
Yes
The friendly name of the property editor, shown in the Umbraco backoffice.
editor
object
Yes
This describes details about the editor. See the table below for further information.
icon
string
No
A CSS class for the icon to be used in the Select Editor dialog - eg: icon-autofill
.
group
string
No
The group to place this editor in within the Select Editor dialog. Use a new group name or alternatively use an existing one such as Pickers.
isParameterEditor
boolean
No
Enables the property editor as a macro parameter editor. Can be either true
or false
(default).
defaultConfig
object
No
Provides a collection of default configuration values, in cases the property editor is not configured or is used a parameter editor (which doesn't allow configuration). The object is a key/value collection and must match the prevalue fields keys.
view
string
Yes
This is the full path to the HTML view for your property editor.
hideLabel
bool
Yes
If set to true
, this hides the label for the property editor when used in Umbraco on a Document Type.
valueType
object
No
This is the type of data you want your property editor to save to Umbraco. Possible values are STRING
, JSON
, DATETIME
, TEXT
and INT
. Default is STRING
.
validation
object
No
Object describing required validators on the editor.
isReadOnly
boolean
No
If set to true this makes the property editor read only.
Alias
string
Yes
Gets the unique alias of the editor.
EditorType
Yes
Gets the type of the editor. Possible values are EditorType.PropertyValue
, EditorType.MacroParameter
or EditorType.Nothing
.
Name
string
Yes
Gets the friendly name of the editor.
View
string
Yes
Gets the view to use to render the editor.
ValueType
string
No
Gets or sets the type of the edited value.
HideLabel
boolean
No
Gets or sets a value indicating whether the editor should be displayed without its label.
Icon
string
No
Gets or sets an optional icon.
Group
string
No
Gets or sets an optional group.
IsDeprecated
boolean
No
Gets or sets a value indicating whether the value editor is deprecated.
Guide on how to implement tracking entity references for Property Editors in Umbraco
Property editors can be extended further to track entity references that may be selected or referenced inside the property editor. For example in the core of the CMS we have added this to numerous property editors.
A good example of this is the Media Picker. The CMS stores a reference to the selected media item, enabling the identification of content nodes that use that particular media item. This avoids it being accidentally deleted if it is being used.
When a content node is saved it will save the entity references as relations.
Go to the Media section.
Select a media item and click the Info tab.
Go to the Settings section.
Under the Relation Types folder, select Related Document relations and click Relations.
Go to the Settings section.
Expand the Data Types folder.
Select the Data Type you wish to view the references
Navigate to the Info tab.
The following example shows how to implement tracking for the inbuilt CMS property editor Content Picker. It will always add a specific media reference, regardless of what value is picked in the content picker. In your own implementations, you will need to parse the value stored from the property editor you are implementing. You will also need to find any references to picked items in order to track their references.