<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet href="atom" type="text/xsl"?>
<!-- This is a comment that has been inserted because of the arrogance of IE7 and FireFox 2 developers that have decided that they don't need to honour a xml stylesheet instruction. Luckily the designers of these browsers use very brittle sniffing techniques that can be overridden by consuming the first 512 bytes of an xml file. This comment provides these essential 512 bytes of crud and destroys the nice simplicity and cleanliness of my Atom feed. This is a comment that has been inserted because of the arrogance of IE7 and FireFox 2 developers that have decided that they don't need to honour a xml stylesheet instruction. Luckily the designers of these browsers use very brittle sniffing techniques that can be overridden by consuming the first 512 bytes of an xml file. This comment provides these essential 512 bytes of crud and destroys the nice simplicity and cleanliness of my Atom feed. This is a comment that has been inserted because of the arrogance of IE7 and FireFox 2 developers that have decided that they don't need to honour a xml stylesheet instruction. Luckily the designers of these browsers use very brittle sniffing techniques that can be overridden by consuming the first 512 bytes of an xml file. This comment provides these essential 512 bytes of crud and destroys the nice simplicity and cleanliness of my Atom feed. -->
<feed 
	xmlns="http://www.w3.org/2005/Atom"
	xmlns:xforms="http://www.w3.org/2002/xforms"
	xml:lang="en">
	<title>FeedMind</title>
	<subtitle>The feed is the content is the feed</subtitle>
	<link rel="self" type="application/atom+xml" href="http://feedme.mind-it.info/"/>
	<updated>2008-01-21T11:59:42-00:00</updated>
	<author>
	<name>admin</name>
	<uri>http://feedme.mind-it.info/index.php</uri>
	<email>meint.post@bigfoot.com</email>
	</author>
	<id>tag:feedmind,2008:FeedMind</id>
	<generator uri="http://www.pivotlog.net" version="Pivot - 1.40.2: 'Dreadwind'">Pivot</generator>
	<rights>Copyright (c) 2008, Authors of FeedMind</rights>
			
	
	
	<entry>
		<title>Transactions, prepared statements and PHP mysqli</title>
		<updated>2008-01-21T11:59:00-00:00</updated>
		<published>2008-01-21T11:30:00-00:00</published>
		<id>tag:feedmind,2008:FeedMind.14</id>
		<summary></summary>
		<link rel="alternate" href="%link_self%" />
		<category term="default, PHP, Web Technology" />
        <content type="xhtml" xml:lang="en" xml:base="http://feedme.mind-it.info/pivot/entry.php?id=14">
					<div xmlns="http://www.w3.org/1999/xhtml">
                <p>While working with my good friend Arnold Consten on his new PHP application we came across some nice learning points for dealing with mysqli transactions and prepared statements. It turns out that the order of events is very specific for transactions and prepared statements to work correctly together:</p><br />
<ol><br />
<li>Make a connection with the database server</li><br />
<li>Disable auto commit</li><br />
<li>Initialize all prepared statements</li><br />
<li>Initialize all query templates</li><br />
<li>Prepare all statements</li><br />
<li>Assign all bind parameters</li><br />
<li>Execute</li><br />
<li>Do a rollback if an error occurs in any of the situations here above</li><br />
<li>If no errors commit the transaction</li><br />
<li>Close the prepared statements</li><br />
<li>Done</li><br />
</ol> <br />
<br />
<p>In code this boils down to the following example:</p><br />
<code><br />
$season_ID = $_POST[frmhiddenseason_ID];<br />
$class_ID = $_POST[frmhiddenclass_ID];<br />
<br />
$link = mysqli_connect("localhost", "my_user", "my_password", "world");<br />
<br />
/* check connection */<br />
if (!$link) {<br />
    printf("Connect failed: %s\n", mysqli_connect_error());<br />
    exit();<br />
}<br />
<br />
mysqli_autocommit($link, FALSE);<br />
<br />
$stmt1 = mysqli_stmt_init($link);<br />
$stmt2 = mysqli_stmt_init($link);<br />
$stmt3 = mysqli_stmt_init($link);<br />
<br />
$sql1 = "INSERT INTO tbl_person (firstname, lastname) VALUES (?, ?)";<br />
$sql2 = "INSERT INTO tbl_person_group (season_ID, class_ID, student_ID) VALUES (?,?,?)";<br />
$sql3 = "UPDATE tbl_person SET firstname = ?, lastname = ? WHERE tbl_person.student_ID = ?";<br />
   <br />
if (mysqli_stmt_prepare($stmt1, $sql1) &amp;&amp; mysqli_stmt_prepare($stmt2, $sql2) &amp;&amp; mysqli_stmt_prepare($stmt3, $sql3)) {<br />
   <br />
	mysqli_stmt_bind_param($stmt1, "ss", $firstname, $lastname);<br />
	mysqli_stmt_bind_param($stmt2, "ssi", $season_ID, $class_ID, $newstudent_ID);<br />
	mysqli_stmt_bind_param($stmt3, "ssi", $firstname, $lastname, $student_ID);<br />
   <br />
		for ($counter = 0; $counter &lt;= 10; $counter++)    {<br />
			$frmhiddenstudent_ID = "hidden" . $counter;<br />
			$student_ID = $_POST[$frmhiddenstudent_ID];  				<br />
			$frmfirstname = "firstname" . $counter;<br />
			$frmlastname = "lastname" . $counter;<br />
<br />
			if ($_POST[$frmfirstname] &lt;&gt; "" &amp;&amp; $_POST[$frmlastname] &lt;&gt; "") {<br />
				$firstname = $_POST[$frmfirstname];<br />
				$lastname = $_POST[$frmlastname];<br />
			}<br />
     <br />
			if (empty($student_ID)) { <br />
				mysqli_stmt_execute($stmt1);<br />
				$newstudent_ID = mysqli_insert_id($link);<br />
				mysqli_stmt_execute($stmt2);<br />
			} else {<br />
				mysqli_stmt_execute($stmt3);            <br />
			}                                          <br />
		}<br />
	}<br />
} else {<br />
	echo "Error";<br />
	mysqli_rollback($link);<br />
}   <br />
   <br />
