by

What’s that Schema? A Basic Alchemy Plugin

Reading Time: 6 minutes

Ever looked at a component in Tridion and wondered, “what schema does that use?” I have. All the time. Opening up the component to find out what schema it uses is just too many clicks. Ain’t nobody got time for that!

I want to select an item, right-click on it, click on something in the context menu, and be told what schema that component uses. Since Alchemy 4 Tridion is what all the cool kids are using, I’ll use it to make a GUI extension that does exactly that.

Content Explorer in Tridion
Where do you come from‽

Some Prerequisites

I have a virtual machine with Visual Studio 2012 running. If you’re also using VS 2012, you may want to check out the other post I wrote like, 30 minutes ago.

Once I got those shenanigans squared away, I downloaded the Alchemy 4 Tridion Developer Pack. I was able to make the plugin in about 3 hours, and most of my time and energy was expended in Anguilla, trying to figure out how to do want I wanted.

An Anguilla-Based Alchemy Plugin

What I want to do here isn’t overly complicated; you can pull this off with just Anguilla. But, (!complicated) !== (!complex). I’d like to think that this is a “Clever” plugin, so that’s what I ended up naming it, “My Clever Plugin”.

The Parts

You can read the basics of an Alchemy plugin in the documentation. For this example, we need four pieces:

  1. A command
  2. A commandset that contains the command
  3. A resourcegroup that combines command and commandset
  4. A place to put it in the GUI (e.g. the context menu)

Adding the Files

All of these parts are templates that are available in the Alchemy 4 Tridion developer pack. Not only are they there, they’ve got some very helpful comments and instructions.

The Command

ctrl + shift + a or go to Add->New->File. Look in your A4T kit,and select Anguilla JavaScript command.

Anguilla JavaScript Command
Anguilla JavaScript Command

When you do that, this will be the code that you’ll see:

Type.registerNamespace("Alchemy.Plugins.${PluginName}.Commands");

// Constructor
Alchemy.Plugins["${PluginName}"].Commands.Command1 = function () {
    Type.enableInterface(this, "Alchemy.Plugins.${PluginName}.Commands.Command1");

    // ATTENTION: Modify "YourCommandName" to the name of your actual command
    this.addInterface("Tridion.Cme.Command", ["YourCommandName"]);
};

// Command Prototype Members
Alchemy.Plugins["${PluginName}"].Commands.Command1.prototype = {

    /**
     * Whether or not the item the command is attached to is available.
     */
    isAvailable: function (selection) {
        return true;
    },

    /**
     * Whether or not the item the command is attached to is enabled (if available and not enabled, will show as disabled state).
     */
    isEnabled: function (selection) {

    },

    /**
     * The action that is performed when someone clicks on the item the command is attached to.
     */
    _execute: function (selection) {

    }
};

We’ll do three things here, to get started:

  1. Replace all four instances of ${PluginName} with the name of the plugin — don’t forget to take out the $, too
  2. Replace YourCommandName on line 8 with the name of the command
  3. Put return true; in the isEnabled function on line 25

Adding Anguilla to Anguilla JS

Now that we’ve done that, let’s thrown some Anguilla at this. Before we do that, let’s evaluate the actual steps that we want to do here:

  1. Get the selection
  2. Get the schema of that selection
  3. Get the name of that schema
  4. Put the name of that schema in the message center
First, set your variables

When a thing is selected, that’s called selection. Of course, more than one item can be selected at once, so let’s just get the first one using getItem().

Every component has .getSchema(), and most anguilla objects have .getInfo();

        var item = $models.getItem(selection.getItem(0)),
            schema = item.getSchema(),
            schemaInfo = schema.getInfo();
Next, set a callback?

Man, Anguilla is weird. So, just getting a thing doesn’t mean you have the thing. You don’t get it until it’s loaded. So, what you actually need to do is bind a callback to an event:

        function schemaLoaded() {
            $messages.registerGoal("Schema Name:  " + schema.getTitle() );

        }
        $evt.addEventHandler(schema, "load", schemaLoaded);

We’re adding an event handler to the schema that we created. We’re using the load event. And when the load event fires, we fire the schemaLoaded() callback.

Inside of that callback, we’ll use the $messages interface to register a goal. What we’ll do is show the title of that schema. Alex Klock has a great blog post on how the message interface works.

Finally, load the schema

So, we’ve declared variables representing the item that’s selected. Then we set a variable for the item’s schema. And then a variable for the schema’s info. Next, we added an event handler to the schema, and a callback for that handler. Now, we have to load that schema. For safety’s sake, we’ll load the item, too:

        item.load(true);
        schema.load(true);

The final product looks like this:

Type.registerNamespace("Alchemy.Plugins.MyCleverPlugin.Commands");

