GUI Extensions: Experimenting with Extended Areas

A little back I posted Event System code on Inheriting the Page Process setting on Structure Groups. I mentioned the missing piece: an “Inherit Page Process” like check box to have full control over the inheriting process. I decided to investigate completing this little extension and started looking at Site Edit 2012′s code where they added check boxes for “Enable Inline Editing”. This brought me to experiment with the <ext:extendedareas /> section of the GUI Extension configuration, and I thought I’d share what I learned in case anyone was wondering what these areas were for or how to use them.  NOTE that this is just my experimentation, and if you see anything stated incorrectly (or done in an inefficient way) please do comment!

The Good News

Extended Areas allows you to add your custom controls and code to existing areas in Views. Since I was looking for a way to add a “Inherit Page Process” check box right below the “Associated Page Process” dropdown in the Workflow Tab of the StructureGroup view, I thought that this would be the perfect way of adding my little extension.

How It Works

The extendedarea section contains another section where you get to target the view and the ExtendableArea control. The following comes from the configuration in the SiteEdit 2012 GUI Extension.

<ext:extension assignid="EnableSEForPublicationTarget" name="{Resources: Tridion.Web.UI.Editors.SiteEdit.Strings, SE}">
    <ext:control>~/Extensions/PublicationTarget/EnableSiteEdit.ascx</ext:control>
    <ext:pagetype></ext:pagetype>
    <ext:renderinblock>false</ext:renderinblock>
    <ext:apply>
        <ext:view name="PublicationTargetView">
            <ext:control id="PublicationTargetTab_ExtendableArea" />
        </ext:view>
    </ext:apply>
</ext:extension>

The above example looks for the Control <c:ExtendableArea id=”PublicationTargetTab_ExtendableArea” runat=”server” RenderInBlock=”false” /> on the PublicationTarget View (PublicationTarget.aspx). It will inject a custom control you define in the <ext:control>~/YourControl.ascx</ext:control> into the ExtendableArea control. Sounds good, right?

The Bad News