mysqli_commit($link);<br />
mysqli_stmt_close($stmt1);<br />
mysqli_stmt_close($stmt2);<br />
mysqli_stmt_close($stmt3);<br />
</code><br />
<br />
<p>And voila, transactions and prepared statements working nicely together making for a robust and safe database handling solution.</p>
					</div>
				</content>
		<author>
			<name>admin</name>
		</author>
		
					
	</entry>
	
	
	
	<entry>
		<title>Templates, template engines and PHP</title>
		<updated>2007-06-06T19:52:00-00:00</updated>
		<published>2007-06-05T20:33:00-00:00</published>
		<id>tag:feedmind,2008:FeedMind.13</id>
		<summary></summary>
		<link rel="alternate" href="%link_self%" />
		<category term="PHP, Web Technology, XML/XSL" />
        <content type="xhtml" xml:lang="en" xml:base="http://feedme.mind-it.info/pivot/entry.php?id=13">
					<div xmlns="http://www.w3.org/1999/xhtml">
                In the process of developing <a href="http://sourceforge.net/projects/lilliputcms">Lilliput CMS</a> I had to think about how to do templating with PHP. There's a lot of material available regarding PHP and templating and most of it is really weird. Having had a look at the <a href="http://www.whenpenguinsattack.com/2006/07/19/php-template-engine-roundup/">Top 25 PHP template engines</a> I can't for the life of me understand why I would want to use something like <a href="http://smarty.php.net">Smarty</a>, <a href="http://phpsavant.com/">Savant</a> or <a href="http://phptal.motion-twin.com/">phptal</a>. Obviously a lot of love and attention has been poured into these solutions but I can't escape the feeling that these template engines are recreating PHP and its innate templating function. This feeling was confirmed when reading the <a href="http://www.phppatterns.com/docs/design/templates_and_template_engines">"Templates and template engines" article</a> on the php patterns website. The article inspired the following train of thought:<br />
<ul><br />
<li>Don't try to recreate PHP and its innate templating functions, it's fine as it is. This doesn't necessarily mean you should only use PHP for templating, rather that you shouldn't rebuild PHP</li><br />
<li>There's no sense in separating logic and content as they are intertwined and dependent on each other; focus on separating content and presentation (with the help of clean XHTML and CSS)</li><br />
<li>Use XHTML for templates instead of HTML 4 Strict so you can benefit from the strictness introduced by XML parsers</li><br />
<li>Templates are created by designers, they shouldn't need to learn a new language to accomplish their goals so the templates should use no frills XHTML</li><br />
<li>Because the templates use XHTML and the output format is XHTML there is no need for a template processing/transformation language, i.e. XSLT</li><br />
<li>There's only one standardized API for manipulating content and that's the DOM so use this for interaction with template and content</li><br />
<li>It should be simple to the point of sacrificing functionality to keep it simple, it won't be all things to all people</li><br />
<li>It needs to be extensible, you can't predict all use cases and you need to offer a way for people to introduce different behavior whilst maintaining forward compatibility</li><br />
</ul><br />
So what has this resulted in? The template needs to be valid XHTML and the designer doesn't need to learn new language constructs (so no phptal like solution). The simplest solution is to use content elements in the template that are replaced with the generated content resulting from business logic processing. Because I'm treating the XHTML templates as real XML files we need to manipulate the content via a formal API for XML, i.e. the DOM. The ground rules for this are laid out in two succinct articles <a href="http://www.tbray.org/ongoing/When/200x/2004/01/11/PostelPilgrim">"On Postel, Again"</a> and <a href="http://www.codinghorror.com/blog/archives/000647.html">"Are You an XML Bozo?"</a>. The replaceable elements are identified by XHTML id's, found via XPath queries and replaced via a DOM manipulation as demonstrated by the following example code.<br />
<br />
Template code:<br />
<div id="code"><br />
&lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"&gt;<br />
&lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"&gt;<br />
&lt;head&gt;<br />
	&lt;title&gt;Lilliput CMS&lt;/title&gt;<br />
	&lt;meta http-equiv="content-type" content="text/html; charset=utf-8" /&gt;<br />
	&lt;link rel="shortcut icon" href="/lilliput/favicon.ico" /&gt;<br />
	&lt;link rel="stylesheet" href="/lilliput/template/lilliput/css/style.css" type="text/css" media="screen, projection" /&gt;<br />
	&lt;script src="/lilliput/template/lilliput/javascript/script.js" type="text/javascript"&gt;&lt;/script&gt;<br />
&lt;/head&gt;<br />
 <br />
&lt;body&gt;<br />
<br />
&lt;div id="wrapper"&gt;<br />
<br />
		&lt;div id="header"&gt;<br />
				&lt;p class="description"&gt;Helping you eat boiled eggs since 2006.&lt;/p&gt;<br />
				&lt;h1&gt;&lt;a href="http://feedme.mind-it.info#"&gt;Lilliput CMS&lt;/a&gt;&lt;/h1&gt;<br />
				&lt;ul id="menu"&gt;<br />
				&lt;/ul&gt;<br />
		&lt;/div&gt;<br />
						<br />
		&lt;div id="content" /&gt;<br />
				<br />
		&lt;div id="footer"&gt;<br />
			&lt;p&gt;This text to be replaced.&lt;/p&gt;<br />
		&lt;/div&gt;<br />
