4

I'm working with Web 8.5 and I've two GUI Extensions which currently works when used alone. When I try to use them both at the same time, the first one defined in the <models> in system.config the file doesn't work properly.

The Extensions have both editor and model parts. They're adding one column each and a text in the column.

The problem is that when I open a component or page ("Open shared an item in read-only mode") and I go back to the CMS the value of the column disappears (only for the GUI Extension defined first, the second one still has the value). If I open the component selection "Edit the parent item", it doesn't happen.

So I think that somehow the second one is overwritten the files of the first one, just don't understand why or how.

One thing I noticed is that the one where the value disappears (first one defined in system.config section) doesn't call the extended getListItemXmlAttributes of the js file when defined as the second model in system.config, it is called.

ExtendedComponentWithSchemaName.js

Type.registerNamespace("Com.Extensions.UI.SchemaNameColumn");

Com.Extensions.UI.SchemaNameColumn.ExtendedComponentWithSchemaName = function (id) {
    Tridion.OO.enableInterface(this, "Com.Extensions.UI.SchemaNameColumn.ExtendedComponentWithSchemaName");
    // test if initiative (from OME) exists
    var isInitiative = false;
    if (Tridion.OO.implementsInterface(this, "Tridion.OnlineMarketing.UI.InitiativeComponent")) {
        this.addInterface("Tridion.OnlineMarketing.UI.InitiativeComponent", [id]);
        isInitiative = true;
    } else {
        this.addInterface("Tridion.ContentManager.Component", [id]);
    }
    var p = this.properties;
    p.isInitiative = isInitiative;
    p.schemaNameColumnExtendedInfo = undefined;
};

Com.Extensions.UI.SchemaNameColumn.ExtendedComponentWithSchemaName.prototype.setDataFromList = function (node, parentId, timeStamp) {
    var p = this.properties;

    if (!timeStamp || timeStamp > this.getTimeStamp()) {
        var tmp;

        if (tmp = $xml.getInnerText(node, "@SchemaNameColumnExtendedInfo")) {
            p.schemaNameColumnExtendedInfo = tmp;
        }
    }

    if (p.isInitiative) {
        this.callBase("Tridion.ContentManager.InitiativeComponent", "setDataFromList", [node, parentId, timeStamp]);
    } else {
        this.callBase("Tridion.ContentManager.Component", "setDataFromList", [node, parentId, timeStamp]);
    }
};

/* get item icon */
Com.Extensions.UI.SchemaNameColumn.ExtendedComponentWithSchemaName.prototype.getItemIcon = function () {
    var icon = this.callBase(this.defaultBase, "getItemIcon");
    return icon;
};

Com.Extensions.UI.SchemaNameColumn.ExtendedComponentWithSchemaName.prototype.getSchemaNameColumnExtendedInfo = function () {
    var p = this.properties;
    var xmlDoc;
    if (p.schemaNameColumnExtendedInfo === undefined && (xmlDoc = this.getXmlDocument())) {
        p.schemaNameColumnExtendedInfo = $xml.selectSingleNode(xmlDoc, "/tcm:Page[@SchemaNameColumnExtendedInfo='true']") ? true : false;
    }
    return p.schemaNameColumnExtendedInfo;
};

Com.Extensions.UI.SchemaNameColumn.ExtendedComponentWithSchemaName.prototype.getListItemXmlAttributes = function (customAttributes) {
    var attribs = {};
    var p = this.properties;

    if (customAttributes) {
        for (var attr in customAttributes) {
            attribs[attr] = customAttributes[attr];
        }
    }
    //This adds my custom column back when the item is updated

    attribs["SchemaNameColumnExtendedInfo"] = p.schemaNameColumnExtendedInfo;
    if (p.isInitiative) {
        return this.callBase("Tridion.ContentManager.InitiativeComponent", "getListItemXmlAttributes", [attribs]);
    } else {
        return this.callBase("Tridion.ContentManager.Component", "getListItemXmlAttributes", [attribs]);
    }
};

ExtendedPageWithSchemaName.js

Type.registerNamespace("Com.Extensions.UI.SchemaNameColumn");

