Sunday, August 29, 2010

XsltListViewWebPart – several XSLT tips

The XSL customizations of the XsltListViewWebPart in SharePoint 2010 are probably not a trivial thing. If you want to do something more complex with that the best starting point for reference materials is of course the SharePoint 2010 SDK - http://msdn.microsoft.com/en-us/library/ff604021.aspx. It provides extensive coverage on the topic, so I won’t repeat any of that in this posting. Here, I will concentrate on two things: the first one is a practical issue, which is probably the first one that you will encounter when customizing the XsltListViewWebPart’s XSL – how to hook your custom XSL to the XsltListViewWebPart. The thing is that you have not just one but four options for that – the XsltListViewWebPart itself exposes two properties for setting the custom XSL – XsltListViewWebPart.Xsl and XsltListViewWebPart.XslLink and also the SPView class has two properties with the very same names and obviously the same purpose. Well, that’s plenty and to quickly answer the two questions that may already have arisen – first – what has the SPView class to do with the XsltListViewWebPart – the answer is simple: the two classes are actually two representations of the same internal SharePoint entity (check a previous posting of mine on the subject - http://stefan-stanev-sharepoint-blog.blogspot.com/2010/02/listviewwebpart-spview-two-sides-of.html). And the second question – if the two classes are indeed representations of one and the same thing do these two sets of properties map to the same internal properties too – the answer here is no, the “Xsl” and “XslLink” properties of the XsltListViewWebPart are actually inherited from a base class (DataFormWebPart) that has no direct relation to list views and indeed the four properties are really independent from one another (and there are some differences in their usage despite the matching names as you will see below). One important thing here is that there is a specific precedence for their usage by the XsltListViewWebPart – the exact evaluation order is this:

  1. XsltListViewWebPart.XslLink
  2. XsltListViewWebPart.Xsl
  3. SPView.Xsl
  4. SPView.XslLink

Before starting with the exact specifics of using these properties I want to mention one other property of the XsltListViewWebPart class – CacheXslTimeOut. This is an integer property that specifies the cache time in seconds for the XslTransform object used by the XsltListViewWebPart. The caching of the XslTransform object is very useful because, of course, it boosts the performance. And the caching of the XslTransform also may be used or not used altogether depending on which of the “Xsl” or “XslLink” properties you use, which I think is important to know beforehand. One other thing here – when I tested the “caching” behavior of these properties I either changed the value of the properties in the case of the “Xsl” ones or changed the underlying XSL file in the case of the “XslLink” properties. The immediate reflection of the change to the rendering of the web part doesn’t necessarily mean that the cache is not used because it may simply mean that the change of the property invalidates the XslTransform cache. So in the short descriptions of the properties’ usage below I won’t mention that the XslTransform cache is not applied but simply that you see or don’t see immediately the change applied in the rendering of the XsltListViewWebPart. Of course for testing purposes you can always set the value of the CacheXslTimeOut property to 1 second so that you can quickly change the XSL and see the result immediately.