<br />
&lt;/div&gt;<br />
<br />
&lt;/body&gt;<br />
&lt;/html&gt;<br />
</div><br />
<br />
The PHP controller code (invoking model actions for retrieving data) that generates the content and passes it on to the view logic:<br />
<div id="code"><br />
function controller_addUser() {<br />
<br />
	// invoke model logic to get the data<br />
	$results = model_addUser();<br />
	// start new DOM instance for document creation<br />
	$dom = new DomDocument('1.0', 'UTF-8');<br />
	$selectElement = $dom->createElement('select');<br />
	$selectElement = $dom->appendChild($selectElement);<br />
	$selectElement->setAttribute('id', 'fm-role');<br />
	$selectElement->setAttribute('name', 'fm-role');<br />
	$resultsCount = count($results);<br />
	for ($row = 0; $row &lt; $resultsCount; $row++) {<br />
		$role = $results[$row]["role"];<br />
		$optionElement = $dom->createElement('option');<br />
                $optionElement = $selectElement->appendChild($optionElement);<br />
                $optionElement->setAttribute('value', $role);<br />
                $elementValue = $dom->createTextNode($role);<br />
                $elementValue = $optionElement->appendChild($elementValue);<br />
	}<br />
	// pass string data to view and get rendered XHTML structure back<br />
	$content["html"] = view_addUser($dom->saveXML($selectElement));<br />
	// return rendered XHTML structure to index.php dispatcher for inclusion in page template<br />
	return $content;<br />
}<br />
</div><br />
<br />
PHP template handling code using PHP 5 XML and DOM functions:<br />
<div id="code"><br />
function processTemplate($templateFile, $content) {<br />
	// Initialize DOM document<br />
	$dom = new DomDocument('1.0', 'utf-8');<br />
        // load the template file into the DOM document<br />
	$returnValue = $dom->load($templateFile);<br />
	$xml = explode("\n", file_get_contents($templateFile));<br />
        // call the replaceDomContent function that substitutes &lt;div id="content"/&gt; with the generated content<br />
	replaceDomContent($dom, '//xhtml:div[@id="content"]', $content["html"]);<br />
        // call the getTitle function to retrieve the title element and replace the content with the generated title<br />
	$title = getTitle($dom);<br />
	replaceNodeContent($dom, "//xhtml:title", $title);<br />
        // export the DOM structure into an XML format<br />
	return $dom->saveXML();<br />
}<br />
<br />
function getTitle($dom) {<br />
	$xpath = new DOMXPath($dom);<br />
	$resultNode = $xpath->query("//h2");<br />
	$title = $resultNode->item(0)->nodeValue;<br />
	return $title;<br />
}<br />
<br />
function replaceDomContent($dom, $query, $content) {<br />
	$xpath = new DOMXPath($dom);<br />
	// register the xhtml namespace otherwise xpath queries will fail<br />
	$xpath->registerNamespace("xhtml", "http://www.w3.org/1999/xhtml");<br />
	$nodelist = $xpath->query($query);<br />
	$oldnode = $nodelist->item(0);<br />
	libxml_use_internal_errors(true);<br />
	$contentImport = new DOMDocument();<br />
	$returnValue = $contentImport->loadXML($content);<br />
	$xml = explode("\n", $content);<br />
	// import the converted XML content into the DOM structure<br />
	$newnode = $dom->importNode($contentImport->documentElement, true);<br />
	// Replace the old content with the new content<br />
	$oldnode->parentNode->replaceChild($newnode, $oldnode);<br />
	return $dom;<br />
}<br />
<br />
function replaceNodeContent($dom, $query, $content) {<br />
	$xpath = new DOMXPath($dom);<br />
	// register the xhtml namespace otherwise xpath queries will fail<br />
	$xpath->registerNamespace("xhtml", "http://www.w3.org/1999/xhtml");<br />
	$nodelistContent = $xpath->query($query);<br />
	$nodelistContent->item(0)->nodeValue = $content;<br />
	return $dom;<br />
}<br />
</div>
					</div>
				</content>
		<author>
			<name>admin</name>
		</author>
		
					
	</entry>
	
	
	
	<entry>
		<title>Lies, damned lies and caching</title>
		<updated>2007-02-24T19:30:00-00:00</updated>
		<published>2007-02-20T22:03:00-00:00</published>
		<id>tag:feedmind,2008:FeedMind.12</id>
		<summary>How to create a caching policy for IIS 5 using VBScript</summary>
		<link rel="alternate" href="%link_self%" />
		<category term="default, XML/XSL" />
        <content type="xhtml" xml:lang="en" xml:base="http://feedme.mind-it.info/pivot/entry.php?id=12">
					<div xmlns="http://www.w3.org/1999/xhtml">
                One of the most excellent aspects of HTTP and the underpinning <a href="http://www.ics.uci.edu/~fielding/pubs/dissertation/rest_arch_style.htm" title="REST">REST architecture</a> is the aspect of idempotence and non-idempotence.  Idempotence  roughly  means  that  some  operation  yields the same result  whether it  is done  only  once  or several  times. This  behaviour forms the basis of caching because if the result is  the same you can work  with a copy  of the  result for all subsequent  requests. This excellent property  of the  web  also  has some  downsides as  the Law  of Preservation  of Complexity demands that for any goodness  in technology  there always  is a  trade off   in increased  complexity somewhere else. In  the case of caching it  is determining when to cache and  when not to cache.<br />
