<?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"
	>

<channel>
	<title>Graceful Exits &#187; projects</title>
	<atom:link href="http://www.jpstacey.info/blog/category/projects/www.jpstacey.info/blog/category/projects/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.jpstacey.info/blog</link>
	<description>Garbage collection, in a very real sense</description>
	<pubDate>Tue, 30 Sep 2008 20:10:32 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.2</generator>
	<language>en</language>
			<item>
		<title>The TimeToLead.eu technical stack: Django and Flex</title>
		<link>http://www.jpstacey.info/blog/2008/09/11/the-timetoleadeu-technical-stack-django-and-flex/</link>
		<comments>http://www.jpstacey.info/blog/2008/09/11/the-timetoleadeu-technical-stack-django-and-flex/#comments</comments>
		<pubDate>Thu, 11 Sep 2008 18:57:43 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[configuration]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[beautifulsoup]]></category>

		<category><![CDATA[django]]></category>

		<category><![CDATA[flex]]></category>

		<category><![CDATA[fouc]]></category>

		<category><![CDATA[hosting]]></category>

		<category><![CDATA[i18n]]></category>

		<category><![CDATA[l10n]]></category>

		<category><![CDATA[lapd]]></category>

		<category><![CDATA[multilingual]]></category>

		<category><![CDATA[swfobject]]></category>

		<category><![CDATA[technical]]></category>

		<category><![CDATA[timetolead.eu]]></category>

		<category><![CDATA[unit test]]></category>

		<category><![CDATA[webfactional]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=203</guid>
		<description><![CDATA[Move over LAMP: here comes LAPD.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.jpstacey.info/blog/2008/09/05/they-work-for-eu/" >As discussed previously</a>, at <a href="http://torchbox.com/" >Torchbox</a> we recently built <a href="http://timetolead.eu/" >TimeToLead.eu</a>, an advocacy site set up by the four major environmental NGOs to prompt MEPs into passing the sort of legislation the world&#8217;s climate desperately needs. The project itself needed a fast turnaround time, and its pan-European audience demanded strong <abbr title="internationalization" >i18n</abbr> and <abbr title="localization">l10n</abbr>. This had to be in place at all layers, including an embedded Flex application.</p>
<p>In a parallel with the acronym LAMP, the overall stack is probably best described as &#8220;LAPD&#8221;:</p>
<ul>
<li><strong>L</strong>inux</li>
<li><strong>A</strong>pache</li>
<li><strong>P</strong>ostgreSQL</li>
<li><strong>D</strong>jango</li>
</ul>
<p>although there are refinements at almost every level, which I&#8217;ll go into below.</p>
<p>I18n with <strong>Django</strong> was a joy to implement. We had to have the site translated in six languages, but this was practically a doddle with <a href="http://www.djangoproject.com/documentation/i18n/" >Django&#8217;s core internationalization behaviour</a>. Before we&#8217;d put the translations in, I clicked on &#8220;POLSKI&#8221; and was sure the l10n wasn&#8217;t working, until I spotted that the &#8220;ENGLISH&#8221; link had magically changed to &#8220;ANGIELSKI&#8221;. </p>
<p>The only issue we&#8217;ve had&#8212;which we&#8217;re hoping to work around&#8212;was deciding on an initial translation when the visitor arrived for the first time. Some parts of the system seemed to need l10n&#8212;the plumping for a specific version of the site&#8212;before Django&#8217;s <code>LocaleMiddleware</code> had done that for us, so we&#8217;ve had to force English until the user states otherwise. I&#8217;m hoping that a version 2 rewrite will fix that.</p>
<p><strong>PostgreSQL</strong> might seem an odd choice of database to the LAMP community, who are used to MySQL&#8217;s minuscule overhead and often actively work around its deficiencies to keep that low overhead. But PostgreSQL&#8217;s stability and maturity outweigh the performance issues which&#8212;with a little care and attention&#8212;it&#8217;s possible to at least partly mitigate. </p>
<p>Compared to MySQL or Oracle, PostgreSQL also has the accolade of <a href="http://docs.djangoproject.com/en/dev/ref/databases/" >being supported by Django but having no outstanding database issues</a>. Whether that&#8217;s because nobody else uses it, or because the integration is tight and relatively bug-free, I daren&#8217;t comment, but we&#8217;ve had nothing but seamless, transparent behaviour thus far.</p>
<p>Our Django-oriented hosting is with <a href="http://www.webfaction.com/hosting/django-hosting" >Webfaction</a>, who are geared up for one-click <strong>mod_python</strong> Django deployments. The Django app sits in its own <strong>Apache</strong> process, while static files are served by the <strong>Linux</strong> server&#8217;s main Apache process: differential serving of content lets us take advantage of differential hosting fees. The hosting is a pretty good package, although TimeToLead.eu (despite being a pretty small application) is already finding the maximum memory package a bit restrictive, so we&#8217;ll need to keep an eye on that.</p>
<p>On top of this, Django has to a greater or lesser extent enabled us to use a whole host of other neat little technologies to integrate the site and improve both the user and the developer&#8217;s experience:</p>
<ul>
<li>The main Flash widget is written in <a href="http://www.adobe.com/products/flex/" >Flex</a>: I&#8217;m no particular fan of Flash myself, but it fulfills the remit admirably here. Because of text flow issues, there are actually six versions of the Flash file, one for each language, but the majority of the explanatory text is then picked up from an XML feed provided by Django, so can be retranslated by the client without recompiling Flex.</li>
<li><a href="http://code.google.com/p/swfobject/" >Swfobject</a> serves up our main widget and the YouTube file. It degrades well, but bear in mind that the API to it has changed considerably in the most recent major version.</li>
<li>Unit tests in Django cover a number of stress points in the code (although not all, owing to the timescale). Towards the end, we were testing any major functionality changes by writing the tests first, which I was really pleased with.</li>
<li>BeautifulSoup, which <a href="/blog/2008/09/07/spliticket-running-again-with-beautifulsoup/" >I mentioned a few days ago</a>, is employed in the unit tests to check that certain content is coming through on the front end. It also managed to help us proof against a browser-dependent <a href="http://www.bluerobot.com/web/css/fouc.asp/" >FOUC</a> we encountered during development.</li>
<li>Translations are managed by the Django application <a href="http://code.google.com/p/django-rosetta/" >Rosetta</a>: the most recent stable version is for Django 0.96 but the bleeding-edge repository copy seems to work OK on 1.0b1</li>
</ul>
<p>Django has been the saviour here, living up to its promise as a framework for rapid application development. No framework is ever perfect, and there&#8217;ll always be arguments over the right way to proceed, reconciling what the framework developers want you to do with what the end developer wants to do, but this particular brief sojourn alongside <a href="http://docs.djangoproject.com/en/dev/internals/committers/" >Holovaty, Kaplan-Moss, Willison <i>et al</i></a> has been tremendous fun.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/09/11/the-timetoleadeu-technical-stack-django-and-flex/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Spliticket running again with BeautifulSoup</title>
		<link>http://www.jpstacey.info/blog/2008/09/07/spliticket-running-again-with-beautifulsoup/</link>
		<comments>http://www.jpstacey.info/blog/2008/09/07/spliticket-running-again-with-beautifulsoup/#comments</comments>
		<pubDate>Sun, 07 Sep 2008 18:10:27 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[formats]]></category>

		<category><![CDATA[hacking]]></category>

		<category><![CDATA[import/export]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[beautifulsoup]]></category>

		<category><![CDATA[html]]></category>

		<category><![CDATA[interface]]></category>

		<category><![CDATA[library]]></category>

		<category><![CDATA[mashup]]></category>

		<category><![CDATA[matthew]]></category>

		<category><![CDATA[parser]]></category>

		<category><![CDATA[rule]]></category>

		<category><![CDATA[sax]]></category>

		<category><![CDATA[somerville]]></category>

		<category><![CDATA[spliticket]]></category>

		<category><![CDATA[state]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=204</guid>
		<description><![CDATA[Or, how I learned to stop parsing and love the soup]]></description>
			<content:encoded><![CDATA[<p>Ages ago Matthew Somerville emailed me to say that <a href="http://www.jpstacey.info/applications/spliticket/" >spliticket</a> had fallen over. It&#8217;s <a href="http://www.jpstacey.info/blog/2008/03/13/cheaper-rail-journeys-with-matthew-and-spliticket/" >my hacky interface</a> to <a href="http://www.dracos.co.uk/wiki/Trains/SplitTickets" >his wiki page documenting split tickets</a>, and ultimately it found the vagaries of even wiki-generated HTML a bit too hard to cope with.</p>
<p>At the time I built the HTML parser using core SAX-based HTML parsing, and it was horrible. SAX works in a basic sense, but you have to build your own internal state engine, track which elements have gone past while working out what to do with the current context, and even write rules for what to do when the underlying dumb parser encounters HTML entities: no mean feat when the document is peppered with &amp;#8211; en dashes. </p>
<p>Not only was writing the rules initially a pain in the rear, but adding new rules and bugfixing the existing ones was even worse. But I lived with SAX,  because I was deploying on shared hosting: I presumed that this was the best option available if I couldn&#8217;t install any new shared libraries.</p>
<p><em>Not true!</em> I&#8217;ve just rebuilt the entire parsing layer with <a href="http://crummy.com/software/BeautifulSoup" >Beautiful Soup</a>, a Python HTML/XML parser library which (a) is available as a single file and (b) works out a decent HTML DOM tree from pretty much anything you throw at it.</p>
<p>Try it yourself, if you have to do any HTML parsing.It&#8217;s astonishing; beautiful, in fact. I will never write another SAX parser ever again, which I&#8217;m sure I&#8217;ve said before.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/09/07/spliticket-running-again-with-beautifulsoup/feed/</wfw:commentRss>
		</item>
		<item>
		<title>They Work For EU</title>
		<link>http://www.jpstacey.info/blog/2008/09/05/they-work-for-eu/</link>
		<comments>http://www.jpstacey.info/blog/2008/09/05/they-work-for-eu/#comments</comments>
		<pubDate>Fri, 05 Sep 2008 10:10:15 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[news]]></category>

		<category><![CDATA[non-programming]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[advocacy]]></category>

		<category><![CDATA[climatechange]]></category>

		<category><![CDATA[eu]]></category>

		<category><![CDATA[european]]></category>

		<category><![CDATA[mep]]></category>

		<category><![CDATA[onehundredmonths]]></category>

		<category><![CDATA[parliament]]></category>

		<category><![CDATA[votes]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=201</guid>
		<description><![CDATA[If you live in the European Union, tell your MEPs that you'd quite like there to be a planet left for your grandchildren.]]></description>
			<content:encoded><![CDATA[<p>The best guesses for how long we&#8217;ve got to completely change our behaviour and avoid catastrophic climate change vary between <a href="http://www.onehundredmonths.org/" >a hundred months</a> and around fifteen years. But in the next few months there&#8217;ll be a number of votes in the European Parliament that could decide the next hundred, which could decide the fate of us all.</p>
<p>To this end, four major environmental NGOs (<a href="http://www.greenpeace.eu/" >Greenpeace</a>, <a href="http://www.foei.org/" >Friends of the Earth</a>, the <a href="http://www.panda.org/">World Wide Fund For Nature</a> and <a href="http://www.climnet.org/" >CAN Europe</a>) have put together the following site, to help ordinary people&#8212;EU constituents&#8212;compel their representative MEPs to pass a package of laws that will tackle climate change and hopefully help to save the planet:</p>
<blockquote style="text-align: center"><p>
  <a href="http://timetolead.eu"><img src="http://timetolead.eu/static/images/nav/en/logo.gif" alt="Keep global warming below 2&deg;C: Europe, it's time to lead!" width="415" height="71"  style="border: solid 1px #882200; background-color: white; padding: 0.5em"/></a>
</p></blockquote>
<p>(Disclaimer: <a href="http://torchbox.com/" >we</a> built the site. More on the tech later: go badger your MEP first!)</p>
<p>[Edit: changed timescales on request]</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/09/05/they-work-for-eu/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Successful musicians write songs that other people like</title>
		<link>http://www.jpstacey.info/blog/2008/08/12/successful-musicians-write-songs-that-other-people-like/</link>
		<comments>http://www.jpstacey.info/blog/2008/08/12/successful-musicians-write-songs-that-other-people-like/#comments</comments>
		<pubDate>Tue, 12 Aug 2008 19:32:24 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[business]]></category>

		<category><![CDATA[culture]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[useability]]></category>

		<category><![CDATA[foss]]></category>

		<category><![CDATA[rants]]></category>

		<category><![CDATA[requirements]]></category>

		<category><![CDATA[scratchyourownitch]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[success]]></category>

		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=193</guid>
		<description><![CDATA[By all means be careful what you invest in: whether you're paying with your time or with your money. But thinking about the needs of your fellow man can reap rewards too: not just for your moral integrity.]]></description>
			<content:encoded><![CDATA[<p>I love <a href="http://steve-yegge.blogspot.com/" >Stevey&#8217;s Blog Rants</a>: I don&#8217;t always agree with him, but he puts forward a hell of a lot of interesting ideas. Also, he writes long blog posts, which is respectful to his readership, who he considers to be something other than attention-deficit idiots. In a way, he&#8217;s writing posts that would probably be interesting to himself and people like him.</p>
<p>This model of publishing in part explains the thinking behind his recent post, robustly titled <a href="http://steve-yegge.blogspot.com/2008/08/business-requirements-are-bullshit.html" >Business Requirements are Bullshit</a>. But Steve&#8217;s audience&#8212;compared to the web at large&#8212;is a small, self-selecting group. So although keeping in mind some of the details of his recent post can prevent you investing in dead-end projects, I just can&#8217;t agree with the overall conclusions about software development.</p>
<p>Although it&#8217;d be nice for everyone in the world to have programming skills, and to be able to behave like autonomous itch-scratching units, that simply isn&#8217;t the case. The vast majority of people need software built for them, and software builders are a demographic, with a broad range of shared interests and a vast landscape of shared uninterests. What if you can&#8217;t program but you want some software? Do you just sit there, or do you pay someone to build software for you? Should that person in turn refuse the money, saying &#8220;that&#8217;s too risky: Steve said so?&#8221;</p>
<p>Steve&#8217;s rant is aimed at CEOs instead, but the principle still stands. To what extent to people have to clamour for a particular feature before a CEO will say &#8220;well, I don&#8217;t want that, but I do want your money?&#8221; Personal phone calls? Petitions to their local MPs? Pre-ordering? Pressing themselves against the windows of electronics shops and drooling on the glass? Demanding it be available on the welfare state? Well, the canny software house would have started building the software before any of the above had happened. But how could they know, if they&#8217;re not secretly telepathic? Well, among other methods, by <em>gathering business requirements</em>.</p>
<p>From Steve&#8217;s point of view it&#8217;s less of an issue, because he works at Google, the House of Blue Sky Development. And I don&#8217;t begrudge him that privileged viewpoint at all, because Google has earned the business success that bankrolls schemes like <a href="http://www.google.com/support/jobs/bin/static.py?page=about.html&#038;about=eng" >twenty-percent time</a> projects. If every business had oodles of cash to throw at developers as they wander off along tangential projects, then maybe none of them would need to through half an oodle at a discovery phase. But as long as money is a locally limited and unevenly distributed resource, then there will need to be different solutions to the problem of working out what to actually build.</p>
<p>The ultimate direction of software development proposed by Steve is just far too exclusive, and it&#8217;s been the bane of open-source projects for years. Project after project caters for its tiny community, never reaching out to what other communities might need; they start to cool off, then founder; the codebase is mothballed, and the project finally expires. Worse, in Steve&#8217;s world, we&#8217;d have to wait for blind people and RSI sufferers to write their own <a href="http://www.theinquirer.net/en/inquirer/news/2006/08/31/microsoft-vista-to-silence-ibm-viavoice-nuance" >FOSS voice recognition software</a>: maybe the blind person could hold the keyboard and mouse, while the RSI sufferer tells them what&#8217;s on the screen. Meanwhile, able-bodied programmers develop that sort of stuff for Vista without a qualm, and they can charge the earth for it because <em>people want it</em>.</p>
<p>Ubuntu&#8217;s recent successes might be almost entirely attributed to the fact that (a) the project is well managed and directed and (b) they reach outside their own community, and solve problems for people other than computer programmers. Despite Ubuntu, <a href="http://ca.biz.yahoo.com/ibd/080807/tech.html?.v=1" >Gartner recently announced that Linux had a 4% market share</a>. 96% of consumers opt for software built by people who on the whole <em>weren&#8217;t</em> solving their own problems.</p>
<p>The first commenter on Steve&#8217;s recent post says:</p>
<blockquote><p>
I find that a lot of Free Software is awful for exactly this reason — the authors built it for themselves. Their software only works for other hardcore programmers because they can put up with the same complex implementation and integration problems and not even notice them, and if it&#8217;s not quite right they lose a million potential worldwide users for every mistake.</p>
<p>The recent success of Ubuntu as Open Source Software shows that a lot of other projects still don&#8217;t get it. The first thing an OS community needs is outreach: scratching other people&#8217;s itches and not their own.
</p></blockquote>
<p>Actuallly, he says almost exactly the opposite, but then: <em>he emails people with patches</em>, so he&#8217;s probably a software author himself. At the very least, he&#8217;s in that four percent, preaching to the rest of the already converted. Meanwhile, the rest of the market went <em>thataway</em>. And if Warren Buffett were to take a break from spreading thickly his easily-believed homespun down-to-earth nonsense for a minute or two, what would he <em>really</em> do to capture that 96%?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/08/12/successful-musicians-write-songs-that-other-people-like/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Firefox/Sage bookmarks to Google Reader import</title>
		<link>http://www.jpstacey.info/blog/2008/07/17/firefoxsage-bookmarks-to-google-reader-import/</link>
		<comments>http://www.jpstacey.info/blog/2008/07/17/firefoxsage-bookmarks-to-google-reader-import/#comments</comments>
		<pubDate>Thu, 17 Jul 2008 19:04:54 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[formats]]></category>

		<category><![CDATA[import/export]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[quickies]]></category>

		<category><![CDATA[bookmarks]]></category>

		<category><![CDATA[DTD]]></category>

		<category><![CDATA[export]]></category>

		<category><![CDATA[firefox]]></category>

		<category><![CDATA[google]]></category>

		<category><![CDATA[import]]></category>

		<category><![CDATA[opml]]></category>

		<category><![CDATA[reader]]></category>

		<category><![CDATA[sage]]></category>

		<category><![CDATA[standard]]></category>

		<category><![CDATA[transform]]></category>

		<category><![CDATA[xml]]></category>

		<category><![CDATA[xslt]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=186</guid>
		<description><![CDATA[When OPML is OPML but it isn't OPML]]></description>
			<content:encoded><![CDATA[<p>Want to migrate your RSS bookmarks from Firefox (or its RSS-reading addon, Sage) to Google Reader? I did, just now.</p>
<p>Christopher Hinze has written a great <a href="https://addons.mozilla.org/en-US/firefox/addon/2625" >Firefox addon that exports bookmarks to OPML 1.0</a>. Unfortunately, OPML is a bit of an <a href="http://www.opml.org/spec1" >anything-goes specification</a>. So although Hinze&#8217;s plugin produces valid OPML, it isn&#8217;t the same sort of valid OPML that Google Reader expects. Google Reader, in fact, gags and chokes on Hinze&#8217;s OPML, and refuses to import it.</p>
<p>The main problem is that the &lt;outline/&gt; element, the basic hierarchical building block for OPML, <a href="http://www.opml.org/spec1#limits" >will take <em>any attributes</em></a>. What does that mean in practice? Well, here&#8217;s what Hinze&#8217;s export produces:</p>
<blockquote class="code"><p>
&lt;outline text=&#8221;Coding&#8221;><br />
&nbsp;&nbsp;&lt;outline type=&#8221;link&#8221; text=&#8221;Joel on Software&#8221; url=&#8221;http://www.joelonsoftware.com/rss.xml&#8221;   /><br />
&lt;/outline>
</p></blockquote>
<p>and here&#8217;s the result of Google Reader exporting its own store of RSS bookmarks:</p>
<blockquote class="code"><p>
&lt;outline title=&#8221;Coding&#8221; text=&#8221;Coding&#8221;><br />
&nbsp;&nbsp;&lt;outline text=&#8221;drupal.org - Community plumbing&#8221;<br />
&nbsp;&nbsp;&nbsp;&nbsp;title=&#8221;drupal.org - Community plumbing&#8221; type=&#8221;rss&#8221;<br />
&nbsp;&nbsp;&nbsp;&nbsp;xmlUrl=&#8221;http://drupal.org/node/feed&#8221; htmlUrl=&#8221;http://drupal.org&#8221;/><br />
&lt;/outline>
</p></blockquote>
<p>To a computer, these are fundamentally two different data formats: the URLs are stored in different attributes, and there are attributes on each that either have different values or are not present on the other. Someone did a <a href="http://static.userland.com/gems/radiodiscuss/opmlDtd.txt" >DTD for OPML</a>: looking at those two apparently analogous fragments above you have to ask yourself why they bothered.</p>
<p>Help is at hand, though. This sort of problem is bread and butter to XSLT, and <a href="/applications/google/ff2gr_opml.xsl" >here&#8217;s an XSL transform for converting Firefox OPML to Google Reader OPML</a>. If you have <code>xsltproc</code> installed on your system, you would type:</p>
<blockquote class="code"><p>
xsltproc http://www.jpstacey.info/applications/google/ff2gr_opml.xsl bookmarks.opml > fixed_bookmarks.opml
</p></blockquote>
<p>Or download the XSLT&#8212;it&#8217;s released under GPL2&#8212;and run it locally, changing that URL there to a local file location.</p>
<p>One thing to note: the XSLT will remove an outline wrapped around your bookmarks with title &#8220;Sage Feeds&#8221; (case-sensitive). So you can export that branch of your bookmarks, and the XSLT will strip the wrapper off and you <em>won&#8217;t</em> import a load of bookmarks tagged &#8220;Sage Feeds&#8221;. If you don&#8217;t like this behaviour then either rename your Sage bookmark container, or learn XSLT: it won&#8217;t kill you.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/07/17/firefoxsage-bookmarks-to-google-reader-import/feed/</wfw:commentRss>
		</item>
		<item>
		<title>The Straight Edge minimalist Wordpress theme</title>
		<link>http://www.jpstacey.info/blog/2008/07/16/the-straight-edge-minimalist-wordpress-theme/</link>
		<comments>http://www.jpstacey.info/blog/2008/07/16/the-straight-edge-minimalist-wordpress-theme/#comments</comments>
		<pubDate>Wed, 16 Jul 2008 21:19:00 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[design]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[alpha]]></category>

		<category><![CDATA[download]]></category>

		<category><![CDATA[php]]></category>

		<category><![CDATA[straightedge]]></category>

		<category><![CDATA[theme]]></category>

		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=172</guid>
		<description><![CDATA[The Straight Edge theme is now available for download.]]></description>
			<content:encoded><![CDATA[<p><a href="/blog/2008/06/30/embracing-minimalism/" >As promised</a>, I&#8217;m releasing <a href="/blog/files/code/straightedge.tgz" >the Straight Edge theme</a> used on this blog under GPL2. </p>
<p>There&#8217;s a brief README.txt in the zipped archive linked above, but the theme&#8217;s main features are:</p>
<ul>
<li>XHTML compatible (in core theme files)</li>
<li>Minimal, semantic markup</li>
<li><em>No sidebar</em></li>
<li>Excerpts on archive and category pages</li>
<li>Implicit RSS feeds: the only orange icon is in your browser chrome</li>
<li>Adaptive top navigation</li>
<li>Separate pages for archives, categorisation and blogrolls</li>
<li>Next/previous rel links in header</li>
<li>Support for special pages e.g. blogroll, tag cloud</li>
</ul>
<p>The todo list includes:</p>
<ul>
<li>Implement my Blogthis! plugin, while trying to keep minimalist</li>
<li>Unobtrusive hiding of elements, using jQuery</li>
<li>Improve styling</li>
</ul>
<p>The theme is in a fairly alpha state. The PHP is fairly straightforward, apart from some neat theme functions, but don&#8217;t blame me if everything goes bang.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/07/16/the-straight-edge-minimalist-wordpress-theme/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Hardy Heron and the Dell Precision M4300</title>
		<link>http://www.jpstacey.info/blog/2008/07/16/hardy-heron-and-the-dell-precision-m4300/</link>
		<comments>http://www.jpstacey.info/blog/2008/07/16/hardy-heron-and-the-dell-precision-m4300/#comments</comments>
		<pubDate>Wed, 16 Jul 2008 19:16:10 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[hardware]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[quickies]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[alsa]]></category>

		<category><![CDATA[dell]]></category>

		<category><![CDATA[display]]></category>

		<category><![CDATA[hardy]]></category>

		<category><![CDATA[heron]]></category>

		<category><![CDATA[install]]></category>

		<category><![CDATA[laptop]]></category>

		<category><![CDATA[linux]]></category>

		<category><![CDATA[m4300]]></category>

		<category><![CDATA[nvidia]]></category>

		<category><![CDATA[precision]]></category>

		<category><![CDATA[sound]]></category>

		<category><![CDATA[ubuntu]]></category>

		<category><![CDATA[upgrade]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=184</guid>
		<description><![CDATA[Summary: it just works.]]></description>
			<content:encoded><![CDATA[<p>In brief: the problems discussed <a href="http://www.jpstacey.info/blog/2008/01/07/the-full-sensory-experience-of-linux-on-a-dell-m4300-sound-vision-and-tinfoil-hat-microwaves/" >here</a> and <a href="http://www.jpstacey.info/blog/2007/08/28/laptop-and-linux-the-fixes-for-a-dell-precision-m4300/" >here</a> go away under the most recent <a href="http://www.ubuntu.com/" >Ubuntu</a> release, Hardy Heron, which I can generally recommend.</p>
<p>Alsa seems stable and graphics support is present from installation onwards. Enabling fancier 3D compiz effects requires the nvidia-glx-new package; compiz spots this, however and prompts for installation. All very smooth. Wireless works; my VoIP headset works; but I haven&#8217;t yet tested Bluetooth.</p>
<p>The only problem was in upgrading from Gutsy: my previous peregrinations had rendered my hybrid distribution shafted and incapable of upgrade. This isn&#8217;t a problem, though, if one has installed the /home directory (and in my case the /music one too) on a separate partition: the Ubuntu Live CD will blat the root partition with Heron, but leave the other partitions alone if you so require. Don&#8217;t resize any of your partitions during installation, though, or you&#8217;ll lose everything. Everything!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/07/16/hardy-heron-and-the-dell-precision-m4300/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Embracing minimalism</title>
		<link>http://www.jpstacey.info/blog/2008/06/30/embracing-minimalism/</link>
		<comments>http://www.jpstacey.info/blog/2008/06/30/embracing-minimalism/#comments</comments>
		<pubDate>Mon, 30 Jun 2008 12:47:34 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[design]]></category>

		<category><![CDATA[news]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[quickies]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[alpha]]></category>

		<category><![CDATA[minimalism]]></category>

		<category><![CDATA[pilgrim]]></category>

		<category><![CDATA[straightedge]]></category>

		<category><![CDATA[theme]]></category>

		<category><![CDATA[tomayko]]></category>

		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=170</guid>
		<description><![CDATA[Graceful Exits goes straight-edge with Straight Edge, a minimalist theme written by yours truly.]]></description>
			<content:encoded><![CDATA[<p>After re-reading <a href="http://www.jpstacey.info/blog/2008/06/22/rss-feeds-keep-them-well-hidden/" >my earlier post</a>, which was in general agreement with Pilgrim and Tomayko&#8217;s minimalism, I decided to practise what I had preached and write a minimalist theme implementing some of the applications of the principles of minimalism. </p>
<p>It&#8217;s called Straight Edge, and I&#8217;ve switched to it today. Once I&#8217;ve finished alpha-testing it I&#8217;ll write more about it, and offer it for download if anyone&#8217;s interested.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/06/30/embracing-minimalism/feed/</wfw:commentRss>
		</item>
		<item>
		<title>User loading and saving in Drupal 5.x</title>
		<link>http://www.jpstacey.info/blog/2008/06/09/user-loading-and-saving-in-drupal-5x/</link>
		<comments>http://www.jpstacey.info/blog/2008/06/09/user-loading-and-saving-in-drupal-5x/#comments</comments>
		<pubDate>Mon, 09 Jun 2008 15:48:48 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[diagnostics]]></category>

		<category><![CDATA[hacking]]></category>

		<category><![CDATA[information]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[5.x]]></category>

		<category><![CDATA[api]]></category>

		<category><![CDATA[core]]></category>

		<category><![CDATA[drupal]]></category>

		<category><![CDATA[hook]]></category>

		<category><![CDATA[id]]></category>

		<category><![CDATA[profile]]></category>

		<category><![CDATA[user]]></category>

		<category><![CDATA[user_load]]></category>

		<category><![CDATA[user_save]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2008/06/09/user-loading-and-saving-in-drupal-5x/</guid>
		<description><![CDATA[Workflows of Drupal's user load and save functionality: spot the hooks and win a programmatical prize.]]></description>
			<content:encoded><![CDATA[<p>Recently at <a href="http://torchbox.com/" >Torchbox</a> we&#8217;ve been looking into how to build extra functionality on top of Drupal users. The standard Drupal user object is a combination of the contents from the users table, plus any properties provided by the core <code>profile</code> module. This means that the Drupal user is a combination of rows (and admittedly deserialized, structured data) from a couple of tables in a relational database. </p>
<div style="float: right;"><a title="flowcharts of user_load and user_save" href="#user-flowcharts"><img src="/blog/files/drupal/core/drupal_user_teaser.gif" alt="flowcharts of user_load and user_save" width="145" height="184" /></a></div>
<p>That works just fine for most purposes, but we may have to bring in content from not just outside the core Drupal tables but outside the core database, and even on a remote server through webservices. To this end we&#8217;ve decomposed the core <code>user</code> module&#8217;s <code>user_load()</code> and <code>user_save()</code> functions. This helps us understand better both the workflow and at what points in it our own code can motor into life, query all those extra resources (or set those queries in motion), assemble the rest of the user++ object, and then hand control back over to Drupal.</p>
<p>For those who don&#8217;t know much about Drupal, its core has a <em>hook-based</em> API structure. At certain points in its workflow, it checks all the modules for functions following certain naming conventions (typically the module name followed by the hook name e.g. <code>mymodule_init</code> on response startup, or <code>mymodule_block</code> to return details about the module&#8217;s support for Drupal page furniture). Any matching hook functions are called in the order defined by module weightings, and then page processing will generally continue: you can crowbar a grind-to-a-halt <code>exit()</code> in your hook, but it&#8217;d be unwise, as you can never be sure what tidying up Drupal might need to do after your hook. Outside these hooks, your code has little control over Drupal&#8217;s core functioning, unless you stub out entirely the bits of core you need yourself, and get your request to use those bits instead.z</p>
<p>Because of the way they let your code tag along with Drupal&#8217;s powerful core, hooks are essential to developing modules in the most <em>Drupalish</em> way. With that in mind, <a name="user-flowcharts"></a>here are flow diagrams of the three basic aspects of user functionality&#8212;create new, load, save existing&#8212;lifted straight from examination of the code:</p>
<ul>
<li><a href="/blog/files/drupal/core/drupal_user_load.gif">Loading an existing user with user_load</a></li>
<li><a href="/blog/files/drupal/core/drupal_user_create.gif">Creating a user: saving a new user with user_save</a></li>
<li><a href="/blog/files/drupal/core/drupal_user_update.gif">Updating a user: saving an existing user with user_save</a></li>
</ul>
<p>Although you have to save a user before you can load them, I&#8217;ve put this functionality first in the above (admittedly unordered) list. There are two main reasons for this:</p>
<ol>
<li><code>user_save</code> actually calls <code>user_load</code> a number of times, once or twice, to &#8220;refresh&#8221; the user object</li>
<li><code>user_load</code> is a more primitive function and so bears examination first</li>
</ol>
<p>Stripped down, <code>user_load</code> consists of: querying the database for a core user record matching the search criteria; returning this and the extended profile data; unserializing a free-data field and inserting it into the user object; discovering user roles; <strong>triggering <code>hook_user('load')</code></strong> and returning the object (or boolean false, if no user found).</p>
<p>What this reveals (which I didn&#8217;t realise before) is that <em>the anonymous user is in the Drupal users table</em>, with ID=0. Otherwise, searching for this user would return no records, and the anonymous user object could not be instantiated. You could therefore attach rich data to the anonymous user, if you were in a hacky mood.</p>
<p>The two <code>user_save</code> workflows are fairly similar. Creating a user means obtaining an ID from the database: because some MySQL providers have poorer feature sets than others, referential integrity is ensured at the application level rather than the database level. In place of obtaining an ID, user update <strong>calls <code>hook_user('update')</code> to pre-process</strong> the user. Both workflows then set aside special fields, such as the user&#8217;s password, user roles and any profile fields managed by that module (determined from <code>user_fields()</code>). Then they save this data into the database in slightly different orders, with user creation <code>calling </code><code>hook_user('insert')</code> early on, and the update procedure <code>calling </code><code>hook_user('after_update')</code> much later in the process, just before determining the external authentication mappings (e.g. OpenID) and returning the user object.</p>
<p>What does this mean for us? Well, we&#8217;ll want varying amounts of data to piggyback on the core user object, so we have somewhere to cache it. Ideally this data won&#8217;t be summoned&#8212;brought out of the distributed data &#8216;cloud&#8217;&#8212;on every request/response cycle, so we&#8217;ll need to do some local cacheing, but not so much that we&#8217;ll get out of synch with the cloud (or that we&#8217;ll duplicate sensitive data). We think that, given the pair of hooks in <code>user_save</code> for existing users, we&#8217;ll have just enough leverage to do this: the first hook will effectively &#8220;tear down&#8221; our extra data, so we can do what we want with it, and store it somewhere temporarily; the second hook will &#8220;set up&#8221; the user for the rest of the request, by putting all that data back in. The existence of <code>user_load</code> within <code>user_save</code> complicates things somewhat, but at the same time it gives us some more wiggle room, because each call to that function fires another hook.</p>
<p>A Drupal hook is worth a thousand lines of module code, but they&#8217;re still a bit few and far between for some workflows. Hopefully the accompanying images will help anyone reading to find them, and ditch those thousand lines before they&#8217;re even written.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/06/09/user-loading-and-saving-in-drupal-5x/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Postcode Anywhere and MailBuild integration with Drupal</title>
		<link>http://www.jpstacey.info/blog/2008/05/22/postcode-anywhere-and-mailbuild-integration-with-drupal/</link>
		<comments>http://www.jpstacey.info/blog/2008/05/22/postcode-anywhere-and-mailbuild-integration-with-drupal/#comments</comments>
		<pubDate>Thu, 22 May 2008 20:28:26 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[projects]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[createsend]]></category>

		<category><![CDATA[drupal]]></category>

		<category><![CDATA[drupal-5.x]]></category>

		<category><![CDATA[googlecode]]></category>

		<category><![CDATA[gpl]]></category>

		<category><![CDATA[mailbuild]]></category>

		<category><![CDATA[module]]></category>

		<category><![CDATA[newsletter]]></category>

		<category><![CDATA[pamb]]></category>

		<category><![CDATA[postcode]]></category>

		<category><![CDATA[postcodeanywhere]]></category>

		<category><![CDATA[rest]]></category>

		<category><![CDATA[soap]]></category>

		<category><![CDATA[sourceforge]]></category>

		<category><![CDATA[torchbox]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2008/05/22/postcode-anywhere-and-mailbuild-integration-with-drupal/</guid>
		<description><![CDATA[Integrate SOAP-y web services with Drupal at a low level using PAMB.]]></description>
			<content:encoded><![CDATA[<p>As a result of building a website for a <a href="http://torchbox.com/" >Torchbox</a> client, I came up with a <a href="http://drupal.org/" >Drupal</a> 5.x module to query the Postcode Anywhere and MailBuild webservices (if they look like an unlikely mix, don&#8217;t worry: they&#8217;re not coupled together generally, so you can use one without the other). I&#8217;ve been meaning to make it live for ages, but never got round to scrubbing the client&#8217;s data out of it. Now that we&#8217;ve unearthed the module for other work then I&#8217;ve finally finished cleaning it.</p>
<p>First, a bit of background. <a href="http://www.postcodeanywhere.co.uk/" >Postcode Anywhere</a> provides a per-lookup paid webservice, converting UK postcodes into valid house names/numbers, streets, towns etc. It allows you to accept a user&#8217;s submission of a postcode to e.g. a signup form, then present them with a list of sample addresses, rather than them having to fill in all of their address details.</p>
<p>There&#8217;s only really one method exposed for the PA functionality, and it takes a text postcode and returns an array of results:</p>
<blockquote class="code"><p>
$data = pamb_pa_get_bypostcode($postcode);
</p>
</blockquote>
<p><a href="http://www.mailbuild.com/" >MailBuild and CreateSend</a>, on the other hand, provide a way to store email lists and send newsletter campaigns to subscribers. However, our client also wanted to use it to store snail-mail addresses, so they could use it for both requests for printed matter and also to keep subscribers up to date via email. It was possible to implement this using custom fields, of which the service permits around a dozen: address line 1, address line 2 etc.</p>
<p>There&#8217;s an <a href="http://drupal.org/project/mailbuild" >existing Mailbuild module</a>, which technically does the trick, but it&#8217;s overkill compared to what the client wanted. pamb&#8217;s integration is essentially just &#8220;push&#8221;: it passes the results of a form submission to CreateSend, by using a <i>template</i> (the one you&#8217;ve moved to your theme directory) to &#8220;theme&#8221; the data as an XML packet. If there are no problems then the system returns nothing; otherwise, it returns a structure of data including the HTTP response and the SOAP packet from the request.</p>
<p>Here&#8217;s some sample code:</p>
<blockquote class="code"><p>
/* Send SOAP packet */<br />
$no_probs = pamb_mb_send_soap($form_id, $form_values);</p>
<p>/* If OK, set a generic message and redirect: the redirect will pick up<br />
   on the generic message and know all has gone well */<br />
if(!isset($no_probs)) {<br />
&nbsp;&nbsp;drupal_set_message(t(AUTO_SUCCESS));<br />
}</p>
<p>/* If not OK, $no_probs contains the diagnostics */<br />
else {<br />
&nbsp;&nbsp;/* do something else */<br />
&nbsp;&nbsp;drupal_set_message(t(AUTO_ERROR));<br />
}
</p>
</blockquote>
<p>For now, for want of a better name, the module is called &#8220;pamb.&#8221; And [edit] the code is now <a href="http://sourceforge.net/projects/drupal-pamb/" >available on Sourceforge</a> under the <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPL</a>: that should make it compatible with <a href="http://drupal.org/LICENSE.txt">Drupal&#8217;s core licence</a>. </p>
<ol>
<li><a href="http://sourceforge.net/svn/?group_id=228839" >Download pamb</a>: currently only available in subversion, but clients exist for Linux, Windows and OSX.</li>
<li>Unpack it into your <code>modules</code> directory</li>
<li>Edit the defines at the top of the file for your own Postcode Anywhere or MailBuild/CreateSend accounts.</li>
<li>Stick the <code>pamb_soap.tpl.php</code> in your theme directory</li>
<li>Switch on the module in the admin interface</li>
</ol>
<p>You&#8217;re now ready to start developing with its handful of functions. Feedback more than welcome, as given Drupal&#8217;s minimal support for mailing-list functionality in core it&#8217;d be good to make this into a fully-functional module for similar third-party contact services, and even port it to 6.x.</p>
<p>[edit 2008-06-17: now hosted on Sourceforge]</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/05/22/postcode-anywhere-and-mailbuild-integration-with-drupal/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