And now the details and specifics about the usage of the “XSL” properties:

  • XsltListViewWebPart.XslLink – you can change this with either the SharePoint UI (it appears in the XsltListViewWebPart’s toolpart) or programmatically with the SharePoint object model. Note here that unlike the XslLink property of the SPView class you can’t specify simply the name of a custom XSL file that resides in the system TEMPLATE\LAYOUTS\XSL folder (e.g. the standard main.xsl or a custom “custom.xsl”) – this won’t work (it may be a bit surprising). You have two options for specifying the path to your custom XSL file here – the first one is to specify a file under the TEMPLATE\LAYOUTS folder (it can be directly in that folder or any sub-folder below it, not just the “XSL” one) – and the path (rather URL in this case) should mandatorily start with “/_layouts/”. The other option is to reference an XSL file that resides on your site, for instance in a document library – then you can use either the site relative URL of the file or the server relative one. For example, if you have a “custom.xsl” in a library whose URL is “documents”, then the site relative URL will be “documents/custom.xsl” (no starting slash) and the server relative URL will be something like “/sites/mysite/documents/custom.xsl” (note the starting slash – the starting part of the URL depends on the server relative URL of your site). And about the caching behavior – interestingly enough it is different depending on whether you use an XSL file from the LAYOUTS folder or in a document library in the site – in the first case the changes to the referenced XSL file won’t be visible immediately, and in the second – they will be.
  • XsltListViewWebPart.Xsl – you can change this property with the object model, but the easier way to do this is with the SharePoint Designer. With it it should be easy to change the Xsl property even without deep understanding of XSL – you can use the enhanced UI of the SharePoint Designer to modify the styling and rendering of individual list columns or to apply conditional rendering on whole rows in the XsltListViewWebPart. The SharePoint Designer automatically populates the Xsl property with an XSL snippet containing one or several XSL templates depending on your exact customizations (this XSL also references the standard “main.xsl” and the templates in it are rather “overrides” of the standard row and column rendering XSL templates). The changes to the “Xsl” property are applied immediately regardless of the value of the CacheXslTimeOut property. If you think that this may pose a performance issue for you, you can always save the XSL contained in the “Xsl” property to an external XSL file which you can then reference with the XsltListViewWebPart.XslLink or SPView.XslLink properties.
  • SPView.Xsl – you can again change this property programmatically – for that you will need to get the hidden SPView instance associated with your XsltListViewWebPart (see the link to my posting on the subject above) or again the easier way to achieve that is to set the property in the view schema in the “schema.xml” file of your custom list template (custom “schema.xml” files can also be specified in ListInstance feature elements via the “CustomSchema” attribute). Note that below the “View” element in the “schema.xml” file you can have both “Xsl” and “XslLink” elements. One important note - you should provide the XSL in the “Xsl” element in the “schema.xml” and also in the SPView.Xsl property in an XML CDATA section. The changes to the SPView.Xsl property (those applied with code) are immediately visible in the XsltListViewWebPart regardless of the value of the CacheXslTimeOut property.
  • SPView.XslLink – you can change this property programmatically and also can set its value in the XslLink element in the view schema in the “schema.xml” file of your SharePoint list. Normally you provide only a file name in this property and the referenced XSL file with that name should exist in the system TEMPLATE\LAYOUTS\XSL folder. You can also reference files in sub-folders of the LAYOUTS\XSL folder and even files in the LAYOUTS folder itself – in the first case you set the “XslLink” property to “sub\custom.xsl” and in the second case - to “..\custom.xsl”. The XmlTransform caching is fully applied for the SPView.XslLink property depending on the value of the CacheXslTimeOut property. There is one extra thing here compared to the XsltListViewWebPart.XslLink property – if you set the CacheXslTimeOut property to 1 second you will need additionally to recycle the application pool (which will force the invalidation of the cache) and only after that you will see the changes to the underlying XSL file immediately in the rendering of the XsltListViewWebPart. This is also true if you have the CacheXslTimeOut  set to 1 second and you see the immediate changes but then set it to some higher value and then again reset it to 1 second – then you will no longer see the immediate changes until you recycle the application pool again. This means that the changing of the CacheXslTimeOut property itself doesn’t invalidate the caching of the XmlTransform in the case of the SPView.XslLink property.

So, this was the first topic that I wanted to talk about and about the second one I will demonstrate a short XSL snippet:

