# Creating Truly Skinnable Web Sites (ver. 1.0.0)
by: SpiderMan of Black Sun Research Facility

A few months back, while working on bread5 (my new, at that time, ultra-secret web site), I had a thought: wouldn't it be cool if I could serve up bread5 with any look I wanted, without having to create entire sites with different layouts by hand. I didn't want to just change the color scheme like most “skinnable” sites; I wanted to create completely different looks without going through too much trouble. The skin engine that I created for bread5 uses a host of new technologies to accomplish its tasks; these technologies include php, XML, XSLT, and XHTML. I've never seen anyone create dynamic sites that are truly skinnable, so this is all new and uncharted territory for me. As a result, the method I describe in this article may not be the best way to do this, but I think it works very well. This is an advanced tutorial. The reader should be familar with PHP and XML.

The idea is simple: when one wants to create different versions of one's site, all the data is the same, the only thing that has changed is the layouts. So joe-webmaster makes up some layouts then goes at the task of copying-and-pasting his site's data into these layouts. He does this over and over for every page on his site. There must be a better way—there is. Since the site's data (such as product information) is the same for every layout, put all the data in an XML file and transform the data into the completely different layouts using XSLT. So, if I wanted to create a new skin for bread5, all I would have to do is create a few XSL files for the skin, upload it to the server, and it's done; the user can choose whichever skin he or she wants to view the site in. Using this method, you can increase the accessibility of your site just by creating screen reader and WAP compatible skins. In a few minutes your site can now be easily accessed by a women using her cell phone, or a blind man sitting at home.

To get started, take your data and put it in an XML file. For example, a site's index page might look like this:

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

    <mySite page="index" title="Welcome to my site!">

      <linkbar>

        <item type="link">

          <uri>./faq.php</uri>

          <title>FAQ</title>

          <desc>Frequently Asked Questions about this site</desc>

        </item>

        <item type="form">

          <action>./search.php</action>

          <method>post</method>

          <input type="text" name="search" maxlength="100"/>

        </item>

        <!-- other links -->

      </linkbar>

      <greeting>

        <para>

          Welcome to my site!

        </para>

        <para>

          Please check out all the sections.

        </para>

      </greeting>

      <news>

        <news-story>

          <date>November 29, 2001</date>

          <story>Some stuff happened.</story>

        </news-story>

        <news-story>

          <date>November 30, 2001</date>

          <story>Some more stuff happened.</story>

        </news-story>

      </news>

    </mySite>

Looks pretty simple, but it can be improved upon to make it even easier for us to maintain. Instead of adding a news story by manually editing the file, I'd like to pull the stories out of my database. That way, I can create a simple script to put my news in the database and then I can easily add news stories without having to FTP in to the server and edit the file.