Com.Extensions.UI.SchemaNameColumn.ExtendedPageWithSchemaName = function (id) {
    Tridion.OO.enableInterface(this, "Com.Extensions.UI.SchemaNameColumn.ExtendedPageWithSchemaName");
    // test if initiative (from OME) exists
    var isInitiative = false;
    if (Tridion.OO.implementsInterface(this, "Tridion.OnlineMarketing.UI.InitiativePage")) {
        this.addInterface("Tridion.OnlineMarketing.UI.InitiativePage", [id]);
        isInitiative = true;
    } else {
        this.addInterface("Tridion.ContentManager.Page", [id]);
    }
    var p = this.properties;
    p.isInitiative = isInitiative;
    p.schemaNameColumnExtendedInfo = undefined;

};

Com.Extensions.UI.SchemaNameColumn.ExtendedPageWithSchemaName.prototype.setDataFromList = function (node, parentId, timeStamp) {
    var p = this.properties;
    if (!timeStamp || timeStamp > this.getTimeStamp()) {
        var tmp;
        if (tmp = $xml.getInnerText(node, "@SchemaNameColumnExtendedInfo")) {
            p.schemaNameColumnExtendedInfo = tmp;
        }
    }
    if (p.isInitiative) {
        this.callBase("Tridion.ContentManager.InitiativePage", "setDataFromList", [node, parentId, timeStamp]);
    } else {
        this.callBase("Tridion.ContentManager.Page", "setDataFromList", [node, parentId, timeStamp]);
    }
};

/* get item icon */
Com.Extensions.UI.SchemaNameColumn.ExtendedPageWithSchemaName.prototype.getItemIcon = function () {
    var icon = this.callBase(this.defaultBase, "getItemIcon");
    return icon;
};

Com.Extensions.UI.SchemaNameColumn.ExtendedPageWithSchemaName.prototype.getSchemaNameColumnExtendedInfo = function () {
    var p = this.properties;
    var xmlDoc;
    if (p.schemaNameColumnExtendedInfo === undefined && (xmlDoc = this.getXmlDocument())) {
        p.schemaNameColumnExtendedInfo = $xml.selectSingleNode(xmlDoc, "/tcm:Page[@SchemaNameColumnExtendedInfo='true']") ? true : false;
    }
    return p.schemaNameColumnExtendedInfo;
};
Com.Extensions.UI.SchemaNameColumn.ExtendedPageWithSchemaName.prototype.getListItemXmlAttributes = function (customAttributes) {
    var attribs = {};
    var p = this.properties;

    if (customAttributes) {
        for (var attr in customAttributes) {
            attribs[attr] = customAttributes[attr];
        }
    }
    //This adds my custom column back when the item is updated

    p.schemaNameColumnExtendedInfo = this.getPageTemplateTitle();
    attribs["SchemaNameColumnExtendedInfo"] = "PT: " + p.schemaNameColumnExtendedInfo;

    if (p.isInitiative) {
        return this.callBase("Tridion.ContentManager.InitiativePage", "getListItemXmlAttributes", [attribs]);
    } else {
        return this.callBase("Tridion.ContentManager.Page", "getListItemXmlAttributes", [attribs]);
    }
};

/* get page template for the page */
Com.Extensions.UI.SchemaNameColumn.ExtendedPageWithSchemaName.prototype.getPageTemplateTitle = function () {
    var p = this.properties;
    if (this.getXmlDocument()) {
        p.pageTemplateTitle = $xml.getInnerText(p.xmlDoc, "/tcm:Page/tcm:Data/tcm:PageTemplate/@xlink:title") || null;
    }
    return p.pageTemplateTitle;
};

If I inspect (using console.log) this.properties in their constructor I see the value of the second one and undefined for the first one.

Model.Config

