More fun with Dreamweaver Templates – TemplateRepeatCount

Quite a while back (in ’09’ I think) I replied to a thread on the sdltridionworld forum regaring how to get the total count of your elements when doing a TemplateBeginRepeat inside a DWT.

For example when you have a special css class that needs to be applied to the last item in the list.

<ul>
<li><a href="/contact/">Contact Us</a></li>
<li><a href="/sitemap/">Site Map</a></li>
<li class="last"><a href="/terms-of-use/"></a></li>
</ul>

An intuitive approach is to have a function or a variable that provides the count so that you can use syntax like this:

<ul>
<!– TemplateBeginRepeat name="Component.LinkList" –>
<li><a href="@@MyUrlField@@">@@MyUrlText@@</a></li>

<!– TemplateBeginIf cond="TemplateRepeatIndex == TemplateRepeatCount()-1 –>
<li class="last"><a href="@@MyUrlField@@">@@MyUrlText@@</a></li>

<!– TemplateEndIf –>
<!– TemplateEndRepeat –>
</ul>

Here is the code for it:

[TemplateCallable]
public string TemplateRepeatCount(string componentTcmId, string fieldName)
{
    Logger.Info("Start of Count");

    IdentifiableObject item = _engine.GetObject(componentTcmId);
    int count = 0;
    if (item.GetType().Name == "Component")
    {
        Component c = item as Component;
        //find the field collection and get the count
        ItemFields fields = new ItemFields(c.Content, c.Schema);

        Logger.Debug("Got component itemfields");
        if (fields.Contains(fieldName))
        {
            Logger.Debug("Found item field: " + fieldName);

            ItemField field = fields[fieldName];
            Logger.Debug("Field type: " + field.GetType().Name);

            switch (field.GetType().Name)
            {
                case "ComponentLinkField":
                {
                    Logger.Debug("Field is ComponentLinkField");
                    ComponentLinkField typedField = field as ComponentLinkField;
                    count = typedField.Values!=null?typedField.Values.Count:0;
                }
                break;

                case "EmbeddedSchemaField":
                {
                    Logger.Debug("Field is EmbeddedSchemaField");
                    EmbeddedSchemaField typedField = field as EmbeddedSchemaField;
                    count = typedField.Values != null ? typedField.Values.Count : 0;
                }
                break;

                case "SingleLineTextField":
                case "ExternalLinkField":
                case "XhtmlField":
                case "MultiLineTextField":
                {
                    Logger.Debug("Field is TextField");
                    TextField typedField = field as TextField;
                    count = typedField.Values != null ? typedField.Values.Count : 0;
                }
                break;

                case "DateField":
                {
                    Logger.Debug("Field is DateField");
                    DateField typedField = field as DateField;
                    count = typedField.Values != null ? typedField.Values.Count : 0;
                }
                break;

                case "NumberField":
                {
                    Logger.Debug("Field is NumberField");
                    NumberField typedField = field as NumberField;
                    count = typedField.Values != null ? typedField.Values.Count : 0;
                }
                break;

                case "KeywordField":
                {
                    Logger.Debug("Field is KeywordField");
                    KeywordField typedField = field as KeywordField;
                    count = typedField.Values != null ? typedField.Values.Count : 0;
                }
                break;
            }
        }
    }

    return count.ToString();
}

Though, here is another approach which I used one late night in the office wanting to finish a template.  It was past my sys admin’s office time, so I couldn’t ask him to deploy a new Custom Functions DLL to the CM server’s GAC/bin, but I just had to get the template done by end of day – I was in the zone.  Besides, I couldn’t let it linger the next day.  

So here is a quick C# fragment that should be used in sequence after Will Price’s “Get Linked Components” extension (just Google it if you don’t know.  It’s the best thing since the combustion engine/sliced bread/eh…SDL Tridion CMS).

Anyway, just drop this C# Fragment in after “Get Linked Components”:

<%@ Import Namespace="Tridion.ContentManager.ContentManagement.Fields"%>
<%@ Import Namespace="System.Collections.Generic"%>

/// This TBB loops through all the items in the package and for any items which are Component Arrays
/// adds a new package item with the array entry count.  This is useful when a Dreamweaver TBB needs to know
/// the total count of components or when needing to know if you're on the last component.

List<KeyValuePair<string, Item>> extractedComponentLists = new List<KeyValuePair<string, Item>>();
foreach (KeyValuePair<string, Item> kvp in package.GetEntries())
{
    if (kvp.Value.ContentType == ContentType.ComponentArray)
    {
        //get count
        extractedComponentLists.Add(kvp);
    }
}