We'll be using php's XSLT extension, Sablotron, to transform the XML file (discussed later on), so why not use its power to add dynamic content to the XML file also? It's easy to do. Just have php hold the XML data, pass it to Sablotron and have it pass the data to the XSL file. For example, to pull news stories out of a MySQL database and have it added to the XML file, we would do the following:

    // code to connect to the database and pull the stories omitted for brevity.

    // $db_query holds an SQL query to pull news stories out of the database.

    $xsltArgs["stories"]="<news-story>";

    while($row= mysql_fetch_array($db_query)

    {

      $date= $row["date"];

      $story= $row["story"];

      $xsltArgs["stories"].="

          <date>$date</date>

          <story>$story</story>";

    }

    $xsltArgs["stories"].="</news-story>";

$xsltArgs is an array. $xsltArgs["stories"] holds the XML for the news stories. This variable will be used later when the transformation takes place. If you want to add more dynamic data to your site, store it as XML in another slot of the array.

Now that we've created the XML file for the index page, we can create an XSL file that will be used by Sablotron to transform the XML file. Here is an XSL file that will transform the XML file into a simple XHTML web page.

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

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

      <xsl:output indent="yes" encoding="utf-8" method="xhtml"/>

      <xsl:template match="/">

        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">

        <head>

          <title>My Web Site - <xsl:value-of select="mySite/@title" /></title>

          <meta content="My homepage!" name="Description" />

          <meta content="My, stuff, cool page" name="Keywords" />

          <meta content="ALL" name="Robots" />

          <meta content="Copyright 2001 Joe-Webmaster" name="Copyright" />

          <meta content="Joe-Webmaster" name="Author" />

          <link href="main.css" rel="stylesheet" type="text/css" />

        </head>

        <body>

          <br />

          <xsl:call-template name="linkbar" />

          <br />

          <xsl:call-template name="content" />

        </body>

        </html>

      </xsl:template>

      <xsl:template name="linkbar">

        <xsl:for-each select="mySite/sidebar/item">

          <xsl:if test='@type="link"'>

            <a href="{uri}" title="{desc}"><xsl:value-of select="title" /></a><br />

          </xsl:if>

          <xsl:if test='@type="form"'>

            <form method="{method}" action="{action}">

              <xsl:for-each select="input">

                <input class="search" type="{@type}" name="{@name}" size="10"

                value="Search" maxlength="{@maxlength}" />

              </xsl:for-each>

            </form>

          </xsl:if>

        </xsl:for-each>

      </xsl:template>

      <xsl:template name="content">

        <strong><xsl:value-of select="mySite/@page" /></strong>

        <br />

        <xsl:for-each select="mySite/greeting/para">

          <p>

            <xsl:value-of select="."/>

          </p>

        </xsl:for-each>

          <hr width="454" align="center" />

          <span class="news">News::</span>

          <br />

          <div class="news">

            <xsl:for-each select="document('arg:/stories')/news-story">

              <span class="newstime"><xsl:value-of select="date" /></span>

              <div class="newsitem">

                <xsl:value-of select="story" />

              </div>

              <br />

            </xsl:for-each>

          </div>

      </xsl:template>

    </xsl:stylesheet>

You could repeat this and create many different XSL files to output many different pages (such as a WML page for viewing on a WAP enabled device). If you stored more dynamic content in another slot of the $xsltArg array, you can retrieve it in your XSL file by using:

    document('arg:/<name of slot>')

where <name of slot> would be the name of the slot in the array.

Now all that is left to do is apply the XSL transformation to the XML file. To do this, I use php's XSLT extension, Sablotron. I should mention that Sablotron is not enabled in default php installations. To activate it on a *nix machine, run the following:

    ./configure --enable-xslt --with-xslt-sablot

If you're being hosted by someone else, ask them to do it for you. I chose a server side transformation because many browsers lack the ability to do transformations on the client end. By doing the transformation on the server, you're guaranteed that the transformation will occur.

The simplest way to use XSLT in php is to pass the path to both the XML file and the XSL file to the xslt_run() function like this:

    // First create an XSLT processor.

    $xh= xslt_create();

    // $xsl holds the path to the XSL file. $xml holds the path to the XML file.

    xslt_run($xh, $xsl, $xml, "arg:/_result", NULL, $xsltArgs);

    $result= xslt_fetch_result($xh);

    // Finally, free the XSLT processor since we're done using it.

    xslt_free($xh);

The $result variable will hold the transformed page which you can then output using echo or do whatever else you may want to do.

The transformation process is a bit different on servers running PHP 4.1 and above. I believe, for completeness, I should explain how it is done. If you're running a lower of version of php, feel free to skip this part. With the advent of PHP 4.1, a new interface to the XSLT engine has been added. Now, to transform an XML file, one does the following:

    // First create an XSLT processor.

    $xh = xslt_create();

    // Next, pass either two variables, one holding the path to an XSL file and one

    // holding the path to an XML file. $result holds the transformed data.

    $result= xslt_process($xh, $xsl, $xml, NULL, $xsltArgs);

    // Finally, free the XSLT processor since we're done using it.

    xslt_free($xh);

There you have it, a simple way to create truly skinnable web sites. What I've just described is pretty basic, you can improve on it. For example, you can have php automatically determine if the user is on a WAP device and have it serve up a WAP page. Also, you can create many skins and have a page that displays a screen shot of each skin, letting the user choose which one he or she wants to view the site with. Then, you can store the user's choice in a cookie, so the next time the user visits the site, the skin he or she chose is already applied. I hope you learned a lot from this tutorial and start using this technique. As always, if you find any errors or have any comments please send them to me (spiderman@witty.com). Kindly direct questions to the message board. Until next time...

## Refrences:

- http://www.php.net/manual/en/ref.xslt.php
- http://www.gingerall.com/charlie/ga/act/gadoc.act?pg=sablot

Last updated: 12/22/01