Validating Tridion Content on Save using Anguilla, part 2 of 3

October 15th, 2012 | Posted by Robert Curlette in GUI Extension | Tridion | Tridion 2011

In this post we’ll continue Nuno’s series on Content Validation and create a GUI Extension using the Anguilla framework to validate a Component’s field content while exploring little-seen Anguilla JavaScript methods to inspect the field content and validate the Save events from the GUI.  Big thanks to Nuno Linhares for writing this article and allowing me to be the Editor.

Solution on Github:  https://github.com/rcurlette/ValidateTitleFieldPart2/

Summary:  Validate that the Component Title field contains an Uppercase letter as the first character after pressing the Save button and show a warning message if a number or lowercase letter is the first character.  We will use Anguilla JavaScript for the field validation.  The estimated time to complete this tutorial is around 90 minutes.  Here is what it will look like in the end:

Overview

  1. Create the Visual Studio Project , Folders and Files (empty)
  2. Create the GUI Extension configuration and setup IIS
  3. Add the Anguilla JavaScript code to Validate the Title Field
  4. Debugging

Create the Visual Studio Project and setup the file structure

We’re going to do this using the CME API (Anguilla) with a javascript alert, and we will extend the following 3 CME commands: “Save”, “Save and Close” and “Save and New”.
Let’s start by creating a Visual Studio Project for our requirements. I tend to start with an Empty Solution, then add the the “Editor” project.  In the next part of this tutorial we will also use a  “Model” project but not in this tutorial. (note that I use the project type “ASP.NET Empty Web Application”).

Delete the first project that Visual Studio always creates.

Now add 1 new project: ValidateTitleFieldEditor

Again, use the “Empty ASP.NET solution” template.

Let’s create the skeleton of our extension. In the Editor project, add the following folders:

  • Commands
  • Config

Under commands, let’s create a file named “ValidateTitleFieldCommand.js” – Visual Studio seems to believe people still use Jscript, let’s not get bothered by it.

Under Config create a file named “ValidateTitleFieldEditor.config”.  This will be used to configure our GUI Extension to ‘listen’ to the Save events.

You should have something similar to this in your project now:

Create the GUI Extension configuration and setup IIS

Here we will add the configuration to the ValidateTitleFieldEditor.config and much of it will not make any sense.  Please follow along and we will be at the fun part in no time.

Open the Configuration ValidateTitleFieldEditor.js file.

If you haven’t learned how to yet, make sure you add the Tridion Schemas to this editor. Right click anywhere on the config file, select properties.

Click on the … button for the Schemas, and add all schemas from [Tridion]\Web\WebUi\Core\Schemas

Tip: you may want to add these schemas to the default Visual Studio schemas, under “C:\Program Files (x86)\Microsoft Visual Studio 10.0\Xml\Schemas”

Let’s build our configuration file with the following:

<?xml version="1.0"?>
<Configuration xmlns="http://www.sdltridion.com/2009/GUI/Configuration/Merge"
               xmlns:cfg="http://www.sdltridion.com/2009/GUI/Configuration"
               xmlns:ext="http://www.sdltridion.com/2009/GUI/extensions"
               xmlns:cmenu="http://www.sdltridion.com/2009/GUI/extensions/ContextMenu">
  <resources cache="true">
    <cfg:filters />
    <cfg:groups>
      <cfg:group name="ValidateTitleField.CommandSet">
        <cfg:fileset>
          <cfg:file type="script">/Commands/ValidateTitleFieldCommand.js</cfg:file>
          <cfg:file type="reference">ValidateTitleField.Interface</cfg:file>
        </cfg:fileset>
        <cfg:dependencies>
          <cfg:dependency>Tridion.Web.UI.Editors.CME</cfg:dependency>
          <cfg:dependency>Tridion.Web.UI.Editors.CME.commands</cfg:dependency>
        </cfg:dependencies>
      </cfg:group>
    </cfg:groups>
  </resources>
  <definitionfiles />
  <extensions>
    <ext:dataextenders />
    <ext:editorextensions>
      <ext:editorextension target="CME">
        <ext:editurls />
        <ext:listdefinitions />
        <ext:taskbars />
        <ext:commands />
        <ext:commandextensions>
          <ext:commands>
            <ext:command name="Save" extendingcommand="ValidateTitleField"/>
          </ext:commands>
          <ext:dependencies>
            <cfg:dependency>ValidateTitleField.CommandSet</cfg:dependency>
          </ext:dependencies>
        </ext:commandextensions>
        <ext:contextmenus />
        <ext:lists />
        <ext:tabpages />
        <ext:toolbars />
        <ext:ribbontoolbars />
      </ext:editorextension>
    </ext:editorextensions>
  </extensions>
  <commands>
    <cfg:commandset id="ValidateTitleField.Interface">
      <cfg:command name="ValidateTitleField" implementation="Company.Extensions.ValidateTitleFieldCommand"/>
    </cfg:commandset>
  </commands>
  <contextmenus />
  <localization />
  <settings>
    <defaultpage />
    <editurls />
    <listdefinitions />
    <itemicons />
    <theme>
      <path />
    </theme>
    <customconfiguration>
    </customconfiguration>
  </settings>