<br />
The website  that is  the topic  of this  entry is  one of  the largest  banking websites of Europe and it is based on  IIS 5 which doesn't do a lot out-of-  the -box  caching  wise.  Problem  is that  if  your  webserver  doesn't supply  any information about the validity of the objects it serves you will find out fairly quickly  that many  parties to  the conversation  will cache  each object  quite agressively. The browser is one of  these parties but also many caching  proxies that are used by the target  infrastructure or your ISP without you  knowing it. The only way of forcing all intermediate parties to relinquish their hold on the  page objects is to change the names of the objects (filenames for clarity). <br />
<br />
A better aproach is to use <a href="http://www.mnot.net/cache_docs/">cache  control</a> to specify the time-to-live (TTL) for each page object. This way the  browser and all intermediate parties know how to treat the page objects. When  the TTL for a given object has expired the browser (or intermediate proxy) will  inquire at the target server if the object is still valid. If so the web server  will respond with a 304 Not Modified instead of retransmitting the page object saving valuable bandwidth.<br />
<br />
However as indicated the IIS 5 webserver doesn't have a lot of easily accesible cache settings available to it. There are some third party modules that you can use for this stuff  but we're talking about a website with upwards of 2 million page views per day  and you can't just slap in any ISAPI filter you'd like and trust that everything  will turn out right (not to speak of all the test work involved when introducing  an additional component in your infrastructure). One of the few components that  I did use on the website (totally unrelated to the subject) was <a  href="http://www.isapirewrite.com/" title="">ISAPI Rewrite</a> from Helicon Tech  and it never failed us through the most extreme loads. I'm usually loath to  promote vendors but these guys offer amazing value for money and offered first  class support even though we paid a bargain price for their software. <br />
<br />
Ok enough  with the product talk, back to the subject of applying a caching policy to IIS.  After reading up on the subject I decided to create a VBScript script for  injecting the proper caching instructions in the IIS Metabase. This way the  support team could apply the same policy to all machines without the risk of  manual error. The script is based on various scrapings found around the web:<br />
<br />
<div id="code"><br />
Option Explicit<br />
Call Main<br />
<br />
Sub Main<br />
	Dim RootDir, CSSDir, JSDir, ImagesDir, JavascriptDir, CSSFile, JSFile, ServerName, SiteNumber, oArgs, iArgNum,<br />
	' ServerName is default at localhost but can be changed<br />
	' via the -s "SERVERNAME" switch<br />
	ServerName = "localhost"<br />
	' SiteNumber is set at 1, the default website in IIS<br />
	' this can be changed via the -i "SITENUMBER" switch<br />
	SiteNumber = "1"<br />
	Set oArgs = WScript.Arguments<br />
	iArgNum = 0<br />
	While iArgNum &lt; oArgs.Count<br />
		Select Case LCase(oArgs(iArgNum))<br />
			Case "-s","--ServerName":<br />
				iArgNum = iArgNum + 1<br />
				ServerName = oArgs(iArgNum)<br />
			Case "-i","--SiteNumber":<br />
				iArgNum = iArgNum + 1<br />
				SiteNumber = oArgs(iArgNum)<br />
			Case "-?","--help":<br />
				Call DisplayUsage<br />
				WScript.Quit(1)<br />
			Case Else:<br />
				WScript.Echo "Unknown argument " &amp; oArgs(iArgNum)<br />
				Call DisplayUsage<br />
				WScript.Quit(1)<br />
		End Select<br />
		iArgNum = iArgNum + 1<br />
	Wend<br />
	WScript.Echo "ServerName: " &amp; ServerName<br />
	WScript.Echo "SiteNumber: " &amp; SiteNumber<br />
	WScript.Echo "Default caching policy for the entire site is caching for 2 hours (7200 seconds)"<br />
	Set RootDir = GetRootDir(ServerName, SiteNumber)<br />
	Call SetVDirCacheability(RootDir, 7200)<br />
	WScript.Echo "Caching policy for /images is caching for 1 month (2592000 seconds)"<br />
	Call GetVDirAndSetCacheability("images", ServerName, SiteNumber, 2592000)<br />
	WScript.Echo "Caching policy for /javascript is caching for 1 week (604800 seconds)"<br />
	Call GetVDirAndSetCacheability("javascript", ServerName, SiteNumber, 604800)<br />
	WScript.Echo "Caching policy for /css is caching for 1 week (604800 seconds)"<br />
	Set CSSDir = GetVDirAndSetCacheability("css", ServerName, SiteNumber, 604800)          <br />
	WScript.Echo "Caching policy for /javascript is caching for 1 week (604800 seconds)"<br />
	Set JSDir = GetVDirAndSetCacheability("javascript", ServerName, SiteNumber, 604800)<br />
	WScript.Echo "/css/super.css must not be cached! Exception policy will be applied ..."<br />
	On Error Resume Next<br />
	Set CSSFile = GetObject("IIS://" &amp; ServerName &amp; "/w3svc/" &amp; SiteNumber &amp; "/root/css/super.css")<br />
	If (Err.Number &lt;&gt; 0) Then<br />
		WScript.Echo "/css/super.css file does not exist yet in metabase, status code " &amp; Err.Number<br />
		Err.Clear<br />
		Set CSSFile = CSSDir.Create("IIsWebFile", "super.css")<br />
		If (Err.Number &lt;&gt; 0) Then<br />
			Wscript.Echo "/css/super.css object in metabase could not be created, error code " &amp; Err.Number<br />
			Wscript.Quit<br />
		Else<br />
			WScript.Echo "/css/super.css object created in metabase and cache policy correctly applied"<br />
		End If<br />
		CSSFile.SetInfo<br />
	Else<br />
		WScript.Echo "/css/super.css object already exists in metabase"<br />
	End If<br />
	Call SetCacheability(CSSFile, 3600, "must-revalidate")<br />
	WScript.Echo "/javascript/include.js must not be cached! Exception policy will be applied ..."<br />
	On Error Resume Next<br />
	Set JSFile = GetObject("IIS://" &amp; ServerName &amp; "/w3svc/" &amp; SiteNumber &amp; "/root/javascript/include.js")<br />
	If (Err.Number &lt;&gt; 0) Then<br />
		WScript.Echo "/javascript/include.js file does not exist yet in metabase, status code " &amp; Err.Number<br />
		Err.Clear<br />
		Set JSFile = JSDir.Create("IIsWebFile", "include.js")<br />
		If (Err.Number &lt;&gt; 0) Then<br />
			Wscript.Echo "/javascript/include.js object in metabase could not be created, error code " &amp; Err.Number<br />
			Wscript.Quit<br />
		Else<br />
			WScript.Echo "/javascript/include.js object created in metabase and cache policy correctly applied"<br />
		End If<br />
		JSFile.SetInfo<br />
	Else<br />
		WScript.Echo "/javascript/include.js object already exists in metabase"<br />
	End If<br />
	Call SetCacheability(JSFile, 3600, "must-revalidate")<br />
	WScript.Echo ""<br />
	WScript.Echo ""<br />
	Call SetHTMLCharSet(ServerName, SiteNumber)<br />
