<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Sam Huggill&#039;s Weblog</title>
	<atom:link href="http://www.huggill.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.huggill.com</link>
	<description>.net, sharepoint and identity</description>
	<lastBuildDate>Thu, 12 Jan 2012 20:32:24 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>

   <image>
    <title>Sam Huggill&#039;s Weblog</title>
    <url>http://www.huggill.com/favicon_96.png</url>
    <link>http://www.huggill.com</link>
   </image><!-- Multi Social Favicon by Patrick http://patrick.bloggles.info/ -->
		<item>
		<title>Setting up Google Apps Single Sign On (SSO) with ADFS 2.0 and a custom STS such as IdentityServer</title>
		<link>http://www.huggill.com/2012/01/12/setting-up-google-apps-single-sign-on-sso-with-adfs-2-0-and-a-custom-sts-such-as-identityserver/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=setting-up-google-apps-single-sign-on-sso-with-adfs-2-0-and-a-custom-sts-such-as-identityserver</link>
		<comments>http://www.huggill.com/2012/01/12/setting-up-google-apps-single-sign-on-sso-with-adfs-2-0-and-a-custom-sts-such-as-identityserver/#comments</comments>
		<pubDate>Thu, 12 Jan 2012 20:32:24 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[Identity]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://www.huggill.com/2012/01/12/setting-up-google-apps-single-sign-on-sso-with-adfs-2-0-and-a-custom-sts-such-as-identityserver/</guid>
		<description><![CDATA[I recently had to undertake some work to enable users to seamlessly authenticate to Google Apps using an identity stored in a custom Secure Token Service such as the excellent IdentityServer open source STS by Dominick Baier.&#160; The work involved is mostly configuration in Google Apps and ADFS but there are quite a number of &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2012/01/12/setting-up-google-apps-single-sign-on-sso-with-adfs-2-0-and-a-custom-sts-such-as-identityserver/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>I recently had to undertake some work to enable users to seamlessly authenticate to Google Apps using an identity stored in a custom Secure Token Service such as the excellent <a href="http://identityserver.codeplex.com/">IdentityServer</a> open source STS by <a href="http://www.leastprivilege.com/">Dominick Baier</a>.&#160; The work involved is mostly configuration in Google Apps and ADFS but there are quite a number of steps and as it was non-trivial I thought I’d document it here for reference.&#160; Note that Google Apps uses SAML 2.0 tokens and because ADFS is brokering the authentication, you shouldn’t have any problems with compatibility as ADFS 2.0 can issue SAML 2.0 tokens.</p>
<p>Here’s a quick architecture diagram:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/google-sso-adfs.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="google-sso-adfs" border="0" alt="google-sso-adfs" src="http://www.huggill.com/wp-content/uploads/2012/01/google-sso-adfs_thumb.png" width="885" height="393" /></a></p>
<p><strong>Key:</strong></p>
<p>Green arrows = user request flow</p>
<p>Blue arrows = service response flow</p>
<h1>Overview</h1>
<p>For those of you impatient, here’s a quick overview of the steps required:</p>
<ol>
<li>Enable SSO in Google Apps</li>
<li>Enter correct ADFS urls into Google Apps</li>
<li>Upload ADFS Token Signing Certificate so Google Apps can verify the SAML tokens</li>
<li>Add Google Apps as a Relying Party in ADFS</li>
<li>Test</li>
</ol>
<p>I will now walk through each stage in detail, for those who like the details.</p>
<h1>Enable SSO in Google Apps</h1>
<p>The first stage is to enable Single Sign-on in Google Apps.&#160; Log in to your administration console at <a href="http://www.google.com/a/&lt;your-domain&gt;/">/&quot;&gt;http://www.google.com/a/&lt;your-domain&gt;/</a>.&#160; Click on Advanced Tools and in the Authentication section click on Set up single sign-on (SSO):</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step01.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step01" border="0" alt="step01" src="http://www.huggill.com/wp-content/uploads/2012/01/step01_thumb.png" width="516" height="274" /></a></p>
<p>This will take you through to a configuration screen.&#160; Make sure the checkbox next to Enable Single Sign-on is ticked, and then enter the following values:</p>
<p>Sign-in page URL: <a href="https://adfs.yourdomain.com/adfs/ls/">https://adfs.yourdomain.com/adfs/ls/</a></p>
<p>Sign-out page URL: <a href="https://adfs.yourdomain.com/adfs/ls/">https://adfs.yourdomain.com/adfs/ls/</a></p>
<p>Change password URL: <a href="https://sts.yourdomain.com/startersts/users/password.aspx">https://sts.yourdomain.com/startersts/users/password.aspx</a></p>
<p>Verification certificate: Upload the ADFS Token Signing cert (.cer file) which you can obtain from the AD FS 2.0 Management Console (under Service &gt; Certificates).&#160; Remember to click Upload.</p>
<p>Check the box next to “Use a domain specific issuer”.</p>
<p>Enter some network addresses into the Network masks box if you wish.</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step02.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step02" border="0" alt="step02" src="http://www.huggill.com/wp-content/uploads/2012/01/step02_thumb.png" width="401" height="609" /></a></p>
<p>At this point Single sign-on is configured and enabled.&#160; Note that this will take immediate effect on your access to Google Apps services so beware!&#160; However it does not affect your login to the admin console – that is always accessed via a manual login, so you can get in and turn it off again.</p>
<h1>Configure ADFS</h1>
<p>Open up the AD FS 2.0 Management Console and navigate to the Relying Parties section.&#160; Click Add Relying Party Trust and follow these steps:</p>
<p>Choose Enter data about the relying party manually</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step03.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step03" border="0" alt="step03" src="http://www.huggill.com/wp-content/uploads/2012/01/step03_thumb.png" width="527" height="429" /></a></p>
<p>Provide a name for the trust (not important, only so you can easily identify it)</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step04.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step04" border="0" alt="step04" src="http://www.huggill.com/wp-content/uploads/2012/01/step04_thumb.png" width="526" height="428" /></a></p>
<p>Choose AD FS 2.0 profile</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step05.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step05" border="0" alt="step05" src="http://www.huggill.com/wp-content/uploads/2012/01/step05_thumb.png" width="516" height="418" /></a></p>
<p>Tick Enable support for the SAML 2.0 WebSSO protocol and enter <a href="https://www.google.com/a/&lt;your-domain&gt;/acs">/acs&quot;&gt;https://www.google.com/a/&lt;your-domain&gt;/acs</a> into the Relying party SAML 2.0 SSO service URL</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step06.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step06" border="0" alt="step06" src="http://www.huggill.com/wp-content/uploads/2012/01/step06_thumb.png" width="507" height="411" /></a></p>
<p>Enter google.com/a/&lt;your-domain&gt; as the relying party identifier</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step07.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step07" border="0" alt="step07" src="http://www.huggill.com/wp-content/uploads/2012/01/step07_thumb.png" width="505" height="409" /></a></p>
<p>Complete the wizard.</p>
<p>Then click on the newly added item and click Properties.&#160; Click on the Signature tab and click add:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step08.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step08" border="0" alt="step08" src="http://www.huggill.com/wp-content/uploads/2012/01/step08_thumb.png" width="342" height="388" /></a></p>
<p>Here we add the Token Signing Certificate – it must be the same one that we uploaded in the Google admin console, and this should be the ADFS Token Signing Certificate.</p>
<p>Once you’ve done that click OK to close the Properties dialog.</p>
<p>Now click Edit Claim Rules and click Add Rule:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step09.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step09" border="0" alt="step09" src="http://www.huggill.com/wp-content/uploads/2012/01/step09_thumb.png" width="348" height="376" /></a></p>
<p>Select Transform an Incoming Claim from the Claim rule template drop-down:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step10.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step10" border="0" alt="step10" src="http://www.huggill.com/wp-content/uploads/2012/01/step10_thumb.png" width="413" height="335" /></a></p>
<p>Give the rule a name, select E-Mail Address as the Incoming Claim Type, set the Outgoing claim type to Name ID and the Outgoing name ID format to Email:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2012/01/step11.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="step11" border="0" alt="step11" src="http://www.huggill.com/wp-content/uploads/2012/01/step11_thumb.png" width="427" height="346" /></a></p>
<p>Finish the wizard.</p>
<h1>Test</h1>
<p>I’ve assumed here that you’ve already got your custom STS configured as a Claims Provider in ADFS.&#160; To test the end-to-end service, visit http://mail.google.com/a/&lt;your-domain&gt;.&#160; You should get redirected to ADFS.&#160; Choose your STS and then enter your credentials.&#160; You should then be redirected back to Google Apps and arrive at your mailbox, logged in.</p>
<p>If you hit problems, check these items:</p>
<p>- You’ve got the correct certificate uploaded to Google Apps and configured in ADFS</p>
<p>- The time on the ADFS server and custom STS servers is correct</p>
<p>- Google Apps SSO configuration is correct</p>
<p>- If all else fails, try Googling!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2012/01/12/setting-up-google-apps-single-sign-on-sso-with-adfs-2-0-and-a-custom-sts-such-as-identityserver/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to run StarterSTS on IIS 6 / Windows 2003</title>
		<link>http://www.huggill.com/2011/11/23/how-to-run-startersts-on-iis-6-windows-2003/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=how-to-run-startersts-on-iis-6-windows-2003</link>
		<comments>http://www.huggill.com/2011/11/23/how-to-run-startersts-on-iis-6-windows-2003/#comments</comments>
		<pubDate>Wed, 23 Nov 2011 13:43:40 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[Identity]]></category>

		<guid isPermaLink="false">http://www.huggill.com/?p=259</guid>
		<description><![CDATA[I’ve been using the awesome StarterSTS project created by Dominick Baier.  In the words of Dominick: StarterSTS is a compact, easy to use security token service that is completely based on the ASP.NET provider infrastructure. It is built using the Windows Identity Foundation and supports WS-Federation., WS-Trust, REST, OpenId and Information Cards. The StarterSTS System &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/11/23/how-to-run-startersts-on-iis-6-windows-2003/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>I’ve been using the awesome <a href="http://startersts.codeplex.com/">StarterSTS</a> project created by <a href="http://www.leastprivilege.com/default.aspx">Dominick Baier</a>.  In the words of Dominick:</p>
<blockquote><p><em>StarterSTS is a compact, easy to use security token service that is completely based on the ASP.NET provider infrastructure. It is built using the Windows Identity Foundation and supports WS-Federation., WS-Trust, REST, OpenId and Information Cards.</em></p></blockquote>
<p>The StarterSTS System Requirements specify IIS 7.x which implies a Windows 2008 variant operating system.  I recently had a requirement to get it running on a Windows 2003 server (with IIS 6), and it isn’t all that straightforward so I thought I’d post the steps (and as much reasoning as possible here).</p>
<h2>Prerequisites</h2>
<p>You’ll need the following things to hand to get going:</p>
<ul>
<li><a href="http://startersts.codeplex.com/releases">StarterSTS download</a> (I’ve tested with StarterSTS 1.2)</li>
<li>A certificate for IIS (can be <a href="http://www.iis-aid.com/articles/how_to_guides/creating_self_signed_certificates_iis">self-signed</a>)</li>
<li><a href="http://www.microsoft.com/download/en/details.aspx?displaylang=en&amp;id=23190">Windows Identity Foundation for Windows 2003</a></li>
<li><a href="http://www.microsoft.com/download/en/details.aspx?displaylang=en&amp;id=19801">WinHTTPCerfCfg tool</a></li>
</ul>
<h2>Installation</h2>
<p>Follow the StarterSTS instructions to extract the web package and create the site in IIS.  Make sure you install the SSL certificate on the IIS web site, and follow the subsequent instructions to set up the relevant StarterSTS configuration files.</p>
<p>If you haven’t installed the WIF package you’ll get an error similar to this:</p>
<blockquote><p><em>Could not load file or assembly Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 or one of its dependencies. The system cannot find the file specified.</em></p></blockquote>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/sshot-11.gif"><img class="alignnone size-large wp-image-260" title="Microsoft Identity Model is Missing" src="http://www.huggill.com/wp-content/uploads/2011/11/sshot-11-1024x431.gif" alt="" width="590" height="248" /></a></p>
<p>Run the WIF install package and that error will go away.</p>
<p>You should then be able to bring up the StarterSTS homepage:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/sshot-7.gif"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="sshot-7" src="http://www.huggill.com/wp-content/uploads/2011/11/sshot-7_thumb.gif" alt="sshot-7" width="482" height="245" border="0" /></a></p>
<p>Go ahead and create yourself a user account by visiting /signup.aspx (remember to create the database as in the StarterSTS instructions):</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/sshot-8.gif"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="sshot-8" src="http://www.huggill.com/wp-content/uploads/2011/11/sshot-8_thumb.gif" alt="sshot-8" width="384" height="336" border="0" /></a></p>
<h2>Token Issuance</h2>
<p>This is where the configuration gets a bit more involved.</p>
<p><strong><span style="color: #ff0000;">Note</span></strong>: The auto-generation of federation meta-data doesn’t work on IIS 6 (normally you would hit /FederationMetadata/2007-06/FederationMetadata.xml).</p>
<p>Try visiting the following URL:</p>
<p><a href="https://sts.gws.lab.local/startersts/users/issue.aspx?wa=wsignin1.0&amp;wtrealm=http://adfs.leastprivilege.vm/adfs/services/trust">https://sts.gws.lab.local/startersts/users/issue.aspx?wa=wsignin1.0&amp;wtrealm=http://adfs.leastprivilege.vm/adfs/services/trust</a></p>
<p>(Remember to replace the first part with the address of your server.  The value of wtrealm depends on what you’ve put in the configuration/relyingParties.config file in StarterSTS.  In this example I’m just using the sample entry that comes with the source download.)</p>
<p>The first error you’ll see is:</p>
<blockquote><p><em>The handle is invalid. System.Security.Cryptography.CryptographicException: The handle is invalid.</em></p></blockquote>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/sshot-21.gif"><img class="alignnone size-large wp-image-261" title="Handle is invalid" src="http://www.huggill.com/wp-content/uploads/2011/11/sshot-21-1024x429.gif" alt="" width="590" height="247" /></a></p>
<p>This error comes about because the identity of the IIS Process executing the StarterSTS code cannot access the private key of your signing certificate (as specified in configuration/certificates.config).  In Windows 2008, this is easily fixed using the Certificates MMC Snap-In, but not so in Windows 2003 (the option isn’t available).  This is where the WinHTTPCertCfg tool comes in handy.  It allows us to check and set the user identities with access to certificates installed on the machine [read <a href="http://www.stevefenton.co.uk/Content/Blog/Date/201101/Blog/X509-Certificates-On-Windows-Server-2003/">X509 Certificates on Windows Server 2003</a> for more info].</p>
<p>If you followed the default install settings, then the tool will have installed to c:\Program Files\Windows Resource Kits\tools\.  Open a Command Prompt, switch to this directory and run the following command to see which user identities have access to the certificate:</p>
<pre class="brush: plain; title: ; notranslate">
winhttpcertcfg.exe -l -c LOCAL_MACHINE\My -s &quot;sts.gws.lab.local&quot;
</pre>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/sshot-3.gif"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="sshot-3" src="http://www.huggill.com/wp-content/uploads/2011/11/sshot-3_thumb.gif" alt="sshot-3" width="753" height="173" border="0" /></a></p>
<p><strong>Note</strong>: Replace the &#8220;sts.gws.lab.local&#8221; with the Common Name on your certificate.</p>
<p>Now you need to check which user is running IIS.  This is done by looking at the properties on the Application Pool assigned to the StarterSTS virtual directory:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/Untitled.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="Untitled" src="http://www.huggill.com/wp-content/uploads/2011/11/Untitled_thumb.png" alt="Untitled" width="361" height="340" border="0" /></a><a href="http://www.huggill.com/wp-content/uploads/2011/11/Untitled2.png"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="Untitled2" src="http://www.huggill.com/wp-content/uploads/2011/11/Untitled2_thumb.png" alt="Untitled2" width="357" height="341" border="0" /></a></p>
<p>My server is running with the out-of-the-box account Network Service.  Now that we know this, we can go back to the Command Prompt and grant this account permissions over the private key:</p>
<pre class="brush: plain; title: ; notranslate">
winhttpcertcfg.exe -g -c LOCAL_MACHINE\My -s &quot;sts.gws.lab.local&quot; -a &quot;NetworkService&quot;
</pre>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/sshot-4.gif"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="sshot-4" src="http://www.huggill.com/wp-content/uploads/2011/11/sshot-4_thumb.gif" alt="sshot-4" width="741" height="151" border="0" /></a></p>
<p>Restart IIS and go back to the the issue.aspx URL and you’ll likely get this error now:</p>
<blockquote><p><em>Object identified (OID) is unknown. System.Security.Cryptography.CryptographicException: Object identifier (OID) is unknown.</em></p></blockquote>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/sshot-5.gif"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="sshot-5" src="http://www.huggill.com/wp-content/uploads/2011/11/sshot-5_thumb.gif" alt="sshot-5" width="754" height="304" border="0" /></a></p>
<p>This comes about due to the need to configure RSA-SHA256 on Windows 2003.  For more details on this see this <a href="http://social.msdn.microsoft.com/Forums/en-US/Geneva/thread/35c10fe5-9693-4f3a-9c5c-8afbb423ee95/">MSDN Forum Post – CryptographicException – Object identifier (OID) is unknown</a>.  Follow the steps in the forum answer:</p>
<ul>
<li>Download Security.Cryptography.dll from <a href="http://clrsecurity.codeplex.com/releases/view/28365">http://clrsecurity.codeplex.com/releases/view/28365</a></li>
<li>Create a new .NET 3.5 Console Application</li>
<li>Add a reference to the Security.Cryptography.dll</li>
<li>Add the following code:</li>
</ul>
<pre class="brush: csharp; title: ; notranslate">
using Security.Cryptography;

class Program
{
    static void Main(string[] args)
    {
        Oid2.RegisterSha2OidInformationForRsa();
    }
}
</pre>
<ul>
<li>Build and run the console application (alongside the DLL) on the server</li>
</ul>
<p>Alternatively, save yourself the hassle and <a href="http://www.huggill.com/wp-content/uploads/2011/11/RegOid.zip">download the ZIP that I made earlier</a>.</p>
<p>After that, everything should fly, and StarterSTS should issue a token and redirect you back to the relying party specified in the wtrealm parameter.  I grabbed a couple of screenshots using Fiddler to show it working:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/sshot-6.gif"><img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="sshot-6" src="http://www.huggill.com/wp-content/uploads/2011/11/sshot-6_thumb.gif" alt="sshot-6" width="846" height="580" border="0" /></a></p>
<p>And that’s it!  I hope this has been helpful, it was very rewarding when I got this working and I have repeated it several times on different boxes.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/11/23/how-to-run-startersts-on-iis-6-windows-2003/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Moving away from WordPress.com and 10 must-have WordPress plugins</title>
		<link>http://www.huggill.com/2011/11/17/moving-away-from-wordpress-and-10-must-have-wordpress-plugins/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=moving-away-from-wordpress-and-10-must-have-wordpress-plugins</link>
		<comments>http://www.huggill.com/2011/11/17/moving-away-from-wordpress-and-10-must-have-wordpress-plugins/#comments</comments>
		<pubDate>Thu, 17 Nov 2011 12:28:39 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">http://www.huggill.com/?p=229</guid>
		<description><![CDATA[Last night, with a couple of hours to spare, I decided to get on and move my blog away from WordPress.com to my own hosting.  Why do this? Access to my own Google Analytics data Use of any plugin I like Association my content with my domain name Greater control over the site, to, for &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/11/17/moving-away-from-wordpress-and-10-must-have-wordpress-plugins/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.huggill.com/wp-content/uploads/2011/11/Moving-Day-Pic-for-Blog-Post-2a0i04v.jpg"><img style="float: right; display: inline; background-image: none;" title="Moving-Day-Pic-for-Blog-Post-2a0i04v" src="http://www.huggill.com/wp-content/uploads/2011/11/Moving-Day-Pic-for-Blog-Post-2a0i04v_thumb.jpg" alt="Moving-Day-Pic-for-Blog-Post-2a0i04v" width="244" height="174" align="right" border="0" /></a></p>
<p>Last night, with a couple of hours to spare, I decided to get on and move my blog away from WordPress.com to my own hosting.  Why do this?</p>
<ul>
<li>Access to my own Google Analytics data</li>
<li>Use of any plugin I like</li>
<li>Association my content with my domain name</li>
<li>Greater control over the site, to, for example, add <a href="http://www.hanselman.com/blog/EmbraceAuthorshipTheImportanceOfRelmeAndRelauthorOnYourContentsSEOAndGoogle.aspx">Google Authorship</a> links</li>
</ul>
<p>I&#8217;ve been running the blog on WordPress.com since <a href="http://www.huggill.com/2007/12/06/hello-world-2/">6th December 2007</a>, but time has come to move to new pastures.   The move process was very simple:</p>
<ol>
<li>Set up WordPress on my Linux server (5 minutes)</li>
<li>Export existing blog content using WordPress XML export</li>
<li>Import XML into new blog</li>
<li>Pay $12 / year to 301 all pages / posts from the old blog to the new domain</li>
</ol>
<div><span style="font-size: x-small;">This was I won&#8217;t lose any of my Google ranking weight, and users won&#8217;t get the dreaded 404 on that key post they were looking for.</span></div>
<div>To get my new blog up to scratch I did some research and loaded up the following 10 awesome WordPress plugins:</div>
<div>
<ol>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://akismet.com/">Akismet</a> &#8211; the <em>de facto</em> comment and trackback spam protection</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://www.mkyong.com/blog/digg-digg-wordpress-plugin">Digg Digg</a> &#8211; add as many social media buttons as you can shake a stick at</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://yoast.com/wordpress/google-analytics/#utm_source=wordpress&amp;utm_medium=plugin&amp;utm_campaign=google-analytics-for-wordpress&amp;utm_content=v420">Google Analytics for WordPress</a> &#8211; all in the name</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://urbangiraffe.com/plugins/headspace2/">Head Space2</a> &#8211; manage SEO meta-data to a fine grained level</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://patrick.bloggles.info/plugins/">Multi Social Favicon</a> &#8211; auto-generate the blog favicon using a social media account</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://www.imallen.com/blog/works/smart-video-plugin-for-wordpress">Smart Video</a> &#8211; simple YouTube, Vimeo video embeds</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://www.viper007bond.com/wordpress-plugins/syntaxhighlighter/">SyntaxHighlighter Evolved</a> &#8211; awesome syntax highlighting for code in posts</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://www.ajaymatharu.com/wordpress-plugin-tweet-old-posts/">Tweet old post</a> &#8211; keep old posts alive by tweeting them every now and then</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://www.w3-edge.com/wordpress-plugins/w3-total-cache/">W3 Total Cache</a> &#8211; speed up your blog with caching</span></li>
<li><span class="Apple-style-span" style="font-size: 12px;"><a href="http://lesterchan.net/portfolio/programming/php/">WP-DBManager</a> &#8211; database management, backups &#8211; essential</span></li>
</ol>
<div>WordPress is an awesome blogging platform; it moves out of the way so you can focus on writing content.  And with the large plugin and theme offerings out there, there&#8217;s no excuse to share your knowledge with the wider community.</div>
<div>Get on and write something!</div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/11/17/moving-away-from-wordpress-and-10-must-have-wordpress-plugins/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Clearing up the confusion over session timeouts in PHP and Zend Framework</title>
		<link>http://www.huggill.com/2011/08/26/clearing-up-the-confusion-over-session-timeouts-in-php-and-zend-framework/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=clearing-up-the-confusion-over-session-timeouts-in-php-and-zend-framework</link>
		<comments>http://www.huggill.com/2011/08/26/clearing-up-the-confusion-over-session-timeouts-in-php-and-zend-framework/#comments</comments>
		<pubDate>Fri, 26 Aug 2011 08:59:34 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">https://shuggill.wordpress.com/?p=181</guid>
		<description><![CDATA[I’ve recently made a foray into the world of Zend Framework.  If you’ve not come across it, it is one of several popular PHP frameworks that implements the Model View Controller software architecture. By day I’m mostly devoted to hacking ASP.NET MVC, but having had some experience with PHP I decided to get my teeth &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/08/26/clearing-up-the-confusion-over-session-timeouts-in-php-and-zend-framework/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p><img style="display: inline; float: right; margin: 0 5px;" src="http://www.serversidemagazine.com/images/bootstrap-php-code/bootstrap.gif" alt="" width="346" height="225" align="right" />I’ve recently made a foray into the world of <a href="http://framework.zend.com/">Zend Framework</a>.  If you’ve not come across it, it is one of several popular PHP frameworks that implements the <a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">Model View Controller</a> software architecture.</p>
<p>By day I’m mostly devoted to hacking <a href="http://www.asp.net/mvc">ASP.NET MVC</a>, but having had some experience with PHP I decided to get my teeth stuck into Zend for a new PHP project I’ve been working on.  Somewhere in my head I have another blog post to write about the dyer state that is documentation for Zend, but that’s a topic for another night.</p>
<p>This post details my journey through figuring out how PHP sessions really work and how to fix them for properly bringing down a user’s session after a period of inactivity, in the context of Zend Framework (although most of the post applies to PHP proper).</p>
<h2>Get your server’s clock synchronised</h2>
<p>The PHP session files are date-stamped when they are created/updated.  The PHP Session cookie that gets sent to the browser has an expiry date-stamp on it.  While you can’t rely on the client’s system clock to be correct, making sure your server’s is makes debugging less of a headache.</p>
<p>On a Windows server this should already be set up – if not check the Date &amp; Time Properties in Control Panel and pay attention to the Internet Time tab.  On a Linux server it’s a case of installing and configuring the <a href="http://www.ntp.org/">ntp</a> package – a bit of Googling should show you the way with whichever distribution you are using.</p>
<h2>The options for session timeouts</h2>
<p>There are a number of options as to how you can handle session timeouts, as I see it:</p>
<ol>
<li>Keep the session alive until the user closes their browser</li>
<li>End the session after a fixed period of time (regardless of activity)</li>
<li>End the session after a fixed period of time with no activity</li>
<li>Some combination of the above</li>
</ol>
<p>I’d put my money on the best user experience being a combination of point 1) and point 3).  This, rather surprisingly, isn’t as straightforward as you would hope.</p>
<h2>PHP Session Settings – What they mean</h2>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/08/image.png"><img style="background-image: none; padding-left: 0; padding-right: 0; display: inline; float: right; padding-top: 0; border: 0; margin: 0 5px;" title="image" src="/wp-content/uploads/2011/08/image_thumb.png" alt="image" width="311" height="248" align="right" border="0" /></a></p>
<p>There are a handful of PHP settings relating to sessions, and understanding how sessions work and what these settings do is critical to getting the behaviour you desire.</p>
<p>PHP sessions are stored as files (by default) – the files contain a serialised version of the $_SESSION superglobal arrray.  The path to these files is set by <a href="http://uk2.php.net/manual/en/session.configuration.php#ini.session.save-path">session.save_path</a>.</p>
<p><strong><span style="color: #ff0000;">N.B. Security Warning</span></strong> – make sure no other users can get to the session directory.  Files are stored unencrypted and therefore should be treated with the same caution as password files.</p>
<p><span style="color: #ff0000;"><strong>An interesting side note:</strong></span> if you inspect the cookie PHPSESSID (the default name for the PHP Session Cookie), you&#8217;ll notice a funny looking value.  Now take a look at the contents of the PHP session directory &#8211; hey presto, there&#8217;s a file called sess_&lt;SESSIONID&gt;. Simple eh.</p>
<p>Each time PHP session_start() is called (which is not just when you first start a session, but every time you want to load session data into your app, i.e. on every request for most apps), PHP works out whether or not it should run the session garbage collector.</p>
<p><strong>Garbage – what garbage?</strong></p>
<p>The session garbage collector runs around and checks all of the timestamps on the session files and compares them to the current time less the <a href="http://uk2.php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime">session.gc_maxlifetime</a> value, to work out if a file should be deleted (because it has exceeded it’s lifetime).</p>
<p>You might think that the garbage collector would run on every session_start(), to avoid any expired session files being left hanging around.  However there is a performance overhead with this and so PHP uses two settings, <a href="http://uk2.php.net/manual/en/session.configuration.php#ini.session.gc-probability">session.gc_probability</a> and <a href="http://uk2.php.net/manual/en/session.configuration.php#ini.session.gc-divisor">session.gc_divisor</a> to calculate the probability that it should run the garbage collector (it does this by dividing the two and then rolling it’s own dice to see whether it should run).</p>
<p>N.B. Remember that each time you access the session the timestamp on the session file gets updated, this stops the garbage collector from killing a user’s session while they are still active.</p>
<p>This all sounds very cosy and like it will do just what I want – dump the user’s session after a period of inactivity (as specified by <a href="http://uk2.php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime">session.gc_maxlifetime</a>).  And on low volume sites you could force the garbage collector to run every time by making the probability 100%.</p>
<p>However, all is not quite so straightforward!</p>
<h2>The order of things</h2>
<p><span style="color: #ff0000;"><strong>I’m sure there’s a good reason for this, but I’m not aware of what it is</strong></span>.  PHP <em>first</em> loads up the session, <em>then</em> works out if it should run the garbage collector.  So even if the garbage collector runs every time and the session file has expired, on the first request the user will appear to still have a session, and then only on the next request they won’t.</p>
<p>Imagine this scenario.  A user has logged in to your site.  They go away for lunch, and during that time their session has expired.  They come back and hit refresh – the screen reloads with their session apparently intact.  All is looking good.  Then they click somewhere else and bam – their session is gone.  Kinda confusing really.</p>
<p>In an ideal world there would be a way to check for dead sessions <em>before</em> the session fires up, and in fact there is, but you have to roll it yourself.</p>
<h2>Did I mention Zend Framework?</h2>
<p>So far everything I have covered is PHP proper, no mention of Zend.  However the Zend_Session object is based on all of these settings, and therefore it warrants understanding the underlying PHP behaviour first.</p>
<p>The solution to this problem is to roll your own inactivity timeout check.  I needed this to implement inactivity logouts.  I used the following code to do my tracking:</p>
<pre class="brush: php; title: ; notranslate">
$idleTimeout = 3600; // timeout after 1 hour&lt;/span&gt;

if(isset($_SESSION['timeout_idle']) &amp;amp;&amp;amp; $_SESSION['timeout_idle'] &amp;lt; time()) {
Zend_Session::destroy();
Zend_Session::regenerateId();
header('Location: /account/signin');
exit();
}

$_SESSION['timeout_idle'] = time() + $idleTimeout;
</pre>
<p><strong>N.B. If you’re not using Zend Framework you can exchange the Zend_Session lines for their standard PHP equivalents.</strong></p>
<p>All this does is check if we have an idle timeout set in our session (remember this gets loaded even if the session has technically expired) and if it has fallen past the current time we tear down the session, regenerate the session ID (to avoid picking up the existing session file) and head off to the log in screen.</p>
<p>Specifically to Zend, you can set the following settings in the application.ini file:</p>
<pre class="brush: php; title: ; notranslate">
resources.session.gc_probability = 1
resources.session.gc_divisor = 1
resources.session.gc_maxlifetime = 3600
resources.session.idle_timeout = 3600
</pre>
<p>And then place the custom idle time out code inside this function in Bootstrap.php:</p>
<pre class="brush: php; title: ; notranslate">
protected function _initSession()
{
# set up the session as per the config.
$options = $this-&amp;gt;getOptions();
$sessionOptions = array(
'gc_probability'    =&amp;gt;    $options['resources']['session']['gc_probability'],
'gc_divisor'        =&amp;gt;    $options['resources']['session']['gc_divisor'],
'gc_maxlifetime'    =&amp;gt;    $options['resources']['session']['gc_maxlifetime']
);

$idleTimeout = $options['resources']['session']['idle_timeout'];

Zend_Session::setOptions($sessionOptions);
Zend_Session::start();

# now check for idle timeout.
if(isset($_SESSION['timeout_idle']) &amp;amp;&amp;amp; $_SESSION['timeout_idle'] &amp;lt; time()) {
Zend_Session::destroy();
Zend_Session::regenerateId();
header('Location: /account/signin');
exit();
}

$_SESSION['timeout_idle'] = time() + $idleTimeout;
}
</pre>
<p>And that’s all there is to it!</p>
<p>If you know the reasoning behind some of these apparently strange PHP session operations then please post them here – I’m left scratching my head in a daze at how complex a matter it has been to implement such a standard bit of user experience.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/08/26/clearing-up-the-confusion-over-session-timeouts-in-php-and-zend-framework/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Using O2 ZTE MF100 Mobile Broadband on Mac OS X Lion</title>
		<link>http://www.huggill.com/2011/07/27/using-o2-zte-mf100-mobile-broadband-on-mac-os-x-lion/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=using-o2-zte-mf100-mobile-broadband-on-mac-os-x-lion</link>
		<comments>http://www.huggill.com/2011/07/27/using-o2-zte-mf100-mobile-broadband-on-mac-os-x-lion/#comments</comments>
		<pubDate>Wed, 27 Jul 2011 11:24:15 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[Mac OS]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://shuggill.wordpress.com/?p=175</guid>
		<description><![CDATA[A few days ago I took the leap and upgraded my Macbook Air to OS X Lion.  After a seamless (and typically Apple) upgrade process, I was enjoying the benefits of an even more refined operating system. However, one of the first things I did was test out my mobile broadband &#8211; and there the &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/07/27/using-o2-zte-mf100-mobile-broadband-on-mac-os-x-lion/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>A few days ago I took the leap and upgraded my Macbook Air to OS X Lion.  After a seamless (and typically Apple) upgrade process, I was enjoying the benefits of an even more refined operating system.<a href="http://www.huggill.com/wp-content/uploads/2011/08/6033_1293438333.jpeg"><img class="alignright size-thumbnail wp-image-183" style="border:0 none;" title="6033_1293438333" src="/wp-content/uploads/2011/08/6033_1293438333.jpeg?w=83" alt="" width="83" height="149" /></a></p>
<p>However, one of the first things I did was test out my mobile broadband &#8211; and there the problem began.</p>
<p>I have an O2 mobile broadband dongle, the ZTE MF100 USB stick. When I originally installed it a small application called O2 Mobile Connect installed and worked a treat for connecting to the O2 service.</p>
<p>After upgrading to OS X Lion the application crashed as soon as it opened &#8211; I assume because of a change in an API somewhere. After hunting around the O2 site (and Google) I could find no update on getting the Mobile Connect application to work on Lion, or even where to download it.  It would appear that O2 have ditched supplying the ZTE dongles in favour of Huwaei branded sticks.</p>
<p>Anyway, I was damned if I was going to lose my mobile broadband (and I certainly didn&#8217;t want to uninstall Lion), so here&#8217;s how I eventually got the stick working:</p>
<ol>
<li>Open System Preferences</li>
<li>Click on the plus sign to Create a new service</li>
<li>Choose ZTEUSBModem as the interface</li>
<li>Name the service O2 Mobile Broadband (or something recognisable)</li>
<li>Set Telephone Number to *99#</li>
<li>Set Account name to 02web</li>
<li>Set Password to password</li>
</ol>
<p><strong>Note</strong> that this is for Pay Monthly Broadband &#8211; I think the details are different for Pay &amp; Go customers.</p>
<p><span style="color:#ff0000;"><strong>Key step: set DNS server to 193.113.200.201</strong></span></p>
<p>And hey presto, I now have mobile broadband working again.</p>
<p>Hope this helps anyone out there trying to get it working.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/07/27/using-o2-zte-mf100-mobile-broadband-on-mac-os-x-lion/feed/</wfw:commentRss>
		<slash:comments>22</slash:comments>
		</item>
		<item>
		<title>The Novice&#8217;s Guide to Replacing a Bathroom &#8211; Part 2</title>
		<link>http://www.huggill.com/2011/07/02/the-novices-guide-to-replacing-a-bathroom-part-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=the-novices-guide-to-replacing-a-bathroom-part-2</link>
		<comments>http://www.huggill.com/2011/07/02/the-novices-guide-to-replacing-a-bathroom-part-2/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 18:57:28 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">http://shuggill.wordpress.com/?p=161</guid>
		<description><![CDATA[In late April (this year) I started out as a novice replacing our main bathroom. Last night I finished the bathroom project, and although still a novice, I&#8217;ve certainly learned a thing or two. The Motivation It&#8217;s taken me two months, and a lots of hours of work, late at night and during weekends. I &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/07/02/the-novices-guide-to-replacing-a-bathroom-part-2/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p>In late April (this year) <a href="http://www.huggill.com/2011/04/25/the-novices-guide-to-replacing-a-bathroompart-1/">I started out as a novice replacing our main bathroom</a>. Last night I finished the bathroom project, and although still a novice, I&#8217;ve certainly learned a thing or two.</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/07/img_3469_small.jpg"><img class="size-medium wp-image-162 alignright" style="margin-left:10px;margin-right:10px;border:0 none;" title="The new bathroom" src="/wp-content/uploads/2011/07/img_3469_small.jpg?w=300" alt="" width="300" height="200" /></a></p>
<p><strong>The Motivation</strong></p>
<p>It&#8217;s taken me two months, and a lots of hours of work, late at night and during weekends. I could have paid someone to do this, but I wanted the satisfaction of understanding how it works, and it&#8217;s been a nice distraction from work.</p>
<p><strong>The Final Outcome</strong></p>
<p>The bathroom now looks nice and clean, spacious and modern, while retaining some of the history of the house (I&#8217;ve left the old radiator in, the door and window frames are the same).  We have a new toilet, sink and taps, bath and taps, new floor, new tiles, new light, new mirror and rails.  We&#8217;ve kept the old shower (which is actually quite new).</p>
<p>It should be good for many years (and baths) to come!</p>
<div id="attachment_166" class="wp-caption alignnone" style="width: 122px"><a href="http://www.huggill.com/wp-content/uploads/2011/07/photo11.jpg"><img class="size-thumbnail wp-image-166 " title="Laying the new floor" src="/wp-content/uploads/2011/07/photo11.jpg?w=112" alt="" width="112" height="150" /></a><p class="wp-caption-text">Laying the new floor</p></div>
<p><strong>What I&#8217;ve Enjoyed</strong></p>
<p>The most rewarding moments have been grouting the tiles (turns an ugly looking wall into a very nice finish), fitting the skirting board and connecting up the taps.  The one job I paid for was fitting the tiles &#8211; this took an experienced handyman a whole day just to cut and fix the tiles &#8211; I did most of the grouting.  This would have taken me at least twice as long to do.</p>
<div id="attachment_167" class="wp-caption alignnone" style="width: 122px"><a href="http://www.huggill.com/wp-content/uploads/2011/07/photo2.jpg"><img class="size-thumbnail wp-image-167" title="Fitting the Bath" src="/wp-content/uploads/2011/07/photo2.jpg?w=112" alt="" width="112" height="150" /></a><p class="wp-caption-text">In goes the bath</p></div>
<p><strong>What I&#8217;ve Learned</strong></p>
<p>Ripping out the bathroom initially took a lot longer and was a lot more difficult than I had anticipated.  This may have been in large part to problems with leaking pipe stops and not having the right tool to undo the bath taps.</p>
<p>Almost every plumbing joint I made leaked the first time around. Compression fittings like to be nice and tight, then they work fine without any PTFE tape or sealant. However, plastic pipe fittings (in particular the water inlet to the toilet cistern and the bath waste) took several attempts to get them sealed. I naively assumed that just fitting the supplied rubber washers would do the job. In the end I resorted to large amounts of silicone sealant &#8211; I&#8217;d pump this in first time from now on.</p>
<object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/PJjKMsCmL-o"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="never"></param><param name="allownetworking" value="internal"></param><param name="flashvars" value="" /><embed src="http://www.youtube.com/v/PJjKMsCmL-o" type="application/x-shockwave-flash" allowscriptaccess="never" allownetworking="internal" allowfullscreen="true" width="425" height="344" flashvars=""></embed></object>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/07/02/the-novices-guide-to-replacing-a-bathroom-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Building a budget VMWare ESXi server</title>
		<link>http://www.huggill.com/2011/06/01/building-a-budget-vmware-esxi-server-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=building-a-budget-vmware-esxi-server-2</link>
		<comments>http://www.huggill.com/2011/06/01/building-a-budget-vmware-esxi-server-2/#comments</comments>
		<pubDate>Wed, 01 Jun 2011 21:35:36 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">https://shuggill.wordpress.com/2011/06/01/building-a-budget-vmware-esxi-server-2/</guid>
		<description><![CDATA[The Background One of the projects that I&#8217;ve been working on recently requires a lot of interaction between different systems, and in order to roll-out changes and carry out integration testing I needed a replica of our production environment.&#160; However the challenge was that it required 7 machines to replicate production. In the past I&#8217;ve &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/06/01/building-a-budget-vmware-esxi-server-2/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p><strong>The Background</strong></p>
<p><img style="display:inline;float:right;margin:0 0 0 5px;" align="right" src="http://media8.connectedsocialmedia.com/08/PID_013725/Podtech_Virtualize_VMware_ESXi.jpg" width="173" height="99" />One of the projects that I&#8217;ve been working on recently requires a lot of interaction between different systems, and in order to roll-out changes and carry out integration testing I needed a replica of our production environment.&#160; However the challenge was that it required 7 machines to replicate production.</p>
<p>In the past I&#8217;ve happily run a few virtual images on my desktop machine, albeit paired back to 500MB RAM each.&#160; But 7, there&#8217;s no way that&#8217;s going to run on my current desktop, even if I had enough RAM.</p>
<p>So I decided it was time to invest in some hardware to give me the flexibility to build larger virtual environments.</p>
<p>This is where <a href="http://blogs.vmware.com/vsphere/2010/07/introducing-vmware-sphere-hypervisor-41-the-free-edition-of-vmware-vsphere-41.html">VMware&#8217;s awesome ESXi</a> product comes in (which I see has now been renamed to vSphere Hypervisor 4.1).</p>
<p><strong>ESXi 101</strong></p>
<p>I won&#8217;t repeat what many excellent blog posts have already said about ESXi, but essentially it&#8217;s a small operating system which can run multiple virtualised machines on top it &#8211; by providing a virtual hardware layer to the virtual machines, it can share the underlying physical system resources out. The sweet thing about this is that you can start and stop virtual machines in a matter of seconds, and very easily duplicate machines. So on one reasonably powered server, you can run a bunch of virtual machines thereby reducing your hardware overheads.</p>
<p>This kind of approach is great both for business line systems and development environments.&#160; I will of course be using it for the latter.</p>
<p><strong>The Hardware</strong></p>
<p><img style="display:inline;float:right;margin:0 0 0 5px;" align="right" src="http://www.officemagic.co.uk/images_2/200x200/OR5900000160821.jpg" />One of my big concerns was getting compatible hardware, and not paying too much for it. After some Googling and talking to fellow geeks I settled on the <a href="http://www.ebuyer.com/product/219937">HP Proliant ML110 G6</a>.&#160; This is an entry level HP server and the base build comes with a 250GB SATA drive and 1GB RAM.</p>
<p>Whilst I couldn&#8217;t do much about the processor, the main two limiting factors were the disk space and RAM.&#160; Most of the virtual machines I want to create are circa 50GB each, so 250GB would run out very quickly.</p>
<p>Therefore I made two upgrades: I bought a <a href="http://www.ebuyer.com/product/182435">Western Digital 1TB &#8216;green&#8217; SATA drive</a> (only 5400 RPM) and 8GB RAM (2x4GB).</p>
<p>I bought the server and the SATA drive from <a href="http://www.ebuyer.com/">eBuyer</a> and the RAM from <a href="http://www.crucial.com/uk/">Crucial</a> &#8211; total spend approximately £400.</p>
<p><strong>Installation</strong></p>
<p>With the new hardware in place I needed one other item &#8211; a 2GB USB stick.&#160; Yes, to make life easier you can install and boot ESXi from a USB stick, rather than take up space on your image storage drives. And the HP machine&#8217;s motherboard came with an on-board USB socket &#8211; so I simply plugged the stick into the motherboard.</p>
<p>I initially installed the special HP version of ESXi &#8211; supposedly it is preferable because it comes with all of the HP drivers.&#160; However after one hour of running I discovered the kernel had performed a memory dump, and after several reoccurrences I ditched it and went back to the vanilla ESXi image and haven&#8217;t had any problems since.</p>
<p>Once booted it&#8217;s best to assign a static IP address to your ESXi box and then that&#8217;s it &#8211; I&#8217;ve stashed mine away in the garage.</p>
<p><strong>Day-to-day use</strong></p>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/06/photo.jpg"><img style="background-image:none;border-bottom:0;border-left:0;padding-left:0;padding-right:0;display:inline;float:right;border-top:0;border-right:0;padding-top:0;" title="My ESXi Server (on the right)" border="0" alt="My ESXi Server (on the right)" align="right" src="/wp-content/uploads/2011/06/photo_thumb.jpg" width="183" height="244" /></a>Once the server is set up you can forget about needing to be near it, as you do everything from the equally awesome (although sometimes annoyingly sluggish) VMWare vSphere Center program. This tool allows you to create new virtual machines, upload ISO images to the datastore (you&#8217;ll need these to install any software, i.e. the operating systems for your virtual machines).</p>
<p>So now when I&#8217;m ready, I fire up my the virtual lab in question and I can happily run all 7 virtual images on 1GB RAM each.</p>
<p>And when I&#8217;m ready, I can max the RAM up to 16GB!</p>
<p>Happy ESXing!</p>
<p>Another useful reference:</p>
<p><a href="www.techhead.co.uk/installing-vmware-esxi-4-0-on-a-usb-memory-stick-the-official-way">Techhead – Installing VMWare ESXi 4.0 on a USB Memory Stick</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/06/01/building-a-budget-vmware-esxi-server-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Synchronising Highrise, Basecamp and Google Shared Contacts–Part 2</title>
		<link>http://www.huggill.com/2011/04/29/synchronising-highrise-basecamp-and-google-shared-contactspart-2/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=synchronising-highrise-basecamp-and-google-shared-contactspart-2</link>
		<comments>http://www.huggill.com/2011/04/29/synchronising-highrise-basecamp-and-google-shared-contactspart-2/#comments</comments>
		<pubDate>Fri, 29 Apr 2011 20:10:57 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">https://shuggill.wordpress.com/?p=147</guid>
		<description><![CDATA[Background: In Part 1 of this series I wrote about a recent project that I worked on to synchronise contact data between a number of web applications, and how I had to work around the (current) shortcomings of their APIs. As Scott Hanselman says, “talk is cheap – show me the code”.  So here we &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/04/29/synchronising-highrise-basecamp-and-google-shared-contactspart-2/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p><img style="display: inline;" src="http://www.free-php.org.uk/images/php%20tutorials.jpg" alt="" width="222" height="150" align="right" /><strong>Background: </strong><a href="http://www.huggill.com/2011/04/01/synchronising-highrise-basecamp-and-google-shared-contactspart-1/">In Part 1 of this series</a> I wrote about a recent project that I worked on to synchronise contact data between a number of web applications, and how I had to work around the (current) shortcomings of their APIs.</p>
<p>As <a href="http://www.hanselman.com/blog/">Scott Hanselman</a> says, “<strong>talk is cheap – show me the code</strong>”.  So here we go…</p>
<h2>Basecamp &amp; Highrise APIs</h2>
<p>Implementing an interface to the <a href="http://developer.37signals.com/">Basecamp &amp; Highrise APIs</a> is relatively straightforward – I did it using <a href="http://php.net/manual/en/book.curl.php">PHP’s curl()</a>.  My implementation doesn’t support all of the API’s functions, but you should be able to easily add any additional calls that you need.</p>
<p>The functions centre around a single function which does the hard work on making the request:</p>
<pre class="brush: php; title: ; notranslate">
private function _getData($uriExtension) {
$base_url = $this-&gt;url.$uriExtension;
$session = curl_init();
curl_setopt($session, CURLOPT_URL, $base_url);
curl_setopt($session, CURLOPT_USERPWD, $this-&gt;apiKey.':X');
curl_setopt($session, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_HTTPGET, 1);
curl_setopt($session, CURLOPT_HTTPHEADER, array('Accept: application/xml', 'Content-Type: application/xml'));
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);
curl_setopt($session, CURLOPT_SSL_VERIFYPEER,false);
curl_setopt($session, CURLOPT_FRESH_CONNECT, true);
$response = curl_exec($session);
curl_close($session);
$xmlObj = new SimpleXMLElement($response);
return ($xmlObj);
}
</pre>
<h2>Google Shared Contacts</h2>
<p>This was simply done using the <a href="http://framework.zend.com/manual/en/zend.gdata.html">Zend GData</a> classes which you can grab here.  Here’s some sample code for updating a shared contact in Google:</p>
<pre class="brush: php; title: ; notranslate">
$client=$this-&gt;googleClientAuth('cp');
$client-&gt;setHeaders('If-Match: *');
$gdata=new Zend_Gdata($client);
$gdata-&gt;setMajorProtocolVersion(3);
// get the existing contact.
$query = new Zend_Gdata_Query($id);
$entry = $gdata-&gt;getEntry($query);
$xml = simplexml_load_string($entry-&gt;getXML());
// update title.
$xnl-&gt;title = $title;
// update the fullname.
$xml-&gt;name-&gt;fullName = $title;
// update entry.
$entryResult = $gdata-&gt;updateEntry($xml-&gt;saveXML(), $entry-&gt;getEditLink()-&gt;href);
</pre>
<p>Get more details over at the <a href="http://code.google.com/googleapps/domain/shared_contacts/gdata_shared_contacts_api_reference.html">Google Apps Shared Contacts API</a> page.</p>
<h2>Basecamp &amp; Highrise – Filling in the API gaps</h2>
<p>So the big problem with the Basecamp &amp; Highrise APIs is that they have bits missing – rather significant bits it turns out.  In particular the ability to create and update companies.</p>
<p>To get around this I wrote a couple of classes that use a combination of curl() and the awesome <a href="http://code.google.com/p/phpquery/">phpQuery</a> to walk through the front-end pages and execute URLs just as a real user would.</p>
<p>Whilst this is not impervious to UI changes by 37Signals, it’s a fairly generic implementation and it’s a best endeavours approach given the current lack in the APIs.</p>
<p>Here’s some sample code:</p>
<pre class="brush: php; title: ; notranslate">
public function createCompany($companyName) {

$success = true;

// go to the company page first, to get the token.
$resp = $this-&gt;_fetchUrl($this-&gt;_urls['base'].$this-&gt;_urls['companies'], 'GET', array(), false, $this-&gt;_ch);

phpQuery::newDocument($resp['data']);
$authToken = pq('input[name=authenticity_token]')-&gt;val();

$formFields = array(
'authenticity_token' =&gt; $authToken,
'company[name]' =&gt; $companyName,
'commit' =&gt; 'Create Company'
);

$resp = $this-&gt;_fetchUrl($this-&gt;_urls['base'].$this-&gt;_urls['companies'], 'POST', $formFields, false, $this-&gt;_ch);

// check - has this company already been created?
phpQuery::newDocument($resp['data']);
if(pq('div.flash_alert')-&gt;text() != '') {
$success = false;
$this-&gt;_errMsg = pq('div.flash_alert')-&gt;text();
}

return $success;
}
</pre>
<h2>Show me the code!</h2>
<p>So, in the hope of helping others to get synchronisation working (and work around the lack in the APIs) you can <a href="https://github.com/shuggill/Contact-Synchronisation">download the code at GitHub</a> for your own use.</p>
<p>Enjoy!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/04/29/synchronising-highrise-basecamp-and-google-shared-contactspart-2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>The Novice’s Guide to Replacing a Bathroom–Part 1</title>
		<link>http://www.huggill.com/2011/04/25/the-novices-guide-to-replacing-a-bathroompart-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=the-novices-guide-to-replacing-a-bathroompart-1</link>
		<comments>http://www.huggill.com/2011/04/25/the-novices-guide-to-replacing-a-bathroompart-1/#comments</comments>
		<pubDate>Sun, 24 Apr 2011 23:10:26 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[Personal]]></category>

		<guid isPermaLink="false">https://shuggill.wordpress.com/2011/04/25/the-novices-guide-to-replacing-a-bathroompart-1/</guid>
		<description><![CDATA[Yes, bank holiday weekend. Time for Easter reflections, visiting family, putting your feet up. Also a great opportunity to get some DIY done. And with the wife and son away for the weekend, I decided to start one of several pending projects: replacing the main bathroom. &#160; A bit of background Our house was built &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/04/25/the-novices-guide-to-replacing-a-bathroompart-1/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.huggill.com/wp-content/uploads/2011/04/img_0573.jpg"><img style="background-image:none;border-bottom:0;border-left:0;padding-left:0;padding-right:0;display:inline;float:left;border-top:0;border-right:0;padding-top:0;" title="IMG_0573" border="0" alt="IMG_0573" align="left" src="/wp-content/uploads/2011/04/img_0573_thumb.jpg" width="244" height="183" /></a>Yes, bank holiday weekend. Time for Easter reflections, visiting family, putting your feet up.</p>
<p>Also a great opportunity to get some DIY done. And with the wife and son away for the weekend, I decided to start one of several pending projects: replacing the main bathroom.</p>
<h2>&#160;</h2>
<h2>A bit of background</h2>
<p>Our house was built in 1988, and both the main bathroom and the en-suite had not been touched since. From the beautiful stripy toothpaste style tiles to the faded pink sanitary ware, they were relics of a by-gone era of interior design, albeit functional relics.&#160; When we moved in we had the en-suite re-fitted (I only did the flooring and painting), so the other bathroom has continued to age over the last 12 months.</p>
<p>My background in DIY is fairly limited – I can paint, lay laminate flooring, and generally solve most basic household problems. However I’ve never done any plumbing, tiling or bathroom work.</p>
<h2>The (Rough) Plan</h2>
<p>So the rough plan was to:</p>
<ul>
<li>Replace the bath, hand basin and toilet</li>
<li>Replace the light fittings</li>
<li>Keep the existing electric shower</li>
<li>Re-paint and re-tile</li>
</ul>
<p>We only decided to get on with this early in the week, so we got a move on with choosing colours, flooring and tiling.&#160; We’ve had the toilet and hand basin in the garage since we moved in.</p>
<h2>Sequence of Events</h2>
<p>So I started off by removing the light fittings (after turning off the circuits of course) and the radiator.&#160; Removing a radiator is easy – but don’t make my mistake which was answering the door, chatting for 10 minutes, and then hearing the pitter-patter of water dripping through the kitchen because I hadn’t fully tightened up the water pipe valves.</p>
<p>Next I removed the electric shower – again this was easy, but I had to take note of how it all went together.&#160; And of course turn off the water before hand (and drain the system).</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:37a76012-347a-4e6b-9da9-285f673e4677" class="wlWriterEditableSmartContent">
<div><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/kEJ-vDepwck"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="never"></param><param name="allownetworking" value="internal"></param><param name="flashvars" value="" /><embed src="http://www.youtube.com/v/kEJ-vDepwck" type="application/x-shockwave-flash" allowscriptaccess="never" allownetworking="internal" allowfullscreen="true" width="425" height="344" flashvars=""></embed></object></div>
<div style="width:448px;clear:both;font-size:.8em;">Removing my Shower</div>
</div>
<p>Then I attacked the tiles with a bolster chisel and club hammer – most of them came off fairly easily (although it’s still hard work).</p>
<p>So far so good.</p>
<p>Then came the plumbing.</p>
<p>I cut the pipes to the sink taps with a pipe slice, unscrewed the supporting bolts and disconnected the waste pipe. Out came the basin and pedestal. Easy.</p>
<p>I popped two pipe caps over the supply pipes and moved on.</p>
<p align="left">Off came the side of the bath and more pipes to disconnect.&#160; In the following two hours I used up my quota of expletives as I wrestled with getting a spanner (which had to be the largest one I had) into an impossibly small hole behind the bath with an overflow pipe in the way.</p>
<p align="left">One blistered hand later, I got the bath out (gingerly moving it and hopping it over the waste pipe).</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:0ef135a9-3b20-4f42-9829-328ac74fbae4" class="wlWriterEditableSmartContent">
<div><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/_vnxmfb5lPY"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="never"></param><param name="allownetworking" value="internal"></param><param name="flashvars" value="" /><embed src="http://www.youtube.com/v/_vnxmfb5lPY" type="application/x-shockwave-flash" allowscriptaccess="never" allownetworking="internal" allowfullscreen="true" width="425" height="344" flashvars=""></embed></object></div>
</div>
<p align="left">At 10pm last night I was feeling very pleased with myself – all pipes capped, two units removed, most of the tiles off. I had even turned the water back on to check the pipe work was OK and no leaks.</p>
<p align="left">Then pop, and splash, and more dripping.</p>
<p align="left">One of the pipe caps had come off.</p>
<p align="left">A cycle that I’ve been through many times: switch off the water, drain, mop up, catch the drips in the kitchen and fixing isolation valves.&#160; Switch the water back on, wait and check for leaks.</p>
<div style="display:inline;float:none;margin:0;padding:0;" id="scid:5737277B-5D6D-4f48-ABFC-DD9C333F4C5D:4cd8e4c3-9cfc-4880-a28d-0a1f52bfaa6c" class="wlWriterEditableSmartContent">
<div><object width="425" height="344"><param name="movie" value="http://www.youtube.com/v/J5ma5W7aoaE"></param><param name="allowFullScreen" value="true"></param><param name="allowscriptaccess" value="never"></param><param name="allownetworking" value="internal"></param><param name="flashvars" value="" /><embed src="http://www.youtube.com/v/J5ma5W7aoaE" type="application/x-shockwave-flash" allowscriptaccess="never" allownetworking="internal" allowfullscreen="true" width="425" height="344" flashvars=""></embed></object></div>
</div>
<p align="left">Today the toilet came out (surprisingly easily) leaving more pipes to sort out.&#160; </p>
<p align="left">New building regulations state that isolation valves should be fitted to all water pipes in new bathrooms.&#160; These valves mean that you can switch off the water supply to an appliance without having to turn off the mains water supply.</p>
<p align="left">I’ve spent hours today testing, tightening and re-testing these valves (and some deal of mopping up).</p>
<p align="left">My conclusion: wires are nice as they bend and you can disconnect them. Pipes do not bend and cannot be turned off.&#160; Electricity can only spark – water drips everywhere.</p>
<p align="left">Electrics = good</p>
<p align="left">Plumbing = bad</p>
<h2 align="left">Top Tips</h2>
<p align="left">My top tips from two days’ of bathroom work:</p>
<ul>
<li>
<div align="left">Check your pipe sizes before buying anything – don’t rely on B&amp;Q advice (like I did).&#160; This probably means removing the side of your bath to have a look (my hot water was 22mm rather than 15mm).</div>
</li>
<li>
<div align="left">Cut pipes as carefully as possibly to avoid deforming them and cutting them square</div>
</li>
<li>
<div align="left">Remove the door – space becomes limited very quickly</div>
</li>
<li>
<div align="left">Give yourself plenty of time!</div>
</li>
</ul>
<h2 align="left">References</h2>
<p align="left">A couple of sites that have been very useful:</p>
<p align="left"><a href="http://www.diydoctor.org.uk/projects/bathroom.htm">How to find a bathroom suite – DIY Doctor</a></p>
<p align="left"><a href="http://www.diyhowto.co.uk/projects/replace-toilet.htm">Remove and Replace a Toilet – DIY How To</a></p>
<h2>More?</h2>
<p>Now that I’ve removed the old bathroom, I’ve got to fit the new one, decorate and finish off…</p>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/04/25/the-novices-guide-to-replacing-a-bathroompart-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Synchronising Highrise, Basecamp and Google Shared Contacts–Part 1</title>
		<link>http://www.huggill.com/2011/04/01/synchronising-highrise-basecamp-and-google-shared-contactspart-1/?utm_source=rss&#038;utm_medium=rss&#038;utm_campaign=synchronising-highrise-basecamp-and-google-shared-contactspart-1</link>
		<comments>http://www.huggill.com/2011/04/01/synchronising-highrise-basecamp-and-google-shared-contactspart-1/#comments</comments>
		<pubDate>Fri, 01 Apr 2011 09:41:17 +0000</pubDate>
		<dc:creator>shuggill</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Technology]]></category>
		<category><![CDATA[Tools]]></category>

		<guid isPermaLink="false">https://shuggill.wordpress.com/2011/04/01/synchronising-highrise-basecamp-and-google-shared-contactspart-1/</guid>
		<description><![CDATA[NOTE: You can read part 2 here or just simply get the code. I recently worked on a project where we needed to synchronise contact data between Highrise CRM (the source), Basecamp (for project management) and Google Shared Contacts (for email).  This is a common issue for small businesses – how to keep disparate systems &#8230; </p><p><a class="more-link block-button" href="http://www.huggill.com/2011/04/01/synchronising-highrise-basecamp-and-google-shared-contactspart-1/">Continue reading &#187;</a>]]></description>
			<content:encoded><![CDATA[<p><strong>NOTE</strong>: You can <a href="http://www.huggill.com/2011/04/29/synchronising-highrise-basecamp-and-google-shared-contactspart-2/">read part 2 here</a> or just simply <a href="https://github.com/shuggill/Contact-Synchronisation">get the code</a>.</p>
<p>I recently worked on a project where we needed to synchronise contact data between Highrise CRM (the source), Basecamp (for project management) and Google Shared Contacts (for email).  This is a common issue for small businesses – how to keep disparate systems in sync.  There are already some proprietary systems out there which help with this but they didn’t fulfil the needs of this client so we opted for a custom tool.</p>
<p>I’ll write up the steps I took to get all of this working in a series of posts, but the overall architecture is thus:</p>
<p><a href="http://www.huggill.com/wp-content/uploads/2011/04/image.png"><img style="background-image:none;padding-left:0;padding-right:0;display:inline;padding-top:0;border-width:0;" title="image" src="/wp-content/uploads/2011/04/image_thumb.png" alt="image" width="469" height="198" border="0" /></a></p>
<h2>Feasibility</h2>
<p>At the time of writing I determined the feasibility of synchronising all of these three systems by looking in detail at the APIs provided.  I’ve summarised my findings here:</p>
<h3></h3>
<h3>Google Shared Contacts</h3>
<p>It’s worth pointing out that we needed to load up NOT Google’s personal contacts, but the domain shared contacts.  This is a repository of contacts that Google provides on its Google Apps product, and works like a Global Address List, providing a single store of contact data to all domain users.  However, for some puzzling reason Google does not provide any user interface to manage this database.  Instead you must use the Google Shared Contacts API.</p>
<p><strong><span style="color:#ff0000;">IMPORTANT POINT</span></strong>: I got very confused as to which versions of Google Apps supports the Google Shared Contacts API. Even though the documentation pages state that it is only supported by Apps for Business (Premium) and Education, the confusing point is that YOU CAN EXECUTE THE API CALLS SUCCESSFULLY and the data IS PERSISTED.</p>
<p>What I later discovered was that although all of the API calls work, the data is not exposed in Google Apps.  This is very frustrating and I wish Google would make this clearer in their documentation.</p>
<p>Also don’t get confused between the Google Contacts Data API and the Google Shared Contacts API.</p>
<h3>Highrise &amp; Basecamp</h3>
<p>Both of these products are made by 37Signals, and if you’ve ever read their books, you’ll know that they are keen proponents of open systems.  They have well documented APIs which are very easy to get up and working with.</p>
<p>However, when you start to use them, you discover that some key elements are missing. The APIs they have provided allow you to get all information out, but when it comes to creating data, the calls just aren’t there. Despite numerous forum posts, 37Signals have not yet given good reasons for the lack of these API calls.</p>
<p>To get around this, I wrote a cheeky HTTP GET, Parse and Re-POST class which effectively walks through the front-end of the site programmatically, as if it were a standard user. Whilst this has been very stable so far, if either of the products publish changes to their UI then this code may need modification.</p>
<h2>Coding Approach</h2>
<p>I wrote all of the code in PHP 5, so all the samples here will be in PHP, but you could easily apply the concepts to different programming languages.</p>
<p>I built singelton classes for each of the products, and then a controlling class to tie them up all to achieve the synchronisation. I used PHP curl() to do the web requests, and for the Google API stuff I used Zend’s GData classes to speed up the development.</p>
<h2>Synchronisation Approach</h2>
<p>In order to synchronise these systems I took the following approach:</p>
<ul>
<li>Get all Highrise Contacts</li>
<li>Add to Basecamp, storing the IDs of the two data objects in a synchronisation file</li>
<li>Add to Google Contacts, storing the ID of the Highrise Contact in the Notes field</li>
</ul>
<h2>Next Steps</h2>
<p>In the next post I’ll start introducing the code I wrote to hook up to the Basecamp and Highrise APIs.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.huggill.com/2011/04/01/synchronising-highrise-basecamp-and-google-shared-contactspart-1/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Page Caching using disk: basic
Object Caching 903/1008 objects using disk: basic

Served from: www.huggill.com @ 2012-01-28 09:52:17 -->
