About a week ago I published my Extensions Manager, a Tridion 2011 extension that allows extensions’ creators define customizations for their users in a few lines of code. The customizations are exposed as fields to the users in a graphical interface that is easy to use and also takes care of the persistence of the values the users enter.
In my previous post I focused on the functional side of the Manager, the different features it has and the visual interaction.
In this post I’d like to focus on the programmatic side – how should an extension creator use it.
Registering an Extension
The main interaction with the Manager is done through the ‘Client’ class which encapsulates the functionality an Extension will need to register fields and use values from the Manager.
To begin, you will need to create a new Client instance. The full name of the class is: “Extensions.Configuration.Client“ but it can also be accessed using the shorthand: “$$ec.Client”.
The constructor of the client expects an ID string which will be the unique identifier of your extension. You should choose the name before beginning and stick with it. Make sure to choose a unique name which will not likely be used by other extensions.
The first line is then:
var configClient = new $$ec.Client("MyUniqueExtensionName");
As a general rule it will be good to build your extension as if the Extensions Manager doesn’t exist. This will allow you to use default values for anything that could possibly be set by a user. Depending on your situation your extension may be installed on environments outside your control and so you cant be sure the Manager will be available there.
This is how you can test for the Extensions Manager before using it:
var extManager = (typeof ($extConfManager) != “undefined” ? $extConfManager : null);
this._configClient = null;
if (extManager) //check that the Extensions Manager is available!
{
this._configClient = configClient = new $$ec.Client(“MyUniqueExtensionName”);
//start using using the client here…
}
The next thing to do is call the init() method, This is an asynchronous method that will attempt to retrieve the definition and values for your extension if it was already registered. Even if this is the first time your registration code is run on an environment calling init() is still required. The good thing is that the code you need for the first run and any subsequent runs is identical.
init() must be called before any other method of the client instance!
Since init() is asynchronous, it expects a single parameter which is a callback method. The callback receives 2 parameters. The first; ‘definition‘ an object containing the definition of the extension’s fields as it was downloaded from the server. if this the first time no fields will be available. The second; ‘loaded‘ a boolean stating whether the extension definition was successfully loaded from the server or not.
So this is how the call to init() should look like:
var extManager = (typeof ($extConfManager) != “undefined” ? $extConfManager : null);
this._configClient = null;
if (extManager) //check that the Extensions Manager is available!
{
this._configClient = configClient = new $$ec.Client(“MyUniqueExtensionName”);
configClient.init(function (definition, loaded)
{
if (!loaded)
{
//definition was not loaded from the server, this is probably the first time!
}
}
}
If ‘loaded‘ is true, the definition of the extension has already been stored on the server and there is no need to add the fields at this point. If its false though, this is when you should be adding your fields.
To add a field you call the client’s addField() method. this method accepts 3 parameters: ‘name‘, ‘props‘, ‘options‘:
- name: The unique field name. No other field registered for this extension should have the same name. The name can include spaces but shouldn’t include special characters.
- props: This is an object containing the different attributes of a field. These are the different attributes:
- Type: One of the following: $extConfConsts.Types.TEXT, $extConfConsts.Types.NUMBER, $extConfConsts.Types.OPTION, $extConfConsts.Types.GROUPS, $extConfConsts.Types.COLOR.
- AdminOnly: Boolean. Whether the field should be accessed by a Tridion administrator user only.
- MultipleValue: Boolean. Whether the field accepts more than one value. (only OPTION and GROUPS)
- HelpText: String. Containing useful information describing the purpose of the field.
- DefaultValue: String. Containing a default value for the field.
Besides the Type attribute, all others are optional.
- options: If the type of the field is OPTION, this parameter will be expected to contain an array of key/value objects that will be used as the options for this field.
For example:
[{ "key": "Yes", "value": "1" }, { "key": "No", "value": "0"}]
Once you’ve added your fields, you need to call the client’s create() method. This will commit the fields definition to be stored on the server.
create() receives an optional callback function parameter which will be called when the asynchronous task of committing the definition to the server has finished. the callback has one parameter which is a Boolean stating whether the commit was successful or not.
So tying all of this together will look like this:
Extensions.MyGreatNextExtension= function Extensions$MyGreatNextExtension()
{
Type.enableInterface(this, “Extensions.MyGreatNextExtension”);
this.addInterface(“Tridion.Cme.Command”, [“MyGreatNextExtension”]);
//************** Extensions Configuration Management **********************//
var extManager = (typeof ($extConfManager) != “undefined” ? $extConfManager : null);
this._configClient = null;
if (extManager) //check that the Extensions Manager is available!
{
this._configClient = configClient = new $$ec.Client(“MyGreatNextExtension”);
configClient.init(function (definition, loaded)
{
if (!loaded)
{
configClient.setTitle(“My Great Next Extension”);
configClient.setDescription(“This a <strong>great</strong> extension that does great things!”);
//** Users Fields
//add a simple text field
configClient.addField(“Text Field 1”, { “Type”: $extConfConsts.Types.TEXT, “HelpText”: “useful info about this field” });
//add a single valued options field
configClient.addField(“optionsField”, { “Type”: $extConfConsts.Types.OPTION, “Default”: “1” }, $extConfConsts.OptionGroups.YES_NO);
//** Admin Fields
//add a multi valued group field
configClient.addField(“Show For Groups”, { “Type”: $extConfConsts.Types.GROUPS, “AdminOnly”: true, “MultipleValue”: true, “HelpText”: “Only show this extension for the chosen groups” });
configClient.create();
}
});
}
//************** Extensions Configuration Management **********************//
};
You can see that I’ve also used the setTitle() and setDescription() methods. These are optional but recommended to use as this is the information that will show in the Manager GUI for your extension. Without it the only thing that will be shown is the ID you’ve used to register the extension.
You will probably want to put all of this code in the constructor of the command you use for your extension. It is very likely you would like to use the values retrieved from the server as quickly as possible so the constructor is the logical place.
Fields Validation
The Manager comes with two simple validator methods for the number and color fields. These are global validators which means they will be used against fields of these types from all of the registered extensions. As an extension creator you can also define your validators for field types used by your extension.
To register a validator that for example limits the range of numbers entered into NUMBER fields you can do something like this:
//Example of registering a validator for an extension
configClient.setFieldTypeValidator($extConfConsts.Types.NUMBER, function (value) //validate number fields for this extension
{
if (value.length > 0)
{
var num = parseInt(value);
return (num > 0 && num < 10); //only allow numbers between 1 and 9
}
});
Currently validation is done at a field type level, not on a specific field. using the setFieldTypeValidator() method you pass the type of the field and your validating function. This function will receive the value entered by the user into the field. If the value fails the validation, the user will not be able to save their changes until they correct it so validation is successful.
Example of failed validation:
Registering a validator through the Client will only apply it to values entered for your extension. Note that if you register a validator of your own, for example for the NUMBER type, the global validator will still be used in conjunction with yours.
You can also register your own global validator for a field type using the following method:
$extConfManager.setFieldTypeValidator(type, validator);
If there is already a global validator registered for this type this will override it and your validator be used for fields of this type from all extensions, even if youre not the one to build them so its preferable not to register global validators as a general principle.
Events
The Client raises a few events which you can hook up to from your extensions.
These events are:
- Initialized: Raised after the extension definition and values are loaded from the server. note that this event will be raised regardless of whether the definition and/or values are already stored on the server.
- Created: Raised when the the extension definition is successfully stored on the server.
- Reloaded: Raised when the extension’s values are returned from the server when the reload method
is called.
Important Considerations
Adding Fields
It is important to keep your field names unique, when trying to add a field, the Client will check its existing collection of fields for a field with the same name. If that field can be found, the Client will log an error and will not store the added field even if it has different properties.
To be able to update a field that has already been stored on the server, you can either delete the definition (XML) file and refresh the browser or you can use the Client’s updateField() method which has the same signature as the addField() method.
Client/Server communication
An interesting fact to be aware of is that the Extensions Manager tries to send as little over the wire between the server and the client as possible. This means a few things; For example when the user clicks the “save” button, the Manager will only send the extensions that had their values changed.
Another important aspect to consider is that the Client will only send the extension definition if the fields properties are different from those received from the server. If they’re not different calling create() will not actually commit to the server
Using the Values
We have covered the first part which is how to define the fields for our extension. Now we’ll go over how to use the values the users enter into the Manager’s GUI.
There are two main methods for doing so, the Client’s getValue() and getValues() methods.
Both methods require a single parameter which is the name of the field as it was defined when creating it.
The value(s) of all fields are stored in an array. If the field you’re interested in has only one value or you’re only interested in the first value you can use the getValue() method if you’re interested in all possible values the field has you will use the getValues().
Notice that getting the value(s) for an Admin field is no different from a regular field.
If the field was not defined with a default value and the user hasn’t entered a value yet, the result of getValue() and getValues() will be null.
Getting the value(s) of a field would look something like this:
if (this._configClient) //only get value if Manager was available at the start
{
var textValue = this._configClient.getValue(“Text Field 1”);
}
As mentioned earlier, it will be a good practice to build your extension with the Manager in mind but not as a dependency so you should define default values in your code to be used if the Manager is not available and no values were to be retrieved from it.
Miscellaneous
There are a few other useful methods in the Client class I want to cover here:
getTitle() Retrieves the title stored for the extension if one was provided.
getDescription() Retrieves the description of the title stored for the extension if one was provided.
getFields() Retrieves the collection of fields that were added to the client using the addField() or updateField methods.
hasField() Expects a single parameter: a String with the name of the field to check. Returns true if a field with this name was added to the client or returned from the server.
reload() Reloads the values for the extension from the server. Expects two parameters: the first; a callback function to call when the asynchronous reloading has finished. The callback method will receive two parameters: the first; an object containing the extension definition and values. the second is an optional context object. the second parameter the reload method receives is an optional context(state) object which will be passed to the callback method.
isGroupsValueAllowed() A utility method that expects the name of a group field. The method will then check whether the current user is a member of one of the groups that were chosen as values for this field. A second optional parameter is a Boolean stating whether the method should skip the check if the current user is an administrator.
Sample Code
There are a few samples I included with the Manager that show how to register an extension. These samples can be found in the samples.js file. By default these are not used but can be enabled from the EcmCommand.js lines 30 and 31.
Download
The latest version of the Extensions Manager can be downloaded using the link on this page.