End Sub<br />
<br />
Function GetRootDir(ServerName, SiteNumber)<br />
	Dim RootDir<br />
	On Error Resume Next<br />
	Set RootDir = GetObject("IIS://" &amp; ServerName &amp; "/w3svc/" &amp; SiteNumber &amp; "/root")<br />
	If (Err.Number &lt;&gt; 0) Then<br />
		WScript.Echo "Root dir does not exist for site index " &amp; SiteNumber &amp; ", error code " &amp; Err.Number<br />
		WScript.Quit<br />
	Else<br />
		WScript.Echo "Root dir found for site index " &amp; SiteNumber<br />
	End If<br />
	Set GetRootDir = RootDir<br />
End Function<br />
<br />
Function SetVDirCacheability(IISObject, CacheTimeInSeconds)<br />
	Call SetCacheability(IISObject, CacheTimeInSeconds, "must-revalidate")<br />
End Function<br />
<br />
Function SetCacheability(IISObject, CacheTimeInSeconds, CacheDirective)<br />
	On Error Resume Next<br />
	IISObject.CacheControlCustom = "max-age=" &amp; CacheTimeInSeconds &amp; "," &amp; CacheDirective<br />
	If (Err.Number &lt;&gt; 0) Then<br />
		WScript.Echo "CacheControlCustom property set failed, error code: " &amp; Err.Number<br />
		WScript.Quit<br />
	Else<br />
		WScript.Echo "CacheControlCustom property set: max-age=" &amp; CacheTimeInSeconds &amp; "," &amp; CacheDirective<br />
	End If<br />
	IISObject.SetInfo<br />
End Function<br />
<br />
Function GetVDirEvenIfNotExists(DirName, ServerName, SiteNumber)<br />
	Dim Dir<br />
	Dim RootDir<br />
	On Error Resume Next<br />
	Set Dir = GetObject("IIS://" &amp; ServerName &amp; "/w3svc/" &amp; SiteNumber &amp; "/root/" &amp; DirName)<br />
	If (Err.Number &lt;&gt; 0) Then<br />
		WScript.Echo "VDir " &amp; DirName &amp; " does not exist yet"<br />
		Set RootDir = GetRootDir(ServerName, SiteNumber)<br />
		Set Dir = RootDir.Create("IIsWebVirtualDir", DirName)<br />
		If (Err.Number &lt;&gt; 0) Then<br />
			WScript.Echo "VDir " &amp; DirName &amp; " could not be created, error code " &amp; Err.Number<br />
			WScript.Quit<br />
		Else<br />
			WScript.Echo "VDir " &amp; DirName &amp; " created"<br />
		End If<br />
		Dir.SetInfo<br />
	Else<br />
		WScript.Echo "VDir " &amp; DirName &amp; " already exists"<br />
	End If<br />
	Set GetVDirEvenIfNotExists = Dir<br />
End Function<br />
 <br />
Function GetVDirAndSetCacheability(DirName, ServerName, SiteNumber, CacheTimeInSeconds)<br />
	Dim Dir<br />
	Set Dir = GetVDirEvenIfNotExists(DirName, ServerName, SiteNumber)<br />
	Call SetVDirCacheability(Dir, CacheTimeInSeconds)<br />
	Set GetVDirAndSetCacheability = Dir<br />
End Function<br />
 <br />
Sub SetHTMLCharSet(ServerName, SiteNumber)<br />
	Dim strExtension, strMimeType<br />
	strExtension = ".htm"<br />
	strMimeType = "text/html; charset=utf-8"<br />
	AddTypeToIIS ServerName, strExtension, strMimeType<br />
	strExtension = ".html"<br />
	strMimeType = "text/html; charset=utf-8"<br />
	AddTypeToIIS ServerName, strExtension, strMimeType<br />
End Sub<br />
 <br />