<?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">
  <resources>
    <cfg:filters /> 
    <cfg:groups>
      <cfg:group name="Com.Extensions.SchemaNameColumn">
        <cfg:fileset>
          <cfg:file type="script">/Scripts/ExtendedComponentWithSchemaName.js</cfg:file>
          <cfg:file type="script">/Scripts/ExtendedPageWithSchemaName.js</cfg:file>
        </cfg:fileset>
      </cfg:group>
    </cfg:groups>
  </resources>
  <definitionfiles />

  <extensions>
    <ext:editorextensions>
      <ext:editorextension target="CME">
        <ext:editurls/>
        <ext:listdefinitions/>
        <ext:taskbars/>
        <ext:commands/>
        <ext:commandextensions/>
        <ext:contextmenus/>
        <ext:lists/>
        <ext:tabpages/>
        <ext:toolbars/>
        <ext:ribbontoolbars/>
      </ext:editorextension>
    </ext:editorextensions>
    <ext:dataextenders>
      <ext:dataextender type="GuiExtensions.SchemaNameColumn.SchemaNameExtender, GuiExtensions.SchemaNameColumn" name="SchemaNameExtender">
        <ext:description>Schema Name Column Extender</ext:description>
      </ext:dataextender>
    </ext:dataextenders>
    <ext:modelextensions>
      <cfg:itemtypes>
        <cfg:itemtype id="tcm:16" implementation="Com.Extensions.UI.SchemaNameColumn.ExtendedComponentWithSchemaName" />
        <cfg:itemtype id="tcm:64" implementation="Com.Extensions.UI.SchemaNameColumn.ExtendedPageWithSchemaName" />
      </cfg:itemtypes>
    </ext:modelextensions>
  </extensions>

  <commands/>
  <contextmenus />
  <localization />
  <settings>
    <customconfiguration />
  </settings>
</Configuration>

Editor.config

<?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:groups>
  </resources>
  <definitionfiles />
  <extensions>
    <ext:editorextensions>
      <ext:editorextension target="CME">
        <ext:editurls/>
        <ext:listdefinitions/>
        <ext:taskbars/>
        <ext:commands/>
        <ext:commandextensions/>
        <ext:contextmenus/>
        <ext:lists>
          <ext:add>
            <ext:extension name="SchemaNameExtender" assignid="SchemaNameExtender" insertbefore="NamedType">
              <ext:listDefinition>
                <ext:selectornamespaces/>
                <ext:columns>
                  <column xmlns="http://www.sdltridion.com/2009/GUI/extensions/List"
                              id="SchemaName" type="data" title="Schema Name"
                              selector="@SchemaNameColumnExtendedInfo" translate="String" />
                </ext:columns>
              </ext:listDefinition>
              <ext:apply>
                <ext:view name="DashboardView" />
                <!--ext:view name="PageView" /-->
                <ext:view name="PopupsView" />
                <ext:view name="InsertComponentPresentationPopup" />
                <ext:view name="ItemSelectDialogPopup" />
              </ext:apply>
            </ext:extension>
          </ext:add>
        </ext:lists>
        <ext:tabpages/>
        <ext:toolbars/>
        <ext:ribbontoolbars/>
      </ext:editorextension>
    </ext:editorextensions>
  </extensions>
  <commands/>
  <contextmenus />
  <localization />
  <settings>
    <defaultpage/>
    <!-- /Views/Default.aspx</defaultpage> -->
    <navigatorurl/>
    <!-- /Views/Default.aspx</navigatorurl> -->
    <editurls/>
    <listdefinitions />
    <itemicons/>
    <theme>
      <path>theme/</path>
    </theme>
    <customconfiguration />
  </settings>
</Configuration>

system.config

<model name="GuiExtensions.SchemaNameExtender.Model">
      <installpath>E:\SDLWeb\web\WebUI\Models\GuiExtensions.SchemaNameExtender.Model\</installpath>
      <configuration>Configuration\GuiExtensions.SchemaNameColumn.Model.config</configuration>
      <vdir>GuiExtensions.SchemaNameExtender.Model</vdir>
    </model>
     <model name="GuiExtensions.PublishStatusExtender.Model">
      <installpath>E:\SDLWeb\web\WebUI\Models\GuiExtensions.PublishStatusExtender.Model\</installpath>
      <configuration>Configuration\GuiExtensions.PublishStatusColumn.Model.config</configuration>
      <vdir>GuiExtensions.PublishStatusExtender.Model</vdir>
    </model>

SchemaNameExtender.cs

namespace GuiExtensions.SchemaNameColumn
{
    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Linq;
    using System.Reflection;
    using System.Xml;
using log4net;

using Tridion.ContentManager;
using Tridion.Web.UI.Core.Extensibility;

public class SchemaNameExtender : DataExtender
{
    private const string NsDs = "http://www.tridion.com/ContentManager/5.0";

    private static readonly ILog Logger = LogManager.GetLogger(typeof(SchemaNameExtender));

