In un sabato decisamente umido, per non dire completamente fradicio, vista l’acqua che sta venendo, ho trovato un po’ di tempo per approfondire la conoscenza con le Content Query Web Part (CQWP) e più precisamente con i sui stili XSL. Volevo prendere dimestichezza con gli stili di default per poterne realizzare di miei in modo da personalizzare i risultati della webart nei mie progetti. Il compito è stato facilitato dalla passione che ho nei confronti del linguaggio XSLT, che ho appreso qualche anno fa lavorando per il portale de ‘il Sole 24 Ore’, il cui rendering era completamente basato sulle trasformazioni dei contenuti in XML con XSLT.

Per cominciare mi sono letto un articolo di Microsoft, presente anche nello SDK di MOSS, “How to: Customize XSL for the Content Query Web Part“, che mi ha presentato i tre fogli di stile ContentQueryMain, ItemStyle.xsl e Header.xsl. Descrivendo i template di ItemStyle l’articolo elencava le funzioni usate per facilitare l’estrazione dei dati dal risultato. A questo punto della lettura cresceva sempre di più dentro di me la voglia di capire quale XML produce una CQWP e come funzionano queste funzioni, anche perchè conoscerle può aiutare a semplificare il lavoro e ottenere risultati solidi e in linea con la filosofia del prodotto.

Ecco un elenco delle funzioni OOTB contenute nell’XSL ContentQueryMain:

  1. OuterTemplate.GetSafeLink
  2. OuterTemplate.GetSafeStaticUrl
  3. OuterTemplate.GetColumnDataForUnescapedOutput
  4. OuterTemplate.GetTitle
  5. OuterTemplate.FormatColumnIntoUrl
  6. OuterTemplate.FormatValueIntoUrl
  7. OuterTemplate.Replace
  8. OuterTemplate.GetPageNameFromUrl
  9. OuterTemplate.GetPageNameFromUrlRecursive
  10. OuterTemplate.GetGroupName
  11. OuterTemplate.CallPresenceStatusIconTemplate

Queste “funzioni” che in realtà sono dei template xsl aiutano ad estrarre le informazioni in modo corretto dall’elemento xml passato. Ritorna a questo punto la prima delle questioni: recuperare il risultato della query, insomma vedere l’xml prodotto dalla CQWP. Girovangando su internet ho trovato un po’ di spunti. Heather Solomon spiega come vedere le proprietà degli item estratti (ovvero gli attributi dell’elemento row):

“aggiungere nel file ContentQueryMain un proprio template del tipo:”

<xsl:template name=”MyCustomStyle” match=”Row[@Style='MyCustomStyle']” mode=”itemstyle”>
<xsl:for-each select=”@*”>
P:<xsl:value-of select=”name()” />
</xsl:for-each>

</xsl:template>

Ma questo non mi bastava, volevo avere sottomano un file XML. A questo punto ho deciso di creare un mio foglio di stile partendo dal ContentQueryMain originale e usare la tecnica dell’identity, così ho prodotto questo codice (IdentityContentQueryMain.xsl):