</Configuration>

What are we doing in this configuration?

We’re defining 1 group of files:

<cfg:group name="ValidateTitleField.CommandSet">
        <cfg:fileset>
          <cfg:file type="script">/Commands/ValidateTitleFieldCommand.js</cfg:file>
          <cfg:file type="reference">ValidateTitleField.Interface</cfg:file>
        </cfg:fileset>
        <cfg:dependencies>
          <cfg:dependency>Tridion.Web.UI.Editors.CME</cfg:dependency>
          <cfg:dependency>Tridion.Web.UI.Editors.CME.commands</cfg:dependency>
        </cfg:dependencies>
      </cfg:group>

The group is a “CommandSet” and we’re basically telling Tridion to treat these files as a group, and that the files have dependencies on other Tridion items.

We’re also saying that this extension targets an existing editor: CME

<ext:editorextension target="CME">

 

And within this, we’re extending the “Save” Command with our own CommandSet.  Note:  This is where we wire up to the Save, Save and Close, and Save and New commands.

<ext:commandextensions>
          <ext:commands>
            <ext:command name="Save" extendingcommand="ValidateTitleField"/>
	    <ext:command name="SaveClose" extendingcommand="ValidateTitleField"/>
	    <ext:command name="SaveNew" extendingcommand="ValidateTitleField"/>
          </ext:commands>
          <ext:dependencies>
            <cfg:dependency>ValidateTitleField.CommandSet</cfg:dependency>
          </ext:dependencies>
        </ext:commandextensions>

 

And finally we’re registering a new CME Command:

<commands>
    <cfg:commandset id="ValidateTitleField.Interface">
      <cfg:command name="ValidateTitleField" implementation="Company.Extensions.ValidateTitleFieldCommand"/>
    </cfg:commandset>
  </commands>

That “implementation” attribute is the name of the Anguilla Class that we will use to implement the command.

 

Adding the extension to IIS and registering in Tridion

Let’s add our extension as a Virtual Directory to the CME’s “Editors” folder. I tend to point these directly to my development folder so that all code changes are immediate.

As you can see in the screenshot, the physical path should match the start of your “Editor” project.

Tridion Content Manager Explorer Configuration
The Tridion CME uses a file to hold all the editor and model configurations (plus quite a lot of other settings). You will find this file under [Tridion]\Web\WebUI\WebRoot\Configuration\System.Config

1. Make a backup copy of this file, and edit it with a proper XML Editor (like Visual Studio).
2. Find the Element named “<editors default=”CME”>” (around line 1000 in my system.config).
3. Add one editor configuration element with YOUR values to change the path:

<editor name="ValidateTitleFieldEditor">
      <installpath>D:\Tridion 2011\ValidateTitleField\ValidateTitleFieldEditor</installpath>
      <configuration>Config\ValidateTitleFieldEditor.config</configuration>
      <vdir>ValidateTitleField</vdir>
    </editor>

4. Restart IIS (iisreset) and reload the Tridion Content Manager Explorer.

