Archive for the ‘XSL’ Category

MS SharePoint as CMS

Friday, June 20th, 2008

In the same vein as using a Google Spreadsheet as a CMS, you can use Microsoft SharePoint similarly.

At work, we us Microsoft SharePoint as our intranet website. Although I think that it has complicated processes, in some ways, I am open to making the best of it. When we started maintaining calendars and lists in SharePoint that we then duplicated on our website, I saw an opening to not only reduce this redundancy, but also to automate website content. Additionally, because I, or someone else, must make any changes to the website, moving the management of some content to within SharePoint will allow others to update this information.

Posting the staff directory in SharePoint, complete with job titles and phone extensions, provided the perfect opportunity to demonstrate to the powers-that-be how we could take advantage of SharePoint beyond it being a company forum and bulletin board.

Basically, I used the RSS feed from SharePoint. There are three parts: the PHP to grab the RSS feed, one XSL file to convert the RSS feed to predictable XML, and another XSL file to render the rss2xml data however it is desired.

Here’s the PHP. We must access our SharePoint through HTTP authentication, therefore CURL was used to access the RSS feed. After one has the RSS feed, the RSS is converted to more predictable XML, and then converted again for HTML output.

<?php
        // SharePoint RSS feed ID
        $feedid = “{01835674-500B-4B3D-BB6C-3F1B1E301E71}”;

        $xml_file = “http://username:password@sharepoint/_layouts/listfeed.aspx?List=$feedid”;

        // generally the first column of a list is the <title> element
        // this variable customizes the XML element name when is is converted
        $title_field = “firstname”;
               
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $xml_file);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        curl_setopt($ch, CURLOPT_USERAGENT,“Mozilla/4.0″);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
        $results = curl_exec($ch);
        $info = curl_getinfo($ch);
        curl_close ($ch);

        //print_r($results);
        //exit;

        // MS SharePoint uses a CDATA section for most of the list content.
        // Let’s get rid of Cdata sections, so that the entire file is treated as XML
        $results = str_replace(“<![CDATA[" , "" , $results);
        $results = str_replace("]]>” , “” , $results);
        $results = str_replace(“<b>” , “” , $results);
        $results = str_replace(“</b>” , “” , $results);
                       
        // Load the XML source
        $xml = new DOMDocument;
        $xml->loadXML($results);

        // This file converts the RSS feed to usable XML, see below
        $xsl_file = “sharepointrss2xml.xsl”;

        $xsl = new DOMDocument;
        $xsl->load($xsl_file);

        // Configure the transformer
        $proc = new XSLTProcessor;
        $proc->importStyleSheet($xsl); // attach the xsl rules

        // Set XSLT parameters
        $proc->setParameter(, ‘TITLEELEMENT’, $title_field);
        $usablexml = $proc->transformToXML($xml);

        $xml = new DOMDocument;
        $xml->loadXML($usablexml);

        // This file converts the usable XML, to HTML output for display
        $xsl_file = “staff2html.xsl”;

        $xsl = new DOMDocument;
        $xsl->load($xsl_file);

        // Configure the transformer
        $proc = new XSLTProcessor;
        $proc->importStyleSheet($xsl); // attach the xsl rules
                       
        echo $proc->transformToXML($xml);

?>

Convert MS SharePoint RSS to usable XML. In theory, this file can be used on most, if not all, MS SharePoint RSS feeds. For the most part, the column heading of each column in the SharePoint list is captured and used as the name of the XML element.

<?xml version=“1.0″?>
<xsl:stylesheet
        xmlns:xsl=“http://www.w3.org/1999/XSL/Transform”
        version=“1.0″>

        <xsl:output method=“xml” indent=“yes”></xsl:output>
        <xsl:strip-space elements=“*”></xsl:strip-space>

        <xsl:template match=“/”>
                <sharepointfeed>
                        <xsl:apply-templates select=“*/channel/item” />
                </sharepointfeed>
        </xsl:template>

        <xsl:template match=“*/channel/item”>
                <item>
                        <xsl:text disable-output-escaping=“yes”>&lt;</xsl:text><xsl:value-of select=“$TITLEELEMENT” /><xsl:text disable-output-escaping=“yes”>&gt;</xsl:text>
                                <xsl:value-of select=“title” />
                        <xsl:text disable-output-escaping=“yes”>&lt;/</xsl:text><xsl:value-of select=“$TITLEELEMENT” /><xsl:text disable-output-escaping=“yes”>&gt;</xsl:text>

                        <xsl:for-each select=“description/div”>
                                <xsl:text disable-output-escaping=“yes”>&lt;</xsl:text>
                                <xsl:value-of select=“substring-before( translate( . , ‘ ‘ , ‘_’) , ‘:’)” />
                                <xsl:text disable-output-escaping=“yes”>&gt;</xsl:text>
                                <xsl:value-of select=“normalize-space( substring-after( . , ‘:’) )” />
                                <xsl:text disable-output-escaping=“yes”>&lt;/</xsl:text>
                                <xsl:value-of select=“substring-before( translate( . , ‘ ‘ , ‘_’) , ‘:’)” />
                                <xsl:text disable-output-escaping=“yes”>&gt;</xsl:text>
                        </xsl:for-each>
                </item> 
        </xsl:template>

</xsl:stylesheet>

Finally, convert the XML above to HTML output for display.

<?xml version=“1.0″?>
<xsl:stylesheet
        xmlns:xsl=“http://www.w3.org/1999/XSL/Transform”
        version=“1.0″>

        <xsl:output method=“html” indent=“yes”></xsl:output>
        <xsl:strip-space elements=“*”></xsl:strip-space>

        <xsl:template match=“/”>
                <xsl:call-template name=“dodepartments” />
                <xsl:call-template name=“donames” />
        </xsl:template>

        <xsl:template name=“dodepartments”>
                <table width=“95%”>
                        <xsl:for-each select=“sharepointfeed/item”>
                                <xsl:sort select=“Last_Name”/>
                                <xsl:if test=“normalize-space(Last_Name)=””>
                                        <tr>
                                                <td valign=“top”><b><xsl:value-of select=“firstname” /></b></td>
                                                <td valign=“top”>312.555.<xsl:value-of select=“Extension” /></td>
                                        </tr>
                                </xsl:if>
                        </xsl:for-each>   
                </table>
                <br /><br />
        </xsl:template>

        <xsl:template name=“donames”>
                <table width=“95%”>
                        <xsl:for-each select=“sharepointfeed/item”>
                                <xsl:sort select=“Last_Name”/>
                                <xsl:if test=“Last_Name!=””>
                                        <tr>
                                                <td valign=“top”>
                                                        <p><b><xsl:value-of select=“Last_Name” /><xsl:text>, </xsl:text><xsl:value-of select=“firstname” /></b><br />
                                                        <xsl:value-of select=“Title” /><br />
                                                        <a href=“#”>Profile Page</a></p>
                                                </td>
                                                <td valign=“top”>
                                                        <p align=“right”>
                                                        312.555.<xsl:value-of select=“Extension” /><br />
                                                        <a href=“mailto:{Email}@domain.com”><xsl:value-of select=“Email” />@domain.com</a>
                                                        </p>
                                                </td>
                                        </tr>
                                </xsl:if>
                        </xsl:for-each>
                </table>
        </xsl:template>

</xsl:stylesheet>

Here are the files altogether:
MS SharePoint as CMS

Google Spreadsheets as CMS

Sunday, June 8th, 2008

I had a client who, after a few years, desired to update his website himself. When I originally designed and developed his website, I knew that he would want to alter the information on his website occasionally, but we agreed that I would make those changes for a number of reasons, not least of which was his level of comfort with a computer. Therefore, when I developed the website, I placed all the data in XML, figuring this would be very easy for me to update and would not require actual website editing. I also have an inherent dislike for locking content to its display, i.e. embedding content into the web page itself (I’ve participated in too many website redevelopment projects). Additionally, should he, or someone else, want to learn how to update the website, I hoped that editing a simple XML file would be a smaller learning curve than having to open a PHP page and alter HTML code, and have my client risk damaging the PHP coding in the process.

In time, I relocated, removing any opportunity for a face-to-face tutorial session. Additionally, I realized that my client was less experienced with a computer than I originally thought. Something easier was in order.

I considered building some pages that would permit my client to update the information in a simple web form. Since I had left private web development work behind, I did not want to devote my precious personal time to what could very quickly become a complicated endeavor. After a week of contemplation, I remembered that one can “publish” a Google spreadsheet. Could I use that? After all, nearly everyone, even the most conservative computer user, has used a spreadsheet before.

Google provides a number of publication options. Atom, an XML format, was one of them. After a little work to import the data into a Google spreadsheet and a little work to transform the data in the Atom stream using XSL so that the data displays nicely, the work was done. No need to build a custom mini-CMS solution. Here are the code ingredients.

An example Atom stream from a Google spreadsheet:

http://spreadsheets.google.com/feeds/list/o13846618212923825784. 2660624158407743517/od6/public/basic

Create an XSL file (I named it dinner.xsl):

<?xml version=“1.0″?>
<xsl:stylesheet
        xmlns:xsl=“http://www.w3.org/1999/XSL/Transform”
        xmlns:openSearch=‘http://a9.com/-/spec/opensearchrss/1.0/’
        xmlns:gsx=‘http://schemas.google.com/spreadsheets/2006/extended’
        xmlns:atom=‘http://www.w3.org/2005/Atom’
        xmlns:str=“http://exslt.org/strings”
        version=“1.0″ >

        <xsl:output method=“html” indent=“yes”></xsl:output>

        <xsl:strip-space elements=“*”></xsl:strip-space>

        <xsl:template match=“/”>
                                <xsl:call-template name=“getHors” />
                                <br /><br />
                                <xsl:call-template name=“getPlats” />
                                <br /><br />
                                <xsl:call-template name=“getDesserts” />
        </xsl:template>

        <xsl:template name=“getHors”>
                <p class=‘large’><b>Hors d’oeuvre</b></p>
                <xsl:for-each select=“atom:feed/atom:entry”>
                        <xsl:for-each select=“atom:title”>
                                <xsl:if test=“normalize-space(.)=’hors’”>
                                        <p>
                                                <xsl:call-template name=“renderItem” />
                                        </p>
                                </xsl:if>
                        </xsl:for-each>
                </xsl:for-each>
        </xsl:template>
       
        <xsl:template name=“getPlats”>
                <p class=‘large’><b>Les Plats</b></p>
                <xsl:for-each select=“atom:feed/atom:entry”>
                        <xsl:for-each select=“atom:title”>
                                <xsl:if test=“normalize-space(.)=’plats’”>
                                        <p>
                                                <xsl:call-template name=“renderItem” />
                                        </p>
                                </xsl:if>
                        </xsl:for-each>
                </xsl:for-each>
        </xsl:template>
       
        <xsl:template name=“getDesserts”>
                <p class=‘large’><b>Desserts</b></p>
                <xsl:for-each select=“atom:feed/atom:entry”>
                        <xsl:for-each select=“atom:title”>
                                <xsl:if test=“normalize-space(.)=’dessert’”>
                                        <p>
                                                <xsl:call-template name=“renderItem” />
                                        </p>
                                </xsl:if>
                        </xsl:for-each>
                </xsl:for-each>
        </xsl:template>
       
        <xsl:template name=“renderItem”>
                <xsl:for-each select=“str:tokenize(../atom:content,’:')”>
                        <xsl:choose>
                                <xsl:when test=“position()=2″>
                                        <xsl:choose>
                                                <xsl:when test=“contains(. , ‘, prix’)”>               
                                                        <b><xsl:value-of select=“substring-before( . , ‘, prix’)”/></b><br />
                                                </xsl:when>
                                                <xsl:otherwise>
                                                        <b><xsl:value-of select=“substring-before( . , ‘, description’)”/></b><br />
                                                </xsl:otherwise>
                                        </xsl:choose>
                                </xsl:when>
                                <xsl:when test=“position()=3″>
                                        <xsl:choose>
                                                <xsl:when test=“contains(. , ‘, description’)”>  
                                                        <xsl:value-of select=“following-sibling::*[1]“/> <xsl:value-of select=“substring-before( . , ‘, description’)”/>-
                                                </xsl:when>
                                                <xsl:otherwise>
                                                        <xsl:value-of select=“.”/>
                                                </xsl:otherwise>
                                        </xsl:choose>
                                </xsl:when>
                        </xsl:choose>
                </xsl:for-each>
        </xsl:template>

</xsl:stylesheet>

And then a little PHP:

<?php

        $atomfeed = “http://spreadsheets.google.com/feeds/list/o13846618212923825784.2660624158407743517/od6/public/basic”;
        $xml_data = file_get_contents($atomfeed);
       
        $xml = new DOMDocument(“1.0″);
        $xml->loadXML($xml_data);
          
        $xsl_file = “dinner.xsl”;
        $xsl = new DOMDocument(“1.0″);
        $xsl->load($xsl_file);
       
        // Configure the transformer
        $proc = new XSLTProcessor;
        $proc->importStyleSheet($xsl);
       
        echo $proc->transformToXML($xml);
               
?>

Here are the files altogether:

Google Spreadsheets CMS Example

Entries (RSS)