Unfortunately, not every view and area contains an ExtendableArea control (hopefully this changes in the future!). And unfortunately for me, the WorkflowTab on StructureGroups is one of those unlucky areas that does NOT contain an ExtendableArea control. :(

With the real extension I plan on posting once its finished up, I’ll probably be doing a lil JavaScript injection to get my customization showing up. But for the sake of science and still wanting to complete my experiment using extended areas, I committed an abomination and a no no for this little experiment…

Adding the ExtendedableArea Control Manually

To see my check box get added using an extended area, I added my own ExtendableArea Control to Tridion’s WorkflowStructureGroup.ascx (\WebUI\Editors\CME\Tabs\Workflow\).

<%@ Import Namespace="Tridion.Web.UI"%>
<%@ Control Language="C#" AutoEventWireup="true" Inherits="Tridion.Web.UI.Editors.CME.Tabs.WorkflowStructureGroup" ClassName="WorkflowStructureGroup" %>

<div id="ItemWorkflow">
    <div id="Workflow_StructureGroup">
        <fieldset>
            <div class="field">
                <label for="PageProcess">
                    <asp:Literal runat="server" Text="<%$ Resources: Tridion.Web.UI.Strings, TextPageProcess %>" />
                </label>
                <c:Dropdown id="PageProcess" runat="server" NullText="<%$ Resources: Tridion.Web.UI.Strings, None %>" disabled="true"/>
            </div>
        </fieldset>
        <c:ExtendableArea id="WorkflowStructureGroup_ExtendableArea" runat="server" RenderInBlock="false" />
    </div>
</div>

InheritFromParent.ascx

The following is just the markup for my custom checkbox.

<%@ Control Language="C#" AutoEventWireup="true" %>
<div class="form stack-elem fieldgroup">
    <div class="field">
        <label for="InheritPageProcess">Inherit Page Process</label>
        <div>
            <input type="checkbox" id="InheritPageProcess" />
        </div>
    </div>
</div>
<div class="stack-elem hr"></div>

InheritFromParent.ascx.js

Again, much thanks to the Tridion UI 2012 code for the PublicationTarget extension for me to look at as a refererence. The following JavaScript was used just for some quick testing.

Type.registerNamespace("Tahzoo.Extensions.StructureGroup");

Tahzoo.Extensions.StructureGroup.InheritFromParent = function Tahzoo$Extensions$InheritFromParent() {
    Tridion.OO.enableInterface(this, "Tahzoo.Extensions.StructureGroup.InheritFromParent");
    this.addInterface("Tridion.DisposableObject");

    this.properties.controls = {};
};

Tahzoo.Extensions.StructureGroup.InheritFromParent.prototype.initialize = function Tahzoo$Extensions$InheritFromParent$initialize() {
    var c = this.properties.controls;
    console.log($display.getItem().getTitle() + " Initialized!");
    c.inheritPageProcess = $("#InheritPageProcess");
    c.inheritPageProcess.disabled = true;

    $evt.addEventHandler(c.inheritPageProcess, "click", this.getDelegate(this._onInheritPageProcessClicked));

    var item = $display.getItem();
    if (item) {
        $evt.addEventHandler(item, "load", this.getDelegate(this._onItemLoaded));
        $evt.addEventHandler(item, "change", this.getDelegate(this._onItemChanged));
        $evt.addEventHandler(item, "undocheckoutfailed", this.getDelegate(this._onItemChanged));
        $evt.addEventHandler(item, "checkinfailed", this.getDelegate(this._onItemChanged));

        if (item.isLoaded()) {
            this._onItemLoaded();
        }
    }

};

Tahzoo.Extensions.StructureGroup.InheritFromParent.prototype.disposeInterface = Tridion.OO.nonInheritable(function Tahzoo$Extensions$InheritFromParent$disposeInterface() {
    var item = $display.getItem();
    if (item) {
        $evt.removeEventHandler(item, "load", this.getDelegate(this._onItemLoaded));
        $evt.removeEventHandler(item, "change", this.getDelegate(this._onItemChanged));
        $evt.removeEventHandler(item, "undocheckoutfailed", this.getDelegate(this._onItemChanged));
        $evt.removeEventHandler(item, "checkinfailed", this.getDelegate(this._onItemChanged));
    }
});

Tahzoo.Extensions.StructureGroup.InheritFromParent.prototype._onItemLoaded = function Tahzoo$Extensions$InheritFromParent$_onItemLoaded() {
    console.log("Item Loaded");
    var item = $display.getItem();

    this._updateCheckbox(item);
};

Tahzoo.Extensions.StructureGroup.InheritFromParent.prototype._onItemChanged = function Tahzoo$Extensions$InheritFromParent$_onItemChanged(event) {
    console.log("Item Changed");
    this._updateCheckbox($display.getItem());
};

Tahzoo.Extensions.StructureGroup.InheritFromParent.prototype._onInheritPageProcessClicked = function Tahzoo$Extensions$InheritFromParent$_onInheritPageProcessClicked(event) {
    var c = this.properties.controls;
    console.log("Clicked To: " + c.inheritPageProcess.checked.toString());
};

Tahzoo.Extensions.StructureGroup.InheritFromParent.prototype._updateCheckbox = function Tahzoo$Extensions$InheritFromParent$_updateCheckbox(item) {
    this.properties.controls.inheritPageProcess.disabled = item.isReadOnly() || item.isLoading();
};

Tridion.Controls.Deck.registerInitializeExtender("WorkflowTab", Tahzoo.Extensions.StructureGroup.InheritFromParent);

The Extension Configuration (InheritedWFSG.config)

And finally we have our extension’s actual configuration file which pieces everything together.

<?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="Tridion.Extensions.GUI.InheritedWFSG.StructureGroup">
                <cfg:fileset>
                    <cfg:file type="script">/InheritFromParent.ascx.js</cfg:file>
                </cfg:fileset>
            </cfg:group>
        </cfg:groups>
    </resources>
    <definitionfiles/>
    <extensions>
        <ext:editorextensions>
        <ext:editorextension target="CME">
        <ext:editurls />
        <ext:listdefinitions />
        <ext:itemicons />
        <ext:taskbars />
        <ext:commands />
        <ext:commandextensions />
        <ext:contextmenus />
        <ext:lists />
        <ext:tabpages />
        <ext:toolbars />
        <ext:ribbontoolbars />
        <ext:extendedareas>
            <ext:add>
                <ext:extension assignid="InheritPageProcessFromParent" name="InheritPageProcessFromParent">
                    <ext:control>~/InheritFromParent.ascx</ext:control>
                    <ext:pagetype></ext:pagetype>
                    <ext:renderinblock>false</ext:renderinblock>
                    <ext:dependencies>
                        <cfg:dependency>Tridion.Extensions.GUI.InheritedWFSG.StructureGroup</cfg:dependency>
                    </ext:dependencies>
                    <ext:apply>
                        <ext:view name="StructureGroupView">
                            <ext:control id="WorkflowStructureGroup_ExtendableArea" />
                        </ext:view>
                    </ext:apply>

                </ext:extension>
            </ext:add>
        </ext:extendedareas>
        </ext:editorextension>
        </ext:editorextensions>
        <ext:dataextenders />
    </extensions>
    <commands/>
    <contextmenus/>
    <localization/>
    <settings>
        <defaultpage />
        <navigatorurl />
        <editurls/>
        <listdefinitions/>
        <itemicons/>
        <theme>
            <path/>
        </theme>
        <customconfiguration/>
    </settings>
</Configuration>

And there you have it! If you need help understanding how to deploy the above, check out this tutorial here.