// Constructor
Alchemy.Plugins["MyCleverPlugin"].Commands.GetSchemaName = function () {
    Type.enableInterface(this, "Alchemy.Plugins.MyCleverPlugin.Commands.GetSchemaName");

    // ATTENTION: Modify "YourCommandName" to the name of your actual command
    this.addInterface("Tridion.Cme.Command", ["GetSchemaName"]);
};

// Command Prototype Members
Alchemy.Plugins["MyCleverPlugin"].Commands.GetSchemaName.prototype = {

    /**
     * Whether or not the item the command is attached to is available.
     */
    isAvailable: function (selection) {
        return true;
    },

    /**
     * Whether or not the item the command is attached to is enabled (if available and not enabled, will show as disabled state).
     */
    isEnabled: function (selection) {
        return true;
    },

    /**
     * The action that is performed when someone clicks on the item the command is attached to.
     */
    _execute: function (selection) {
        var item = $models.getItem(selection.getItem(0)),
            schema = item.getSchema(),
            schemaInfo = schema.getInfo();
        function schemaLoaded() {
            $messages.registerGoal("Schema Name:  " + schema.getTitle() );

        }
        $evt.addEventHandler(item, 'load', function (e) {

        });
        $evt.addEventHandler(schema, "load", schemaLoaded);
        item.load(true);
        schema.load(true);
    }
};

The commandset

Next, again, using the Alchemy Developer Pack, we add a commandset.cs file. This is a very straightforward commandset; there’s only one command, after all:

using Alchemy4Tridion.Plugins.GUI.Configuration;

namespace MyCleverPlugin.Assets
{
    public class MyCommands : Alchemy4Tridion.Plugins.GUI.Configuration.CommandSet
    {
        public MyCommands()
        {
            AddCommand("GetSchemaName");
        }
    }
}

A Resource Group

The job of a ResourceGroup is to combine all of your resources together. It puts JavaScript, CSS, HTML, images, and commandsets all in the same place. Now, in the case of this particular plugin, the resource group is fairly small:

using Alchemy4Tridion.Plugins.GUI.Configuration;
using Alchemy4Tridion.Plugins.GUI.Configuration.Elements;

namespace MyCleverPlugin.Assets
{
    public class MyResourceGroup : Alchemy4Tridion.Plugins.GUI.Configuration.ResourceGroup
    {
        public MyResourceGroup()
        {
            AddFile("GetSchemaName.js");
            AddFile();
            Dependencies.AddAlchemyCore();
        }
    }
}

Add the JavaScript file. Then add the commandset (which I’ve called MyCommands). Then add the AlchemyCore … just because;

Put it Somewhere

Context Menu’s are easy, because, well, they’re part of the developer kit.

A context menu GUI extension establishes six things

  1. AssignId: just think of a good unique name
  2. InsertBefore: The thing in the menu it sits on top of. You’ll need to use your web browser’s dev tools to find this ID
  3. Name: A unique name for the menu
  4. AddItem: Context, the text that appears, the command
  5. Dependencies: Resource Groups and Sub menus count here
  6. Apply.ToView() … where you want this contextmenu to work
using Alchemy4Tridion.Plugins.GUI.Configuration;

namespace MyCleverPlugin.Assets
{
    public class MyContextMenu : Alchemy4Tridion.Plugins.GUI.Configuration.ContextMenuExtension
    {
        public MyContextMenu()
        {
            // Use this property to specify where in the context menu your items will go
            AssignId = "MyContextMenuId";
            InsertBefore = "cm_refresh";
            Name = "CleverContextMenu";
            AddItem("cm_my_context", "Schema Name", "GetSchemaName");

            // Use AddItem() or AddSubMenu() to add items for this context menu

            // Add a dependency to the resource group that contains the files/commands that this toolbar extension will use.
            Dependencies.Add();

            // apply the extension to a specific view.
            
            Apply.ToView(Constants.Views.DashboardView);
        }
    }
}

The build

My solution ends up with a total of four files:

  1. GetSchemaName.js for… getting the schema name with Anguilla
  2. MyCommands.cs that establishes my JavaScript as a command
  3. MyResourceGroup.cs that puts my files together in a single group
  4. MyContextMenu that puts it somewhere

Once I build it, I need to go to bin/Debug, find my .a4t file, and then drag and drop it into the Alchemy page within Tridion. It doesn’t get easier than that.

The Final Product

Now, I have a thing to click on in the context menu.

Schema Name in the context menu
Schema Name in the context menu

And, I have a message in the message center, too. Nifty. Or is it clever?

Name of Schema in Message Center
Name of Schema in Message Center

If you’re curious to see how my “Clever Plugin” turned out, of course, it’s on Github.

2 Comments



  1. Hey Frank – looks cool, have you submitted this extension to the alchemy web store yet?

Comments are closed.