    private static List<string> commands;

    private static string SchemaNameNotFoundText = "N/A";

    public SchemaNameExtender()
    {
        var dllConfigPath = Common.Helper.GetRunningFolder(Assembly.GetExecutingAssembly().CodeBase);
        var commandsList = Common.Helper.GetAppSetting(dllConfigPath, "commandsList");
        SchemaNameNotFoundText = Common.Helper.GetAppSetting(dllConfigPath, "SchemaNameNotFoundText");

        try
        {
            Logger.Debug("commandsList: " + commandsList);
            commands = commandsList.Split(',').ToList();
        }
        catch (Exception ex)
        {
            Logger.Error("SchemaNameExtender.SchemaNameExtender - Error trying to get the commandList - " + ex.Message);
        }
    }

    public override string Name
    {
        get
        {
            var thisType = this.GetType();
            return string.Concat(thisType.Namespace, ".", thisType.Name);
        }
    }

    public XmlTextReader AddSchemaTitle(XmlTextReader xReader, PipelineContext context)
    {
        var strWriter = new StringWriter();
        var xWriter = new XmlTextWriter(strWriter);
        const string AttrName = "SchemaNameColumnExtendedInfo";
        var schemaNames = new Dictionary<string, string>();

        xReader.MoveToContent();

        Logger.Error("Schema Name Column Extender");


        while (!xReader.EOF)
        {
            switch (xReader.NodeType)
            {
                case XmlNodeType.Element:

                    xWriter.WriteStartElement(xReader.Prefix, xReader.LocalName, xReader.NamespaceURI);
                    xWriter.WriteAttributes(xReader, false);

                    if (xReader.LocalName == "Item" && xReader.NamespaceURI == NsDs)
                    {
                        // 1. Check if it's Page or Component
                        var schemaId = xReader.GetAttribute("SchemaId");
                        var addInfo = "N/A";
                        try
                        {
                            addInfo = SchemaNameNotFoundText;
                        }
                        catch (Exception ex)
                        {
                            Logger.Error("SchemaNameExtender.AddSchemaTitle - Eerror - " + ex.Message);
                        }

                        if (schemaId == null || !schemaNames.TryGetValue(schemaId, out addInfo))
                        {
                            var tcmUri = xReader.GetAttribute("ID");
                            if (TcmUri.IsValid(tcmUri))
                            {
                                var uri = new TcmUri(tcmUri);
                                addInfo = GetAdditionalInfo(uri);
                                if (schemaId != null)
                                {
                                    schemaNames.Add(schemaId, addInfo);
                                }
                            }
                        }

                        if (addInfo != null)
                        {
                            xWriter.WriteAttributeString(AttrName, addInfo);
                        }
                    }

                    if (xReader.IsEmptyElement)
                    {
                        xWriter.WriteEndElement();
                    }

                    break;
                case XmlNodeType.EndElement:
                    xWriter.WriteEndElement();
                    break;
                case XmlNodeType.CDATA:

                    // Copy CDATA node  <![CDATA[]]>
                    xWriter.WriteCData(xReader.Value);
                    break;
                case XmlNodeType.Comment:

                    // Copy comment node <!-- -->
                    xWriter.WriteComment(xReader.Value);
                    break;
                case XmlNodeType.DocumentType:

                    // Copy XML documenttype
                    xWriter.WriteDocType(xReader.Name, null, null, null);
                    break;
                case XmlNodeType.EntityReference:
                    xWriter.WriteEntityRef(xReader.Name);
                    break;
                case XmlNodeType.ProcessingInstruction:
                    xWriter.WriteProcessingInstruction(xReader.Name, xReader.Value);
                    break;
                case XmlNodeType.SignificantWhitespace:
                    xWriter.WriteWhitespace(xReader.Value);
                    break;
                case XmlNodeType.Text:
                    xWriter.WriteString(xReader.Value);
                    break;
                case XmlNodeType.Whitespace:
                    xWriter.WriteWhitespace(xReader.Value);
                    break;
                default:
                    Logger.Error($"SchemaNameExtender.AddSchemaTitle - default - xReader.NodeType: {xReader.NodeType}");
                    break;
            }

            xReader.Read();
        }

        xWriter.Flush();
        xReader = new XmlTextReader(new StringReader(strWriter.ToString()));
        xReader.MoveToContent();
        return xReader;
    }