Sub AddTypeToIIS(ServerName, varMimeExt, varMimeTyp)<br />
	Dim boolFound, intCount, intMimeMap, objMimeMap, varMimeMap, i, MMItem, aMimeMapNew()<br />
	Const ADS_PROPERTY_UPDATE = 2<br />
	' create the ADSI object &amp; current MIME map at that path<br />
	Set objMimeMap = GetObject("IIS://" &amp; ServerName &amp; "/MimeMap")<br />
	varMimeMap = objMimeMap.GetEx("MimeMap")<br />
	'Delete a mapping by copying to a new map array.<br />
	i = 0<br />
	For Each MMItem in varMimeMap<br />
		If MMItem.Extension &lt;&gt; varMimeExt Then<br />
			Redim Preserve aMimeMapNew(i)<br />
			Set aMimeMapNew(i) = CreateObject("MimeMap")<br />
			aMimeMapNew(i).Extension = MMItem.Extension<br />
			aMimeMapNew(i).MimeType = MMItem.MimeType<br />
			i = i + 1<br />
		Else<br />
			WScript.Echo "Mime type " &amp; MMItem.Extension &amp; " excluded"<br />
		End If<br />
	Next<br />
	objMimeMap.PutEx ADS_PROPERTY_UPDATE, "MimeMap", aMimeMapNew<br />
	objMimeMap.SetInfo<br />
	' get the MIME map count<br />
	intMimeMap = UBound(aMimeMapNew) + 1<br />
	' if no extension information is found, create the new mapping<br />
	ReDim Preserve aMimeMapNew(intMimeMap)<br />
	Set aMimeMapNew(intMimeMap) = CreateObject("MimeMap")<br />
	aMimeMapNew(intMimeMap).Extension = varMimeExt<br />
	' store the new information in the MIME map<br />
	aMimeMapNew(intMimeMap).MimeType = varMimeTyp<br />
	objMimeMap.PutEx ADS_PROPERTY_UPDATE, "MimeMap", aMimeMapNew<br />
	objMimeMap.SetInfo<br />
End Sub<br />
<br />
Sub DisplayUsage<br />
	WScript.Echo "Usage: iis_caching_policy.vbs &lt;-s|--ServerName ""SERVERNAME""&gt; &lt;-i|--SiteNumber ""SITENUMBER""&gt; [-?|--Help]"<br />
	WScript.Echo ""<br />
	WScript.Echo "SERVERNAME Optional - The name of the server, default is localhost"<br />
	WScript.Echo "SITENUMBER   Optional - The sitenumber of the affected site, default is 1"<br />
	WScript.Echo ""<br />
	WScript.Echo "Example: iis_caching_policy.vbs -s tracker -i 3"<br />
	WScript.Quit (1)<br />