<?xml version="1.0" encoding="utf-8"?>

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"

    xmlns:asp="http://schemas.microsoft.com/ASPNET/20" xmlns:mycontrols="http://mycontrols" >

  <xsl:output method="html" indent="no"/>

  <xsl:decimal-format NaN=""/>

  <xsl:param name="XmlDefinition" select="."/>

 

  <xsl:template match="/">

    <xsl:value-of disable-output-escaping="yes" select="'&lt;%@ Register Tagprefix=&quot;asp&quot; Namespace=&quot;mycontrols.WebControls&quot; Assembly=&quot;mycontrols, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d03859869fe4a098&quot; %&gt;'"/>

    <mycontrols:MyControl runat="server" ListUrl="docs" />

 

    <table cellpadding="3">

      <tr>

        <xsl:for-each select="$XmlDefinition/ViewFields/FieldRef">

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

        </xsl:for-each>

      </tr>

      <xsl:for-each select="/dsQueryResponse/Rows/Row">

        <tr>

          <xsl:variable name="row" select="." />

          <xsl:for-each select="$XmlDefinition/ViewFields/FieldRef">

            <xsl:variable name="fieldName" select="@Name" />

            <td>

             <xsl:choose>

                <xsl:when test="$fieldName='LinkFilenameNoMenu'">

                  <xsl:value-of select="$row/@FileLeafRef"/>

                </xsl:when>

                <xsl:otherwise>

                  <xsl:value-of select="$row/@*[name() = $fieldName]" disable-output-escaping="yes"/>

                </xsl:otherwise>

              </xsl:choose>

            </td>

          </xsl:for-each>

        </tr>

      </xsl:for-each>

    </table>

  </xsl:template>

</xsl:stylesheet>

You can see in the snippet that it makes its own custom rendering of the underlying SharePoint list data (not very good at that) and doesn’t even include the standard SharePoint “vwstyles.xsl” and “fldtypes.xsl” (it’s only for demonstration purposes and should be as short as possible). The important thing in it is in the very beginning of the body of its single XSL template definition – you can see there a somewhat concealed asp.net page “Register” directive and immediately after it an asp.net markup declaration of a server control. Note also that the “mycontrols” namespace of the server control element is declared in the root element of the XSL file. So, basically this means that you can place server controls inside the XSL and these will be instantiated by the XsltListViewWebPart and their logic will be executed server-side. As you see in the snippet I placed only one server control before the actual rendering of the list data, but you can place controls in every row or even cell that you render and you can provide some list item data to the properties of the server control or controls (there may be of course performance considerations because of the number of controls that can be created in this manner).

And to answer the question – how does the XsltListViewWebPart instantiate the server controls that you may place in the XSL. It is actually simple – the XsltListViewWebPart uses the XSL transformation to produce HTML markup from the source XML, it then checks whether there are occurrences of the “runat=server” token inside the HTML markup: if there are no occurrences, the HTML markup is simply rendered, otherwise the web part instantiates a server control using the asp.net TemplateControl.ParseControl method providing the raw HTML markup to its single parameter. The TemplateControl.ParseControl method as it name suggests parses asp.net markup and creates a server controls tree from it much like it happens when a normal “aspx” or “ascx” file gets compiled. So, with this small “trick” the XsltListViewWebPart allows you to place server controls and implement more complex server side logic inside your custom XSL. And one limitation to this approach – you can’t use user controls (ascx files in the CONTROLTEMPLATES or LAYOUTS system folders) inside the XSL (server controls that use user controls internally with the TemplateControl.LoadControl method will also fail to instantiate their user controls).

Sunday, August 8, 2010

Site Template Configurator utility

This is the newest Windows forms tool that uses the famous TreeView-PropertyGrid combo (a long time favorite of mine). I called it the “Site template configurator” and as its name suggests it is designed to provide an easy to use UI for creating, updating and maintaining SharePoint site templates (with a focus on portal site templates). You can download the source code of the tool from here. Here is a small teaser screenshot:

stcfg1