    public override XmlTextReader ProcessRequest(XmlTextReader reader, PipelineContext context)
    {
        return reader;
    }

    public override XmlTextReader ProcessResponse(XmlTextReader reader, PipelineContext context)
    {
        var xReader = reader;

        try
        {
            var command = context.Parameters["command"] as string;
            if (command == null)
            {
                return null;
            }

            command = command.ToLower();

            if (commands.Contains(command))
            {
                xReader = this.AddSchemaTitle(reader, context);
            }

            return xReader;
        }
        catch (Exception ex)
        {
            Logger.Error(
                $"SchemaNameExtender.ProcessResponse - Exception while reading parameters - Message: {ex.Message} - StackTrace: {ex.StackTrace}");
            return null;
        }
    }

    private static string GetAdditionalInfo(TcmUri uri)
    {
        var additionalInfo = "N/A";
        try
        {
            additionalInfo = SchemaNameNotFoundText;
        }
        catch (Exception ex)
        {
            Logger.Error("SchemaNameExtender.GetAdditionalInfo - Error - " + ex.Message);
        }

        if (uri.ItemType != ItemType.Component)
        {
            return additionalInfo;
        }

        // Only component will shown its Schema Name. If Pages should show it too, then we must use the PageAggregator.
        var cid = new ComponentAggregator(uri.ToString());
        return cid.GetSchemaName(); --> calls other internal api
    }
}

}

The other GUI Extension is almost the same but I'm changing SchemaNameColumn with other name say MyGuiExtensionColumn

I've the Virtual Folder in IIS defined and configured in system.config. That part I think is right or I wouldn't be seeing it working when I use only one or only for the second one defined.

Any ideas?

Thanks in advance. Guillermo.

UPDATE

Found something interesting... setDataFromList is only called for the second gui extension declared in system.config... it's not being called for the first one... Someone knows who should be calling it?

UPDATE 2

I've seen

upgradedToType: "Com.Extensions.UI.PublishStatusColumn.ExtendedComponent"

Does that mean it will only use that extension and not the other as it was "upgraded" to it?

enter image description here

1

I see you've added the new data extender to expose the attributes required to the component properties on Load.

My guess would be that where you've extended getListItemXmlAttributes from Tridion.ContentManager.Component to use these attributes it's only called once - which is defined by the order or the node.

Could you

  1. check if the property exists/doesn't throw an exception
  2. concatenate the string (or add a collection of 'available' columns
    • p.schemaNameColumnExtendedInfo = this.getPageTemplateTitle() + this.getSchemaName()

UPDATE

In ExtendedPageWithSchemaName.js I see you reset p.schemaNameColumnExtendedInfo to this.getPageTemplateTitle()

BUT in ExtendedComponentWithSchemaName.js you haven't added the value back for the update... p.schemaNameColumnExtendedInfo = this.aSpecificFunctionToReturnTheSchemaName()

  • Hi, Thx for answering. The method Com.Extensions.UI.SchemaNameColumn.ExtendedComponentWithSchemaName.prototype.getListItemXmlAttributes is not event called for the extension that is defined first. I don't see any errors in the console. Regarding adding the value back, the value is set from .NET I don't have a way to get it from javascript so far. But since the method is not called...I think it doesn't matter. – polonskyg May 15 '18 at 12:01
  • But the point is that in one extension you explicitly re set schemaNameColumnExtendedInfo for the attribs - but in the other extension you don't. So, for example, if you remove p.schemaNameColumnExtendedInfo = this.getPageTemplateTitle(); from the first file - doesn't the refresh break for that too? – Dylan .. Mark Saunders May 15 '18 at 14:06
  • One thing to note is that I'm not using it right now for Pages, only for components...so what happens for pages (right now) is not relevant – polonskyg May 15 '18 at 14:52
  • 1
    I tried to update the component js with this attribs["SchemaNameColumnExtendedInfo"] = 'value'; and I don't see anything (it's empty), so that doesn't seem to help... – polonskyg May 15 '18 at 15:11
  • Found something interesting... setDataFromList is only called for the second gui extension declared in system.config... it's not being called for the first one... Do you know who should be calling it? – polonskyg May 15 '18 at 15:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.