Summary
We’re done with the boring part.  Now we can have some fun with the JavaScript action and doing something in the GUI.

 

Creating the JavaScript  for the Anguilla Save Event – The Command

Let’s create our Command. Open the “ValidateTitleFieldCommand.js” file.

For now, all that we want is to get our extension loading (and being able to verify it), so let’s add the following code:

Type.registerNamespace("Company.Extensions");

Company.Extensions.ValidateTitleFieldCommand = function ValidateTitleFieldCommand() {
    Type.enableInterface(this, "Company.Extensions.ValidateTitleFieldCommand");
    this.addInterface("Tridion.Cme.Command", ["ValidateTitleFieldCommand"]);
};

Company.Extensions.ValidateTitleFieldCommand.prototype._isAvailable = function ValidateTitleFieldCommand$_isAvailable(selection) {
    console.debug("Is Available called");
    return $cme.getCommand("Save")._isAvailable(selection);
};

Company.Extensions.ValidateTitleFieldCommand.prototype._isEnabled = function ValidateTitleFieldCommand$_isEnabled(selection) {
    console.debug("Is Enabled called");
    return $cme.getCommand("Save")._isEnabled(selection);
};

Company.Extensions.ValidateTitleFieldCommand.prototype._execute = function ValidateTitleFieldCommand$_execute(selection, pipeline) {
    console.debug("Execute called");
    return $cme.getCommand("Save")._execute(selection, pipeline);
};

This implements the _isAvailable, _isEnabled, and _execute functions required for every command. As you can see, all we’re doing is logging that the code was invoked (so we can verify it was loaded) and telling Tridion to go ahead and execute the “Save” Command.  The console.debug statement will write the output the the Firebug Console window or Console window in the Chrome Developer Tools.

We should now have enough to register this (Editor) extension with Tridion. To do this, we need to tell Tridion that we are an extension, and where the configuration for the extension is.

How do I know it’s working?

Well, there’s a few easy ways to do this. If you messed up something, chances are that your CME will look like this:

Open a Javascript console (Firebug is best here) and you’re likely to see this error:
Uncaught ReferenceError: Tridion is not defined

This is very generic, but usually comes with a very useful URL just before it:
http://t2011guruv3/WebUI/Editors/CME/Views/Dashboard/Dashboard_v6.1.0.55920.11_.aspx?mode=js
or http://$$SERVERNAME$$/WebUI/Editors/CME/Views/Dashboard/Dashboard_v$$TRIDION_CME_VERSION$$.$$CONFIGURATION_VERSION$$.aspx?mode=js

URL in FireBug GUI:

Open this url in your browser and you might have a lot more information (Chrome does not show the URL):

In this case, the error is caused because my Editor configuration in System.Config points to a vdir named “ValidateTitleFieldEditor” but the Virtual Directory in IIS is called “ValidateTitleField”. Changing the configuration to specify the correct vdir will fix the issue. If you had a different issue, chances are that opening this url in a browser will give you concrete error messages that should guide you into fixing your problem.

Once you fix your issue you should be able to load both the CME and the JS file:

Tip – if your Javascript is loading minified/obfuscated, you can change this setting.

If you were paying attention to any of this, you should know how to test if your command is being called. If you weren’t, here’s how.

  1. We configured our command as an extension to the “Save” Command (in ValidateTitleFieldEditor.config)
  2. We added 3 functions to our JS – _isAvailable, _isEnabled and _execute
  3. We added a “console.debug” instruction to all those functions.

So basically now we just need to:

  1. Clear your browser cache
  2. Reload the CME
  3. Open a Javascript debug console (I recommend chrome)
  4. Go somewhere in the CME where the Save command would be available (open a page or a component)

If you now open the console you should be able to see something like this:

This confirms our command is being called and our GUI Extension is working.

Now let’s tell our users about their mistake with a popup.  In the popup we will show them a message that the title field needs to start with a Captial letter.  This involves using the Anguilla API to inspect the field’s value when the Save, Save and Close, or Save and New button.