Let me start first with a few words about site templates in SharePoint. We have the normal (ONET.XML) site template which configures the functionality for a single SharePoint site. And we also have the so called portal site templates which specify not the configuration of a single site but rather a site hierarchy – this includes which ONET site template should be used for the root site of the hierarchy and then for the sub-sites in the site hierarchy. In SharePoint 2010 there’re already two ways of provisioning ONET site templates – the old one available in SharePoint 2007 with folders under the TEMPLATE\SiteTemplates SharePoint system folder and a WEBTEMP*.XML file under the TEMPLATE\[LCID]\XML folder; and the new one – with “WebTemplate” features (which works for both farm and sandbox solutions as opposed to the “SiteTemplates” approach). The portal site templates are provisioned with “WebManifest” XML files as the one available in the TEMPLATE\SiteTemplates\WebManifest SharePoint folder for the standard “Collaboration Portal” portal site definition (plus an entry in a WEBTEMP*.XML file much like the “SiteTemlates” type ONET site definitions).

The obvious purpose of site templates is to automate the process of creating SharePoint sites with the same functionality (or site hierarchies with the same structure). Although the ONET.XML provides many different configuration options it can basically come down to specifying only which site scoped and web scoped features should be activated in the site – I think that ONET elements like the “Lists” and “Modules” ones should be deprecated in favor of the corresponding “ListInstance” and “Module” feature elements. So if we assume that SharePoint features are the smallest blocks of functionality then site templates can be viewed as simply sets of features that should deploy these various functionality items to a SharePoint site. This is a neat conceptual frame but when it comes to real life implementation it may turn really difficult to maintain site definitions especially if they grow rapidly in number. And here comes the tool I will present in this posting that will hopefully help address at least some of the issues of creating and maintaining site templates.

The tool is developed in Visual Studio 2010 and targets SharePoint 2010. It uses only a small set of classes from the SharePoint object model so it can be easily recompiled for SharePoint 2007, meaning that it can be used for the older version of SharePoint as well. To start with an important note – the tool doesn’t modify existing SharePoint site templates and doesn’t change any standard configuration files under the SharePoint 14\TEMPLATE folder. It allows you to create new site templates and select and configure site and web features for them and all configurations are saved in a so called project file, which is an XML file with a custom format. The project file can be opened in the tool at later times for further updating and maintaining of your site templates. From the project file the tool can generate SharePoint site template definitions that can be deployed to a SharePoint installation where they can be used. I think that keeping all configurations in a single file is definitely an advantage at least in terms of maintainability (unfortunately in SharePoint we should deal with much more configuration files).

And here is a quick overview of the tool’s commands starting with the tool’s toolbar:

  • “Actions” menu – this menu is context dependant – it displays commands that apply for the currently selected node in the TreeView control
  • “Project” menu:
    - “New Project …”
    - “Open Project”
    - “Save Project”
    - “Save Project as …”
    - “Close Project”
    These are all self explanatory – they handle the creating, opening, updating and closing of the project file.
    - “Generate artifacts …” – this command generates the SharePoint site definitions that are configured in the project file – I will explain this in more detail below.