// This code has to be kept in a separate loop since you can't modify the package.GetEntries collection while looping through it.
foreach (KeyValuePair<string, Item> kvp in extractedComponentLists)
{
    IComponentPresentationList linkCollectionCPList = ComponentPresentationList.FromXml(kvp.Value.GetAsString());
    int componentCount = linkCollectionCPList.Count;
    package.PushItem(kvp.Key + "Count", package.CreateStringItem(ContentType.Number, componentCount.ToString()));
}

After this TBB runs, you’ll get a new entry on the package with the count for each “Tridion/Component[]” item type that “Get Linked Components” fetches.  So if we had a multi-value component link field in the component called “LinkList”, then “Get Linked Components” should fetch a component array and call it after your field name – something like “LinkList”.  Then after this DWT a new entry will appear called “LinkListCount”.

Use it like this in your DWT:

<ul>
<!-- TemplateBeginRepeat name="Component.LinkList" -->
<li><a href="@@MyUrlField@@">@@MyUrlText@@</a>
<li><!-- TemplateBeginIf cond="TemplateRepeatIndex == LinkListCount-1 --> 
<li><a href="@@MyUrlField@@">@@MyUrlText@@</a>
<li><!-- TemplateEndIf -->
<!-- TemplateEndRepeat -->
</ul>

Get and Set Variables in DWTs

Here is a creative and useful set of Dreamweaver Custom Functions that allow instantiating your own variables in a Dreamweaver Tridion template.  Credit goes out to my fellow team members Trevor Bartlett and Riyaz Hameed.

You can do stuff like this:

<!-- TemplateBeginRepeat name="Field.columnSection" --> <div class="wpsPortlet"> <div class="wpsPortletTitle"> <br />@@Field.title@@</div> @@SetVariable(“columnSectionIndex”, "${TemplateRepeatIndex}")@@ <!-- TemplateBeginRepeat name="Field.subColumnSection" --> @@GetVariable("columnSectionIndex")@@ <!-- TemplateBeginRepeat name="Field.subTitle" --> … <!-- TemplateEndRepeat --> </div> <!-- TemplateEndRepeat -->

Here is the code:

/// <summary>
/// Sets a varialbe in the package to the name and value specifed. Also removes any other variable that was set with the same name before.
/// </summary>
/// <param name="variableName">Name of the varialbet</param>
/// <param name="value">Value of the variable</param>
[TemplateCallable()]
public string SetVariable(string variableName, object value)
{
    //Remove the old variable and set the new variable
    _engine.PublishingContext.RenderContext.ContextVariables.Remove(variableName);
    _engine.PublishingContext.RenderContext.ContextVariables.Add(variableName, value);
 
    return "";
}
 
/// <summary>
/// Gets a varialbe from the publishing context.
/// </summary>
/// <param name="variableName">Name of the variable</param>
[TemplateCallable()]
public object GetVariable(string variableName)
{
    //Get the varialbe
    return _engine.PublishingContext.RenderContext.ContextVariables[variableName];
}

Getting used items using the core service

Just wanted to post up a quick code snippet showing how to get a list of used items XML using the SDL Tridion 2011 core service.  Why?  Well previously the API typically used to work like itemType.GetListUsedItems() (where itemType is a Component or Page object etc), where as now the ‘Using’ and ‘Used’ items are read using a filter via the core service client method .GetListXML().

Continue reading

Remove HTML whitespace from the template output

HTML published from Tridion is typically created using Tridion Dreamweaver Templates (DWTs).  These templates can involve a lot of ‘If’ statements to check that values exist or what their value is etc.  I like to put line breaks between each DWT statement so that its easier to read and work with, which usually looks something like this (actually it can get a lot crazier than this!):


Continue reading

Displaying item XML in the GUI with interface extensions

Yoav Niran has written and blogged about another great Tridion GUI extension.  This time  providing a really elegant solution for developers/users that need to see the full XML content of an given item without having to (in my case) faff about with ID’s, Paths or templates.

Continue reading

Some notes about Tridion item naming

I’ve just recently written a script which imported a load of content from flat files into Tridion.  Using the flat files, the script creates the various folders, pages and components etc that it needed.  Unfortunately I cannot share the code as the client contract permits me to do (plus it was a throw-away tool written specifically around the format of the import data) but I can share a couple of titbits that I learnt about item naming and WebDAV lengths.

Continue reading

Special characters within Tridion webdav URL’s

Obtaining an item from the Tridion database can either be done via Tridion TCM ID, or using the item webdav path. The latter is always the safest bet in the event the item has been deleted and recreated or the code is being moved over different environments but if the item name contains special characters you’ll need a resource which lists what they are .

Continue reading