Before moving on, let’s do ‘Save All’ in Visual Studio.  Here’s a neat trick that might help you moving forward.  Instead of opening the project as we have defined now, try doing the following:

  1. Open Visual Studio
  2. Select File -> Open -> Website
  3. Select “Local IIS” on the left side, then search for the “SDL Tridion 2011” website, expand it and select “WebUI”.

Visual Studio is going to ask you if you want to upgrade your configuration to ASP.NET 2.0 – Select ‘No’ – do not ever say yes, unless you can safely claim you know what you’re doing.  If you have not yet saved all files, the first popup will ask if you want to ave files – for this select ‘yes’

Once it’s open you should now see your editor under “Editors”:

This loads the rest of the Tridion CME as well, which will:

  1. Make visual studio pretty slow at times
  2. Give you some small help with some IntelliSense (though you can’t trust the whole of it)

Now that we’re here, we’re going to try reading the current value of the “Title” field in the component you’re trying to save. So, just like with any other Tridion Extension, the first thing we’ll do is make sure we should be executing _at all_.

Conditions for executing are:

  1. Item you’re editing is a component
  2. Current component’s schema has a field named “Title”
  3. Value of this field does not start with an Uppercase letter.

Open the ValidateTitleFieldCommand.js file and modify the _execute method to determine if we should execute:

Company.Extensions.ValidateTitleFieldCommand.prototype._execute = function ValidateTitleFieldCommand$_execute(selection, pipeline) {
    console.debug("Execute called");
    var p = this.properties;
    var item = $display.getItem();
    if (item) {
        if (item.getItemType() == "tcm:16") {
            // We know it's a component
            var fieldBuilder = $display.getView().properties.controls.fieldBuilder;
            if (fieldBuilder.getField("Title")) {
                // We have a field named Title
                var titleField = fieldBuilder.getField("Title");
                if (titleField.getValues().length > 0) {
                    var fieldValue = titleField.getValues()[0];
                    console.debug("Current title value is " + fieldValue);
                    // Voodoo magic
                    var firstCharacter = fieldValue.substring(0, 1);
                    if (isNaN(firstCharacter)) {
                        if (firstCharacter != firstCharacter.toUpperCase()) {
                            console.debug("First character is NOT uppercase");
                        } else {
                            console.debug("First character is uppercase");
                        }
                    } else {
                        // Interestingly isNaN returns false on a space. Good to know, I guess
                        console.debug("First character is a number!");
                    }
                }
            }
        }
    }
    return $cme.getCommand("Save")._execute(selection, pipeline);
};

The fieldBuilder is the magic here that provides us the ability to read fields in the Tridion GUI.  Using the JavaScript FireBug Console we can set a breakpoint on this line and then go to the Console window and inspect the object for other properties and methods.

Also, notice the return statement where we allow the GUI to continue saving.

At this point we can start thinking about the real solution. You could, for instance, add an alert(“First letter of title must be Upper Case”) to your code and stop the execution of the Save command (commented the changes to the code):

Company.Extensions.ValidateTitleFieldCommand.prototype._execute = function ValidateTitleFieldCommand$_execute(selection, pipeline) {
    console.debug("Execute called");
    var item = $display.getItem();
    var failed = false;  // ** Added
    if (item) {
        if (item.getItemType() == "tcm:16") {
            // We know it's a component
            var fieldBuilder = $display.getView().properties.controls.fieldBuilder;
            if (fieldBuilder.getField("Title")) {
                // We have a field named Title
                var titleField = fieldBuilder.getField("Title");
                if (titleField.getValues().length > 0) {
                    var fieldValue = titleField.getValues()[0];
                    console.debug("Current title value is " + fieldValue);
                    // Voodoo magic
                    var firstCharacter = fieldValue.substring(0, 1);
                    if (isNaN(firstCharacter)) {
                        if (firstCharacter != firstCharacter.toUpperCase()) {
                            console.debug("First character is NOT uppercase");
                            // *** Show popup
                            alert("First character of 'Title' field must be uppercase");
                            failed = true;
                        } else {
                            console.debug("First character is uppercase");
                        }
                    } else {
                        // Interestingly isNaN returns false on a space. Good to know, I guess
                        console.debug("First character is a number!");
                        // *** Show popup
                        alert("First character of 'Title' field cannot be a number.");
                        failed = true;  // *** Stop save action
                    }
                }
            }
        }
    }
    if(!failed)
        // *** Continue saving
        return $cme.getCommand("Save")._execute(selection, pipeline);
};