End Sub<br />
</div><br />
<br />
If you look closely at the script (not too close, it's not a major work of art)  you can see there's an exception for two files: super.css and  include.js. The reason for this is that all pages are published as static  pages from a Content Management System. Static pages deliver the optimum trade  off in speed versus processing power. The problem with static pages, idempotent  behavior and caching however is that if you do your work very well hardly any  updates will get over to the customer unless you change filenames. This is  rather unwieldy with a large website as this would require continuous  republication of the website when you want to deliver new javascript or css  files. The trick I applied was to have a "super" CSS file that includes all  other CSS files. The super CSS file has a very limited time span  (1 hour), as demonstrated by the VBScript, so you can update the included CSS files by changing their filenames  and the client will notice the updated CSS files after the super CSS file has  expired after one hour without having to republish all web pages. The same trick  applies to the Javascript files.<br />
<br />
Oh, by the way this stuff is a breeze to setup in Apache but sometimes you have to work with the  stuff at hand.
					</div>
				</content>
		<author>
			<name>admin</name>
		</author>
		
					
	</entry>
	
	
	
	<entry>
		<title>PHP, DOM and XPath query</title>
		<updated>2007-02-24T19:42:00-00:00</updated>
		<published>2007-01-28T15:56:00-00:00</published>
		<id>tag:feedmind,2008:FeedMind.11</id>
		<summary>How to use PHP XPath queries with XHTML</summary>
		<link rel="alternate" href="%link_self%" />
		<category term="PHP, XML/XSL" />
        <content type="xhtml" xml:lang="en" xml:base="http://feedme.mind-it.info/pivot/entry.php?id=11">
					<div xmlns="http://www.w3.org/1999/xhtml">
                My work for the <a href="https://sourceforge.net/projects/lilliputcms/">Lilliput CMS</a> has lead to some interesting new findings about PHP 5's new DOM functions, particularly the XPath part of it. Whilst working on the templating structure for Lilliput I couldn't get the DOM XPath queries to work on the file at hand, a normal XHTML 1.0 Strict document. With an external tool called <a href="https://sourceforge.net/projects/xpe/">XPath Explorer (XPE)</a> every query evaluated correctly but as soon as I tried the same XPath expression in PHP it failed. After searching long and hard I came across a code snippet that contained the solution namely to explicitly declare the xhtml namespace for the DOM document you're working on:<br />
<br />
<code><br />
&lt;?php<br />
$templateFile = 'template/lilliput/index.xhtml';<br />
$dom = new DomDocument('1.0', 'utf-8');<br />
$returnValue = $dom->load($templateFile);<br />
$xpath = new DOMXPath($dom);<br />
$xpath->registerNamespace("xhtml", "http://www.w3.org/1999/xhtml");<br />
$nodelist = $xpath->query('//xhtml:div[@id="content"]');<br />
$noderesult = $nodelist->item(0);<br />
?&gt;<br />
</code><br />
<br />
After this no problems any more but very strange that this isn't mentioned in the PHP DOM documentation anywhere. Even stranger that my user contributed note on this on the <a href="http://www.php.net/manual/en/function.dom-domxpath-query.php">XPath query function manual page</a> doesn't seem to have been accepted. Hopefully somebody will gain some help from this blog entry.
					</div>
				</content>
		<author>
			<name>admin</name>
		</author>
		
					
	</entry>
	
	
	
	<entry>
		<title>Atom feeds, client-side XSLT and crappy browsers</title>
		<updated>2007-02-24T20:29:00-00:00</updated>
		<published>2007-01-09T14:34:00-00:00</published>
		<id>tag:feedmind,2008:FeedMind.9</id>
		<summary>Automatic recognition of feeds and subsequent applicability of browser XSL stylesheets can be avoided by taking up the first 512 bytes with comments</summary>
		<link rel="alternate" href="%link_self%" />
		<category term="XML/XSL" />
        <content type="xhtml" xml:lang="en" xml:base="http://feedme.mind-it.info/pivot/entry.php?id=9">
					<div xmlns="http://www.w3.org/1999/xhtml">
                After providing feedback on an article on 24 ways called <a href="http://24ways.org/2006/beautiful-xml-with-xsl">"Making XML Beautiful Again: Introducing Client-Side XSL"</a> <span class="strikethrough">that never got published</span> I decided to write a short entry that explains how this feedsite escapes the feed sniffing and subsequent browser applied default XSLT stylesheet that has been implemented by Safari 2, FireFox 2 and Internet Explorer 7. As soon as these browsers see content they perceive as a feed they ignore all instructions like an XSL style sheet inclusion and render the contents to their liking instead of the authors liking.<br />
<br />
Luckily enough the method these browsers employ for feed sniffing is crude enough to trick them into believing they're not dealing with a genuine feed by eating up the first 512 bytes of the feed with unrecognizable crud. So basically the only thing you need to do is insert a comment before the feed element that is larger than 512 bytes. An excerpt from the source code of this page:<br />
<br />
<code>&lt;?xml version="1.0" encoding="utf-8"?&gt;<br />
&lt;?xml-stylesheet href="atom" type="text/xsl"?&gt;<br />
&lt;!-- This is a comment that has been inserted because of the arrogance of IE7 and FireFox 2 developers that have decided that they don't need to honour a xml stylesheet instruction. Luckily the designers of these browsers use very brittle sniffing techniques that can be overridden by consuming the first 512 bytes of an xml file. This comment provides these essential 512 bytes of crud and destroys the nice simplicity and cleanliness of my Atom feed. This is a comment that has been inserted because of the arrogance of IE7 and FireFox 2 developers that have decided that they don't need to honour a xml stylesheet instruction. Luckily the designers of these browsers use very brittle sniffing techniques that can be overridden by consuming the first 512 bytes of an xml file. This comment provides these essential 512 bytes of crud and destroys the nice simplicity and cleanliness of my Atom feed. This is a comment that has been inserted because of the arrogance of IE7 and FireFox 2 developers that have decided that they don't need to honour a xml stylesheet instruction. Luckily the designers of these browsers use very brittle sniffing techniques that can be overridden by consuming the first 512 bytes of an xml file. This comment provides these essential 512 bytes of crud and destroys the nice simplicity and cleanliness of my Atom feed. --&gt;<br />
&lt;feed<br />
    xmlns="http://www.w3.org/2005/Atom"<br />
    xmlns:xforms="http://www.w3.org/2002/xforms"<br />
    xml:lang="e&gt;<br />
    &lt;title&gt;FeedMind&lt;/title&gt;<br />
</code><br />
<br />
And presto you can apply your own client-side XSLT to your hearts content.
					</div>
				</content>
		<author>
			<name>admin</name>
		</author>
		
					
	</entry>
	
	
	
	<entry>
		<title>Building this site</title>
		<updated>2007-03-02T20:17:00-00:00</updated>
		<published>2006-02-14T10:33:00-00:00</published>
		<id>tag:feedmind,2008:FeedMind.10</id>
		<summary>A description of the techniques used to build this site</summary>
		<link rel="alternate" href="%link_self%" />
		<category term="XML/XSL" />
        <content type="xhtml" xml:lang="en" xml:base="http://feedme.mind-it.info/pivot/entry.php?id=10">
					<div xmlns="http://www.w3.org/1999/xhtml">
                This blog uses a novel approach (I think) in that it is entirely Atom 1.0 based. There is no underlying (X)HTML, everything is Atom (with a hint of XSL).  I came to this approach after I figured out that it is silly to serve the same content in multiple formats when one format suffices. Modern browser are perfectly capable of executing XML/XSL that allow for the transformation of the Atom 1.0 feed into XHTML. There's some nice stuff going, let's dig into the details (hey I'm a technologist at heart so why not busy myself with the stuff I love):<br />
<br />
The main engine is Pivot, http://www.pivotlog.net. I like Pivot more and more, it doesn't get in the way and I haven't had to tweak a single line of code to get where I'm at right now which is frankly amazing to me and a tribute to the design of Pivot.<br />
<br />
Pivot creates an Atom output file, index.xml (the filename is configurable in Pivot).<br />
<br />
This file is configured as default document via an Apache .htaccess instruction:<br />
<br />
<code>DirectoryIndex index.xml</code><br />
<br />
This means your browser gets XML (Atom) served directly when you access http://feedme.mind-it.info/ instead of HTML.<br />
<br />
Pivot has several templates available to apply to content output, one of them being an Atom 1.0 template. Due to Pivot's ingenious templating structure I was able to add an XSL stylesheet to the XML output without a problem:<br />
<br />
<code>&lt;?xml-stylesheet type="text/xsl" href="atom"?></code><br />
<br />
The XSL stylesheet is called atom, I use Apache content negotiation via Multiviews to deliver the correct XSL stylesheet with either application/xhtml+xml or text/html (Internet Explorer) as media format:<br />
<code><br />
Options +Multiviews<br />
<span class="strikethrough">AddType application/xhtml+xml;qs=0.9 .xsl+xhtml<br />
AddType text/html;qs=0.9 .xsl</span><br />
AddType application/xhtml+xml .xsl+xhtml<br />
AddType text/html .xsl+html<br />
</code><br />
The code above basically selects the right atom XSL file based on the capabilities of the browser. If the browser is application/xhtml+xml capable and prefers this media type (via the qs quality factor) the atom.xsl+xhtml file will be served. If on the other hand the browser isn't willing or capable (think Internet Explorer) it will be served the atom.xsl+html file.<br />
<br />
atom.xsl+html:<br />
<code><br />
&lt;?xml version="1.0" encoding="UTF-8"?><br />
&lt;xsl:stylesheet version="1.0" <br />
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" <br />
		xmlns:dc="http://purl.org/dc/elements/1.1/"<br />
		xmlns:atom="http://www.w3.org/2005/Atom"<br />
		xmlns:xhtml="http://www.w3.org/1999/xhtml"<br />
		xmlns="http://www.w3.org/1999/xhtml"><br />
<br />
&lt;xsl:output encoding="utf-8" indent="yes" method="xml"<br />
				omit-xml-declaration="yes"<br />
        <strong>media-type="text/html"</strong><br />
        doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" <br />
        doctype-system="DTD/xhtml1-strict.dtd"<br />
        cdata-section-elements="script style"/><br />
<br />
&lt;xsl:include href="main.xml" /><br />
&lt;/xsl:stylesheet><br />
</code><br />
atom.xsl+xhtml:<br />
<code><br />
&lt;?xml version="1.0" encoding="UTF-8"?><br />
&lt;xsl:stylesheet version="1.0" <br />
		xmlns:xsl="http://www.w3.org/1999/XSL/Transform" <br />
		xmlns:dc="http://purl.org/dc/elements/1.1/"<br />
		xmlns:atom="http://www.w3.org/2005/Atom"<br />
		xmlns:xhtml="http://www.w3.org/1999/xhtml"<br />
		xmlns="http://www.w3.org/1999/xhtml"><br />
<br />
&lt;xsl:output encoding="utf-8" indent="yes" method="xml"<br />
				omit-xml-declaration="no"<br />
        <strong>media-type="application/xhtml+xml"</strong><br />
        doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN" <br />
        doctype-system="DTD/xhtml1-strict.dtd"<br />
        cdata-section-elements="script style"/><br />
<br />
&lt;xsl:include href="main.xml" /><br />
&lt;/xsl:stylesheet><br />
</code><br />
<span class="strikethrough">The XSL file itself uses an xsl:include to import the main part of the XSL transformation so I can have two relatively small XSL files to support the content negotiation (atom.xsl+html for text/html and atom.xsl+xhtml for application/xhtml+xml)</span>.<br />
<br />
All looked well but somehow the HTML formatting wasn't getting through in Mozilla FireFox. No problems in Internet Explorer though, strange ... This is the kind of conundrum that can take all day to solve and it did (take a whole day). On the positive side I learned a lot about XSL, CDATA, namespaces and local names but man what an annoying problem. It turns out that Internet Explorer is quite forgiving but Firefox is more strict in copying portions of XML in a different namespace in the result XML tree. The source document is in the Atom 1.0 namespace (http://www.w3.org/2005/Atom) whilst the result document is in the XHTML namespace (http://www.w3.org/1999/xhtml). Firefox will not allow you to do this (as per XSL specification). The solution took ages to find with various sidesteps involving trying to replace the Atom namespace via XSL but the solution turned out to be pretty simple. I just have to add a &lt;div&gt; element with the XHTML namespace to the content element and all is well. <br />
<code><br />
&lt;content type="xhtml" xml:lang="en" xml:base="http://feedme.mind-it.info/pivot/entry.php?id=8"&gt;<br />
&lt;div xmlns="http://www.w3.org/1999/xhtml"&gt;<br />
..<br />
&lt;/div&gt;<br />
&lt;/content&gt;<br />
</code><br />
<br />
Well that's basically it. I need to change and tweak a lot but the technique described here is basically what drives this feed/site. <br />
<br />
BTW if you're wondering about the layout of the site, I'm not much of a designer so I used a ready made layout from <a href="http://andreasviklund.com/" title="Various templates from Andreas Viklund">Andreas Viklund</a>. The photo at the top is mine though, I shot it in 2003 in Gyongju (South-Korea).
					</div>
				</content>
		<author>
			<name>admin</name>
		</author>
		
					
	</entry>
	
	
	
	<entry>
		<title>Welcome to FeedMind</title>
		<updated>2006-09-06T15:20:00-00:00</updated>
		<published>2006-02-14T08:51:00-00:00</published>
		<id>tag:feedmind,2008:FeedMind.7</id>
		<summary>Starting entry for feedmind explaining the content of the blog</summary>
		<link rel="alternate" href="%link_self%" />
		<category term="default" />
        <content type="xhtml" xml:lang="en" xml:base="http://feedme.mind-it.info/pivot/entry.php?id=7">
					<div xmlns="http://www.w3.org/1999/xhtml">
                Welcome to FeedMind, my personal blogging space and ongoing experiment in web technology. I've been thinking about doing a blog for years, started several times but never finished anything. This time it looks like I'm going to succeed :-) I'll be mainly blogging about ICT Architecture, Information Security and Web Technology in the broadest sense. Once in a while something personal may crop up, it can't be helped.<br />
<br />
I'm quite a practical guy although I do like the saying that "there's nothing more practical than a good theory". There's a lot of opinion floating around that's propagated by people who obviously haven't carried any large projects in the real world. There's a limit to the amount of new technology you can cram into any project and believe you me that five year old technology still counts as new. I hope to add a voice to the chorus that tries to explain that there's more than one way to do it (although certain problems can have only a limited amount of correct solutions). You see, I'm already being reasonable on the first posting, it's stronger than myself.<br />
<br />
Anyway, hope you like the blog.<br />
<br />
Cheers,<br />
<br />
Meint
					</div>
				</content>
		<author>
			<name>M.E. Post</name>
		</author>
		
					
	</entry>
	
	
	
</feed>