<xsl:stylesheet version=”1.0″ exclude-result-prefixes=”x xsl cmswrt cbq” xmlns:x=”http://www.w3.org/2001/XMLSchema” xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xmlns:cmswrt=”http://schemas.microsoft.com/WebPart/v3/Publishing/runtime” xmlns:cbq=”urn:schemas-microsoft-com:ContentByQueryWebPart”>
<xsl:output method=”xml” version=”1.0″ encoding=”UTF-8″ indent=”yes”/>
<xsl:param name=”cbq_isgrouping”/>
<xsl:param name=”cbq_columnwidth”/>
<xsl:param name=”Group”/>
<xsl:param name=”GroupType”/>
<xsl:param name=”cbq_iseditmode”/>
<xsl:param name=”cbq_viewemptytext”/>
<xsl:param name=”SiteId”/>
<xsl:param name=”WebUrl”/>
<xsl:param name=”PageId”/>
<xsl:param name=”WebPartId”/>
<xsl:param name=”FeedPageUrl”/>
<xsl:param name=”FeedEnabled”/>
<xsl:param name=”SiteUrl”/>
<xsl:param name=”BlankTitle”/>
<xsl:param name=”BlankGroup”/>
<xsl:param name=”UseCopyUtil”/>
<xsl:param name=”DataColumnTypes”/>
<xsl:param name=”ClientId”/>
<xsl:template match=”/”>
<xml-cqwp>
<params>
<xsl:element name=”cbq_isgrouping”>
<xsl:value-of select=”$cbq_isgrouping”/>
</xsl:element>
<xsl:element name=”cbq_columnwidth”>
<xsl:value-of select=”$cbq_columnwidth”/>
</xsl:element>
<xsl:element name=”Group”>
<xsl:value-of select=”$Group”/>
</xsl:element>
<xsl:element name=”GroupType”>
<xsl:value-of select=”$GroupType”/>
</xsl:element>
<xsl:element name=”cbq_iseditmode”>
<xsl:value-of select=”$cbq_iseditmode”/>
</xsl:element>
<xsl:element name=”cbq_viewemptytext”>
<xsl:value-of select=”$cbq_viewemptytext”/>
</xsl:element>
<xsl:element name=”SiteId”>
<xsl:value-of select=”$SiteId”/>
</xsl:element>
<xsl:element name=”WebUrl”>
<xsl:value-of select=”$WebUrl”/>
</xsl:element>
<xsl:element name=”PageId”>
<xsl:value-of select=”$PageId”/>
</xsl:element>
<xsl:element name=”WebPartId”>
<xsl:value-of select=”$WebPartId”/>
</xsl:element>
<xsl:element name=”FeedPageUrl”>
<xsl:value-of select=”$FeedPageUrl”/>
</xsl:element>
<xsl:element name=”FeedEnabled”>
<xsl:value-of select=”$FeedEnabled”/>
</xsl:element>
<xsl:element name=”SiteUrl”>
<xsl:value-of select=”$SiteUrl”/>
</xsl:element>
<xsl:element name=”BlankTitle”>
<xsl:value-of select=”$BlankTitle”/>
</xsl:element>
<xsl:element name=”BlankGroup”>
<xsl:value-of select=”$BlankGroup”/>
</xsl:element>
<xsl:element name=”UseCopyUtil”>
<xsl:value-of select=”$UseCopyUtil”/>
</xsl:element>
<xsl:element name=”DataColumnTypes”>
<xsl:value-of select=”$DataColumnTypes”/>
</xsl:element>
<xsl:element name=”ClientId”>
<xsl:value-of select=”$ClientId”/>
</xsl:element>
</params>
<result>
<xsl:apply-templates/>
</result>
</xml-cqwp>
</xsl:template>
<xsl:template match=”*”>
<xsl:element name=”{name(.)}”>
<xsl:for-each select=”@*”>
<xsl:attribute name=”{name(.)}”><xsl:value-of select=”.”/></xsl:attribute>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!–
<xsl:template match=”@* | node()”>
<xsl:copy>
<xsl:apply-templates select=”@* | node()”/>
</xsl:copy>
</xsl:template>
–>
</xsl:stylesheet>

C’è un problema però, non basta questo file per estrarre l’XML, bisogna adottare un altro trucchetto, ho creato un secondo stylesheet xsl che ho chiamato Empty.xsl e che è fatto in questo modo:

<xsl:stylesheet version=”1.0″ xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xmlns:fo=”http://www.w3.org/1999/XSL/Format”>
<xsl:output method=”xml” version=”1.0″ encoding=”UTF-8″ indent=”yes”/>
</xsl:stylesheet>

Assegnando il primo file alla proprietà MainXslLink e il secondo alle proprietà ItemXslLink e HeaderXslLink si ottiene un xml di questo tipo:

<?xml version=”1.0″ encoding=”utf-8″?>
<xml-cqwp>
<params>
<cbq_isgrouping>false</cbq_isgrouping>
<cbq_columnwidth>100</cbq_columnwidth>
<Group/>
<GroupType/>
<cbq_iseditmode>true</cbq_iseditmode>
<cbq_viewemptytext>La query non ha restituito elementi. Per configurare la query per questa web part, &lt;a href=”#” onclick=”javascript:MSOTlPn_ShowToolPane2(’Edit’,'g_a3f67598_8486_4855_bbd1_47a052236458′);”&gt;aprire il riquadro Strumenti&lt;/a&gt;.</cbq_viewemptytext>
<SiteId>21eba696-36a0-4ad5-95e8-842c0fe0d0c4</SiteId>
<WebUrl>%2Fit%2Fatmnews%2Fatminforma</WebUrl>
<PageId>5c33b0e5-d576-4d0f-a6d5-afbac1bfdefb</PageId>
<WebPartId>a3f67598-8486-4855-bbd1-47a052236458</WebPartId>
<FeedPageUrl>/_layouts/feed.aspx?</FeedPageUrl>
<FeedEnabled>false</FeedEnabled>
<SiteUrl>http://admin.atm.it</SiteUrl>
<BlankTitle>(Vuoto)</BlankTitle>
<BlankGroup>(Vuoto)</BlankGroup>
<UseCopyUtil>false</UseCopyUtil>
<DataColumnTypes>;Modified,DateTime;Title,Text;Author,User;Editor,User;_Level,Number;Created,DateTime;{1d22ea11-1e32-424e-89ab-9fedbadb6ce1},Counter;FileRef,Lookup;Description,Note;PublishingRollupImage,Image;Comments,Note;</DataColumnTypes>
<ClientId>ctl00_SPWebPartManager1_g_a3f67598_8486_4855_bbd1_47a052236458</ClientId>
</params>
<result>
<dsQueryResponse>
<Rows>
<Row ListId=”{A898ACF6-7C42-441F-99A0-05D322E419EF}” WebId=”{AC38FAD2-8709-42EF-9FCF-7B4A822993A8}” ID=”1″ Title=”" FileRef=”en/giromilano/Pages/default.aspx” _x007B_1d22ea11_x002D_1e32_x002D_424e_x002D_89ab_x002D_9fedbadb6ce1_x007D_=”1″ Modified=”2009-02-06 23:38:26″ Author=”Account di sistema” Editor=”Account di sistema” Created=”2009-02-06 15:28:42″ PublishingRollupImage=”" _Level=”2″ Comments=”" LinkUrl=”http://admin.atm.it/en/giromilano/Pages/default.aspx” PubDate=”Fri, 06 Feb 2009 23:38:26 GMT” ImageUrl=”" ImageUrlAltText=”" Description=”" Style=”Default” GroupStyle=”DefaultHeader” __begincolumn=”True” __begingroup=”False”/>
<Row ListId=”{F7E2272E-75BF-435B-A467-59AEC5A0755C}” WebId=”{22DCC1AB-EE62-46E9-B020-E3D2C50DD325}” ID=”2″ Title=”La missione” FileRef=”en/ilgruppo/chisiamo/Pages/lamissione.aspx” _x007B_1d22ea11_x002D_1e32_x002D_424e_x002D_89ab_x002D_9fedbadb6ce1_x007D_=”2″ Modified=”2009-02-06 17:37:05″ Author=”Account di sistema” Editor=”Account di sistema” Created=”2009-02-06 16:06:02″ PublishingRollupImage=”" _Level=”2″ Comments=”" LinkUrl=”http://admin.atm.it/en/ilgruppo/chisiamo/Pages/lamissione.aspx” PubDate=”Fri, 06 Feb 2009 17:37:05 GMT” ImageUrl=”" ImageUrlAltText=”" Description=”" Style=”Default” GroupStyle=”DefaultHeader” __begincolumn=”False” __begingroup=”False”/>
</Rows>
</dsQueryResponse>
</result>
</xml-cqwp>

Con in mano l’xml prodotto posso finalmente lavora of-line per debuggare i miei stili persoinalizzati. Il prossimo passo sarà creare degli XSL personalizzati a partire dagli originali che siano usabili fuori dal contesto SharePoint, ovvero enibendo le funzioni custom di SharePoint del namespace “cmswrt“, in questo modo avrò piena liberta di esplorare la struttura e le logiche degli XSL. Senza però dar troppo peso al risultato.