Select and Link to Media Items with XSLT Using the Sitecore Web Content Management System

For the current edition of this blog post, see:

http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog/Posts/2010/11/Select-and-Link-to-Media-Items-with-XSLT-Using-the-Sitecore-Web-Content-Management-System.aspx

A few recent posts on the Sitecore Web Content Management forums on the Sitecore Developer Network triggered me to write about an XSL solution to a common challenge. Often, CMS users need to select some number of media items to associate with a content item. The developer wants to generate links to each of those media items, and those links should indicate the media type, for instance by showing an icon. This post describes one way to approach this issue.

In the data template for the content type, create a field of type Treelist to allow the user to select related media items. I named my field MediaLinks for this example. Set the Source property of the new Treelist field to the appropriate path in the media library. A source of /sitecore/media library will let the CMS user select any item in the media library, but this allows the user to add folders, which you would have to account for in your code. You can enter other values for the Source of the field to limit what the user can select as described in the Data Definition Reference. For example, to allow the user to select JPG, GIF, PNG, and possibly other types of images from within the Images folder in the media library, set the Source of the treelist field to DataSource=/sitecore/media library/images&IncludeTemplatesForSelection=Jpeg,Image. Because I am too lazy to add the code to iterate folder contents (especially considering nested folders), I used DataSource=/sitecore/media library&ExcludeTemplatesForSelection=media folder.

Add a block of code based on the following to the XSL rendering, and then simplify the logic and adjust the markup to meet your requirements.

<xsl:if test="sc:fld(‘medialinks’,.)">
  <ul>
    <xsl:for-each select="sc:Split(‘medialinks’,.)">
      <xsl:for-each select="sc:item(text(),.)">
        <xsl:variable name="alt">
          <xsl:choose>
            <xsl:when test="sc:fld(‘alt’,.)">
              <xsl:value-of select="sc:fld(‘alt’,.)" />
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="@name" />
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>
        <li>
          <img src="{concat(‘/’, sc:fld(‘__icon’,.))}" border="0" alt="{$alt}" />
          <a href="{concat(‘/’,sc:GetMediaUrl(.))}">
            <xsl:value-of select="$alt" />
          </a>
          (<xsl:value-of select="sc:ToLower(sc:fld(‘extension’,.))" />)
        </li>
      </xsl:for-each>
    </xsl:for-each>
  </ul>
</xsl:if>

Explanations follow.

<xsl:if test="sc:fld(‘medialinks’,.)">

This condition evaluates to false if the MediaLinks field does not exist in the context item or if the context item does not contain a value for that field. This prevents output of an empty <ul> if someone uses this rendering with a data template that does not contain this field or if the CMS user did not select any media items to associate with the content item.

  <ul>
    <xsl:for-each select="sc:Split(‘medialinks’,.)">

Fields that let the CMS user select multiple items store a pipe-separated list of GUIDs with no trailing pipe, such as {87C60CFE-3553-4E80-B781-6F9E071017BB}|{911359A8-B7D4-4B07-A0FD-7104216EC531}. The sc:Split() XSL extension function abstracts this detail so that you don’t have to use a recursive XSL template to parse the field value (surprisingly, XSL is notoriously bad for parsing strings). Inside the sc:Split() loop, the value of text() is the GUID of an individual item from the field value.

      <xsl:for-each select="sc:item(text(),.)">

The sc:item() XSL extension function retrieves the item associated with an individual GUID (the value from text()). I use for-each for my convenience even though there should only ever be one item corresponding to any given GUID. The for-each prevents processing of broken references – if the referenced media item does not exist for some reason, processing continues to the next GUID. Additionally, for-each sets the context item to whatever it’s iterating, so I don’t need to create a variable. Within this “loop”, . represents the media item rather than the content item containing the field referencing the media items.

        <xsl:variable name="alt">
          <xsl:choose>
            <xsl:when test="sc:fld(‘alt’,.)">
              <xsl:value-of select="sc:fld(‘alt’,.)" />
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="@name" />
            </xsl:otherwise>
          </xsl:choose>
        </xsl:variable>

This creates a variable that contains the value of the Alt field in the media item if that field exists and has a value. Otherwise, the variable contains the name of the media item itself.

        <li>
          <img src="{concat(‘/’, sc:fld(‘__icon’,.))}" border="0" alt="{$alt}" />

The curly brace indicates an Attribute Value Template, which is an XSL construct that actually has nothing to do with Sitecore, but affects Sitecore developers. Basically, if you need to invoke an XSL construct from within an attribute value in an element that XSL does not recognize (such as <img>), you need to wrap that construct with curly braces. This generates an <img> element using the value of the system field that contains the icon of the media item. Prefixing the path with a leading slash prevents a potential issue with IIS limits to the length of URLs.

          <a href="{concat(‘/’,sc:GetMediaUrl(.))}">
            <xsl:value-of select="$alt" />
          </a>

This creates a link to the media item, again avoiding the potential issue with paths too long for IIS.

          (<xsl:value-of select="sc:ToLower(sc:fld(‘extension’,.))" />)

I included this to show how you can get the original file extension of the media item (such as png), converting to lowercase from whatever character case the uploaded file originally had.

        </li>
      </xsl:for-each>
    </xsl:for-each>
  </ul>
</xsl:if>

Another approach would be to use <xsl:choose> to make some decision based on the type of the media item. I didn’t test this, but something like:

<xsl:choose>
  <xsl:when test="sc:ToLower(sc:fld(‘extension’,.)) = ‘pdf’">
    <!–do something for PDFs–>
  </xsl:when>
  <xsl:when test="sc:ToLower(sc:fld(‘extension’,.)) = ‘jpg’ or sc:ToLower(sc:fld(‘extension’,.)) = ‘jpeg’ or …">
    <!–do something else for images–>
  </xsl:when>
  <xsl:otherwise>
    <!–do something else for every other type of media item–>
  </xsl:otherwise>
</xsl:choose>

This looks like another valid case for an XSL variable to me. You could also use the template name (@template) instead of the extension.

I’ll leave identification of possible bugs and conversion to .NET as an exercise for the reader.

More information about XSL with Sitecore in the Presentation Component XSL Reference.

Update: One of the people that asked this question was using a field of type File to select the media item rather than using a field that lets the user select multiple items. In that case…

<xsl:variable name="mediaid" select="sc:fld(‘FileField’,.,’mediaid’ )" />

The File field type stores the GUID of the media item in the mediaid attribute (the value of the field is an XML element with attributes for the path and ID). This gets the GUID out of the field named FileField in the context item.

<xsl:for-each select="sc:item($mediaid,.)">

This iterates over the items with that GUID, setting the context element to the media item (if the field exists, the user selected a media item, and that media item exists, I think). I think you can avoid creating the variable by using something like <xsl:for-each select="sc:item(sc:fld(‘FileField’,.,’mediaid’),.)"> instead of creating the variable and using this for-each.

  <a href="{concat(‘/’, sc:GetMediaUrl(.))}">

    <xsl:value-of select="@name" />

  </a>

This creates a link to the media item using the name of the media item as the text of the link.

  (<xsl:value-of select="sc:fld(‘extension’,.)" />) (<img src="{concat(‘/’,sc:fld(‘__icon’, .))}" />)

This shows the extension and icon.

</xsl:for-each>

This entry was posted in Obsolete. Bookmark the permalink.

1 Response to Select and Link to Media Items with XSLT Using the Sitecore Web Content Management System

  1. Pingback: Select and Link to Media Items with XSLT Using the Sitecore Web … « xslt

Leave a comment