Which would result in something like this showing up to your editors.  We did it!  Congratulations for making it this far!

Debugging

Let’s see if our GUI Extension code is firing:
– Open Firefox, FireBug, Script Tab
– Open a Component

Search for ‘ValidateTitleFieldCommand’ and around line 96404 you will see our new GUI Extension JavaScript code.  Here I place a breakpoint and then with pressing the Save button I can step through the code.

While stepping through code I can also go to the Console Window and see the other methods  and properties that are available for these objects.

Summary

The Tridion 2011 Anguilla Framework gives us the opportunity to provide friendlier feedback to the user and also increase the quality of content entered into Tridion by enforcing editorial guidelines.  We’ve used the Anguilla Framework to access the fieldBuilder object in the Tridion GUI and inspect the field contents, showing a warning if they do not meet our requirements.  In the next post we’ll update the popup and allow users to make changes to the content in the popup window itself.  Thanks again to Nuno for creating this awesome post.

Solution on Github:  https://github.com/rcurlette/ValidateTitleFieldPart2/

You can follow any responses to this entry through the RSS 2.0 You can leave a response, or trackback.

6 Responses

  • Mihai says:

    I was playing the other day with creating Schemas programmatically and I noticed there is this Pattern property on a SingleLineTextFieldDefinition, which allows you to specify a regular expression pattern. Only values matching that expression would be allowed (otherwise you cannot save the item using that Schema).

    SchemaFields fields = new SchemaFields(schema);
    fields.Fields.Add(new SingleLineTextFieldDefinition(“Email”) {
    Description = “Enter valid email address”,
    Pattern = @”^[\w._%+-]+@[\w.-]+\.[A-Za-z]{2,4}$”
    });

    The same logic can be achieved specifying the pattern directly in the Schema XSD (manually).

    My question is: why wouldn’t you use this OOTB mechanism?

  • robert curlette says:

    Hi Mihai,

    Thanks for the great suggestion. Yes, it is a cleaner and easier solution to edit the XSD source and insert a regular expression to validate the field. In the past the error message window did not provide good enough feedback to the user and sometimes the message was nested in a long error message.

    I have not yet tried this in 2011 but will do so and update the post if it is a clear and friendly error message.

    For more complicated business rules, such as if the Country is US then the State field must be filled in – but the State field is optional in the Schema, this solution works well and the RegEx would not be able to validate this more complicated solution.

    Thanks again for the good feedback!

    Robert

  • Alvin says:

    The troubleshooting tips are a nice touch. What should we call that empty GUI error that I’m sure we’ve all seen?

    Maybe “Almost Hello World?” “Mr P’s Response to a not-Quite-Well-Formed Question?”

  • Hey Mihai,

    Using schemas for the content validation was discussed in the 1st part of this tutorial, available here: http://nunolinhares.blogspot.com/2012/07/validating-content-on-save-part-1-of.html

    This 2nd part provides a more advanced way of doing the same thing. Obviously it is meant to teach people how to fish, not how to validate content or determine which approach to follow…

  • Sanony says:

    I have few question related to this post:

    1. If we want to implement the functionality for save, save & close and Save & New using single JavaScript file. While using above code, on save and close component/Page is getting save and not being closed. Same is the case with Save and New. Is there any way we can execute all above functionality in a single extension ?
    It is clear to understand that we are overriding the functionality of Save & Close and Save & New but how can we achieve the same functionality for save & close and save & new. We have added Save and close and Save and New to our configuration file.

    2. If we have made some changes in a component and wishes to check-in then it prompts for saving the component. Using above extension it allows to save the component without checking the component Title first character. Do we need to add check-in too in our config file.

  • Great questions. I’m afraid I don’t know the answers to them but I’d suggest to ask them on the Tridion StackExchange site here, tridion.stackexchange.com



Leave a Reply

Your email address will not be published. Required fields are marked *