The tool displays several entities in a hierarchical view in the left side pane, these are: “Project”, “Template”, “Web”, “Feature” and “Feature property”. Each of these contains a number of configuration properties that are displayed in the right side property grid pane (they can also be modified in the property grid) and also each entity comes with a number of “actions” that are available as menu items under the “Actions” toolbar menu or in the context menu which appears on right mouse click on the tree node.

  • “Project” entity – it contains some general settings for the project and your templates. It has the following properties:
    - “ID” – automatically generated GUID – internal unique identifier of the project, used when the SharePoint  artifacts get generated
    - “Name” – internal name of the project
    - “DisplayName” – display name of the project (for reference purposes only, not used internally)
    - “Description” – description of the project (for reference purposes only, not used internally)
    - “DefaultDisplayCategory” – this specifies the display name of the group under which your site templates will appear in the SharePoint “create site” page
    - “Locale” – set to English US by default (1033) – this specifies the target locale of your site templates
    - “StartTemplateID” – this one is important – this number will be used for the “ID” attributes of your site templates in the generated WEBTEMP*.XML file – note that these should be unique throughout all WEBTEMP*.XML files otherwise you will get an error when you create a site with your templates.
    The commands for the “Project” entity:
    - “Add template” – adds a new site template to the project
    - “Paste template” – after you use the “copy” command of an existing template you can paste it as a new template using the Windows clipboard.
  • “Template” entity – it contains the settings for a site definition with the following properties:
    - “BaseTemplateName” – you can specify an existing ONET or portal site definition here and also one of the other templates in the project. Note that currently you cannot use “WebTemplate” feature site definitions (you won’t see these in the “site templates” drop-down control in the “Add template” dialog form). Note also that you can use portal site templates as well – nesting these is not a problem. The tool will also generate portal site templates for your site definitions.
    - “BaseTemplateConfiguration” – this is used in conjunction with the “BaseTemplateName” property to specify the configuration ID of the base site definition within its ONET.XML.
    - “Name” – internal name of the template (as it is used to create a site with code)
    - “DisplayName” – display name of the template (as it appears on the standard “create site” page)
    - “Description” – description of the template (as it appears on the standard “create site” page)
    - “DisplayCategory” – this specifies the display group of the template as it appears on the standard “create site” page, if empty the “DefaultDisplayCategory” property of the “Project” entity will be used instead during artifact generation.
    - “Hidden” – specifies whether the template is hidden
    - “RootWebOnly” – analogous to the “RootWebOnly” attribute in the WEBTEMP*.XML
    - “SubWebOnly” – analogous to the “SubWebOnly” attribute in the WEBTEMP*.XML
    - “IncludeLists” – specifies whether the Configuration/Lists element from the ONET.XML of the base site definition (specified in the “BaseTemplateName” property) should be copied to the new definition
    - “IncludeModules” – specifies whether the Configuration/Modules element from the ONET.XML of the base site definition (specified in the “BaseTemplateName” property) should be copied to the new definition
    The “Template” entity has the following commands:
    - “Add web” – allows you to add a sub-web to the template’s site hierarchy
    - “Delete template” – deletes the template
    - “Copy template” – copies the definition of the template to the clipboard (see “Paste template” above)
    - “Copy template (shallow)” – the same as “Copy template” but copies the template’s definition without its sub-webs
    - “Paste web” – pastes a “Web” definition from the clipboard as a new sub-web
  • “Web” entity – you can have “Web” elements under your “Template” elements and also nest “Web” elements under other “Web” elements. This way you can specify a site hierarchy for your template (since it is a portal site template). The “Web” entity contains a subset of the properties of the “Template” entity. There is one difference here – the usage of the “Name” property – here it specifies the URL of the sub-web relative to the template’s root or its parent “Web”. The “Web” entity has the following commands:
    - “Add web” – adds a sub-web under the current web
    - “Delete web” – deletes the current web
    - “Copy web” - copies the definition of the web to the clipboard (see “Paste web” above)
    - “Copy web (shallow)” – the same as “Copy web” but copies the web’s definition without its sub-webs
    - “Paste subweb” – pastes a “Web” definition from the clipboard as a new sub-web
  • “Feature” entity – it represents a SharePoint feature that should be activated for the site based on the template or some of the “Web” elements available in the template’s portal hierarchy. The “Feature” elements appear under the “SiteFeatures” and “WebFeatures” sub-nodes of “Template” and “Web” elements. When you add a new “Template” or “Web” element all available site and web features from the “Configuration” element of the base template’s (“BaseTemplateName” property) ONET.XML are copied and will appear in your definition. Note that when the base template is a portal site definition (this includes the templates in the current project) the feature nodes won’t be available – portal site definitions are treated as “sealed” by the tool. The “Feature” entity has the following commands:
    - “Add feature property” – adds a new “Feature property” element for the current “Feature”
    - “Delete feature” – deletes the current “Feature”
    - “Copy feature” – copies the current “Feature” definition to the clipboard
    - “Paste feature property” – creates a new “Feature property” from a copied to the clipboard “Feature property” definition. If a “Feature property” with the same “Key” property exists it will be overwritten.
    The following “Feature” commands are available for the “SiteFeatures” and “WebFeatures” nodes:
    - “Add/remove features” – this command opens a dialog form with a multi-select control that allows you to quickly add and remove many features at a time and also allows you to change the ordering of the features.
    - “Paste feature” – pastes a “Feature” definition from the clipboard. If the feature already exists it will be overwritten (the feature properties if available will be overwritten actually).
  • “Feature property” entity – it represents a property of a SharePoint feature. It has the following properties:
    - “Key” – represents the “Key” attribute of the ONET.XML “Property” element
    - “Value” - represents the “Value” attribute of the ONET.XML “Property” element
    The “Feature property” entity has the following commands:
    - “Delete feature property” – deletes the selected “Feature property”
    - “Copy feature property” – copies the definition of the “Feature property” to the clipboard.


Generation of the SharePoint site definitions

And a few words about the generation of the real SharePoint definitions from the tool’s custom template definition XML format: initially I was considering several approaches to get the site templates functionality available (site templates in the most general sense as I mentioned above – getting a set of features applied to a site) including these scenarios:

  • doing this programmatically using the SharePoint object model (either with a custom feature receiver or using the new SharePoint 2010 “web provisioned” event). A problem with this was that the object model provides methods to activate a feature but you can’t specify the feature properties that should be applied with the activation (there is an internal method for that but I didn’t want to resort to reflection).
  • using the “WebTemplate” feature approach – it seems a little bit more economical – you can pack many site definitions into a single feature and you don’t need the WEBTEMP*.XML with an extra entry there so that the definition becomes available. A small but annoying problem with “WebTemplate” features is that you can’t specify for example that the site definition is hidden – the moment you install the “WebTemplate” feature in the farm all site definitions in it become globally available.

So in the end I decided to use the good old “SiteTemplates” type of site definitions coupled with “WebManifest” files for the portal site definitions. And here is what you will get when you click the “Generate artifacts …” command – first you will need to select a target folder (it’s better to use a temporary one rather than the standard 14\TEMPLATE folder), then the tool will output all necessary SharePoint artifact files in two main folders below the target folder: “[LCID]\XML” and “SiteTemplates”.

The first one will contain a single WEBTEMP*.XML file – the asterisk will be replaced by a Guid like sequence of characters (starting with “G” and followed by thirty two alphanumeric characters – this is actually the auto-generated “ID” property of the “Project” element). The WEBTEMP*.XML file will contain entries for all portal site templates generated from the “Template” elements in the project file and also hidden “Template” entries for all base site templates (actually copies of these – see below) that were used in the template project.

The “SiteTemplates” folder – this will contain one folder named G[32 characters project GUID]_WebManifest and several folders named G[32 characters project GUID]_[base template name]. The former will contain “WebManifest” XML files for all templates in the project and the latter ones – there will be one folder per base template used – a folder will be created only for non-portal base templates – this will be an exact copy of the original folder of the base template containing all files and sub-folders, the only difference will be the ONET.XML file – it’s “Configurations” element will be completely changed and will contain one “Configuration” element for every “Template” and “Web” element in the project that use this base template. The individual “Configuration” elements will contain the selected site and web features for the parent element in your project file plus optionally the “Lists” and “Modules” element from the base configuration.

The tool just outputs the SharePoint artifact files without creating a WSP file (one of the directions for improving the tool should be the built-in generation of CAB files (the WSP is a CAB) although this is a little bit tedious job in Windows). The simple copying of the files to the “14\TEMPLATE” folder is actually sufficient (followed by a recycling of the application pool/pools – including the central administration site application pool if you want to create sites from central administration) to get the generated site definitions available (though it is a bit cumbersome since it should be made on every machine in the farm).