<?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:rssdatehelper="urn:rssdatehelper"><channel><title>Benjamin Howarth, Code Gecko</title><link>http://www.benjaminhowarth.com</link><pubDate>2012-02-29T15:09:24</pubDate><generator>Blog4Umbraco v3.0.0</generator><description>Test</description><language>en</language><item><title>Responsive web design: a response</title><link>http://www.benjaminhowarth.com/blog/2012/02/29/responsive-web-design-a-response</link><pubDate>Wed, 29 Feb 2012 13:59:36 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2012/02/29/responsive-web-design-a-response</guid><description><![CDATA[ 
<p>Earlier today, <a
href="http://blogs.msdn.com/b/thebeebs/archive/2012/02/28/responsive-web-design-in-3-minutes-42-seconds.aspx">
Martin Beeby, a developer evangelist over at Microsoft</a>, posted
a blog about how responsive web design is the solution to
developing rich web experiences across a multitude of platforms,
and how designers are probably thinking about "just" desktop
(browser) and mobile (cellphone), and need to think more in terms
of other devices - iPads, interactive TVs, and so on.</p>

<p>He mentions three staples that <a
href="http://ethanmarcotte.com/">Ethan Marcotte</a> (the guy who
coined the term "responsive design") <a
href="http://www.abookapart.com/products/responsive-web-design">wrote
about in his book</a>, coincidentally titled "Responsive Web
Design" (ingenuitive, huh?). Of those three, one is media queries -
the @media directive <a
href="http://www.w3.org/TR/2002/CR-css3-mediaqueries-20020708/">that's
been around in CSS2</a>, but has only now been started to touted as
"CSS3" in recent years, with improved browser support for window
size detection.</p>

<p>Now, I find @media queries useful and great when you're building
CSS to target multiple devices - it allows you to set out base
styles that enforce brand consistency across your design, and then
simply adjust positioning for various devices - I get the concept,
and I think it's a great idea. However, there's a big caveat about
@media queries which gives me a particularly large cause for
concern.</p>

<p>I run a business which deals with a lot of small customers - and
having patched Umbraco to work in cheap hosting, I recognise the
commercial aspects of people being on a <strong>very</strong> tight
budget - I was there, after all, it's what prompted my
patches.<br />
 However, throughout the design and build process of a website,
there needs to be consideration for this very simple fact -
<strong>bandwidth is fast becoming expensive and a limited
resource</strong>.</p>

<p>I recently quit Vodafone because they removed my 5GB data
bolt-on without telling me, and then all they offered as a
replacement was a 2GB one (3GB if you have an iPad). Most domestic
broadband providers cap usage, or have a "fair use" policy which
constrains to an average of 40GB - when I Google'd, only four
companies had <strong>truly</strong> "unlimited" packages (and some
of these are also documented to use traffic filtering at peak
periods - Virgin's XXL Package, for example). <a
href="http://www.websiteoptimization.com/speed/tweak/average-web-page/">
The average page size of Alexa's top 500, as of May '11, is fast
approaching 700kb</a>, and the one bit that people who tout @media
as the answer to everything don't tell you is: <strong>your entire
stylesheet</strong>, with resources, images, the works - for
<strong>all</strong> viewports -&nbsp;is loaded, before the @media
stuff is parsed by the browser.<br />
<br />
 Yes, you read correctly - <strong>everything</strong> gets loaded,
regardless of the device you're looking at a page on, prior to
@media being parsed by the browser. This means if you're on a
mobile device which should be served up maybe 50kb of page and
image, you'll get all 700kb of the desktop page too. Conversely, if
you're on a desktop, you'll get 50kb of unnecessary mobile stuff
downloaded for no good reason. Let's quickly work out how this
breaks down:</p>

<ol>
<li>Mobile with 2GB data bolt-on - means you can browse
approximately 2000 ~700kb pages before you cap out at your limit.
That's less than ten a day for a month. Add in the fact that if you
own a Smartphone, you can kiss goodbye to several MB a day if you
have Twitter, Facebook, Foursquare, etc. all hooked up and running
in the background.</li>

<li>Home broadband package with 40GB allowance (which is the top
end of most broadband companies, with exceptions already noted):
you can browse ~60,000 pages a month, or ~200 a day. It is a
drastic improvement by 20-fold, but homes now normally have more
than one PC, and if your kids are Youtubing, or you're using
something like BBC iPlayer/ITV Player/4OD, then you're going to cut
into that limit pretty quickly.</li>
</ol>

<p>These figures don't take into account caching, which the link
above notes cuts back on average response sizes of the top 500 home
pages from &gt;500kb to &lt;100kb - an 80+% saving. However,
cellphone and portable devices, whilst improving, are notorious for
having pretty crappy memory capabilities, and caching takes time to
implement in a robust policy (since I build on <a
href="http://www.umbraco.com/">Umbraco</a>, I of course am biased
to recommend using the included <a
href="http://clientdependency.codeplex.com/">ClientDependency</a>
library, originally built by <a
href="http://aaron-powell.com/">Aaron Powell at Redify</a> and <a
href="http://shazwazza.com/">Shannon Deminick at Umbraco HQ</a>,
both formerly of <a
href="http://www.thefarmdigital.com.au/">TheFARM Digital in
Oz</a>).</p>

<p>The main issue here is that on your mobile, if you're browsing a
site that uses responsive desgn to handle multiple devices, you're
wasting in excess of 80%, maybe even 90% of <strong>your visitor's
bandwidth</strong> just for them to view your site. For desktop
viewers, it's more like 10% - but still nothing short of bad
practice. Bandwidth is money - both for your customer, and for
whoever's hosting the site.</p>

<p>I personally built the new <a
href="http://www.kingsmillbread.com/">Kingsmill Bread website</a>
before Christmas for CMW London, with two sites - one for <a
href="http://www.kingsmillbread.com/">full desktop and iPads</a>,
and <a href="http://m.kingsmillbread.com/">one specifically for
cellphones</a> (if you're on a cellphone, tough luck, we do
user-agent checking to redirect you to the mobile one
automatically. Sorry!).&nbsp;If they want to expand that out in
future to support TVs, tablets and netbooks specifically, great -
we know that the site is fully performance-optimised for each
platform already to save them and their visitors money on
bandwidth.</p>

<p>Responsive design has a place, but it's by no means a
one-size-fits-all solution. Remember that in an
ever-increasingly-connected world, bytes are money, and it's your
responsibility to save them to deliver value-for-money in your
solutions.</p>
]]></description></item><item><title>Multi-discipline</title><link>http://www.benjaminhowarth.com/blog/2011/10/22/multi-discipline</link><pubDate>Sat, 22 Oct 2011 08:51:37 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2011/10/22/multi-discipline</guid><description><![CDATA[ 
<p>I've been having some great conversations with a few developer
friends lately, as we all hate having to "re-pave" our machines
every time we upgrade hardware, or our laptop dies and we have to
sort out a replacement so we can continue to work.</p>

<p>People think that becoming a developer is all about code, and in
some cases, that's true. I think, though, that being a developer
means solving the problem at hand - whatever the technology,
language, or platform. A lot of us are purely focused on our
specialism - be that .NET, webforms/MVC, front-end
HTML5/CSS3/jQuery, etc. What about the metal behind it? All that
code that makes what you write, possible - the operating system,
web server, network protocols, and stuff that just makes your brain
crackel with excitement? OK, maybe that's just me...</p>

<p>Anyway. Developers hate repaving machines. Some of you probably
have IT departments for doing that kind of stuff, but for the lone
freelancer, one dead hard-disk (as happened to mine earlier this
week) means re-installing everything afresh on a new disk. And
there are ways of making this stuff quicker, if it happens to you
regularly (disclaimer: I'm on laptop number 7 since March 2008, but
six of those have been in the last 2 years...).</p>

<p>Planning for failure is something every developer should do, and
that includes your hardware failing too. Here's a quick
step-by-step plan on how to make sure you can minimize that
downtime.</p>

<ol>
<li>Source control. If you don't know what it is, find a shotgun
and a mirror... wait, I'm kidding.<br />
<br />
 Seriously though, if you're writing code without source control,
it's like sleeping with your girlfriend, without contraception, a
week before her period, drunk <strong>and</strong> blindfolded. One
slip, and something goes wrong, and you don't remember how the hell
it happened, or how you can fix it. And man, your head hurts.<br />
<br />
 Get source control. Anything except Visual SourceSafe will do, as
long as you can commit changes to a <strong>remote server</strong>
via HTTP/S. I personally use Subversion, but I'm starting to lean
towards using Git, as my hosted project provider has support for
both. This way, anything goes wrong, not a big deal, you have the
entire history of everything you changed and you can re-download it
on demand. <a href="http://www.assembla.com/">Assembla</a> (my old
provider) offers free private spaces up to 1GB in size. If you're
doing it professionally, pay for pro tools - I use <a
href="http://www.codespaces.com/">CodeSpaces</a>' <a
href="http://www.codespaces.com/pricing">$29.99/mo plan</a>, for up
to 10 users &amp; unlimited projects, and I pay for it every 6
months in bulk. I get project, task, user, time and code management
- it's the best hosted provider I've used by far (with the
exception of their API, but that's for another post).</li>

<li>Get an external hard disk, and back up to it weekly.</li>

<li>Image your hard disk with all your apps pre-installed. This is
a tricky one, and the main meat of this blog post. I'm also using
Windows 7, so YMMV with different operating systems.</li>
</ol>

<p><a
href="http://technet.microsoft.com/en-us/windows/ee530017">Here's a
really sweet video walkthrough</a> on how to generalise, and then
capture, a full system image, including installed applications
(basically, go nuts with installing your copies of Visual Studio,
SQL Server, Office, etc. etc.), and store this in a .WIM file,
which can then be used to either re-deploy to other systems (IT
admins, this one's for every time you hire a new dev team member),
or used to re-image your own system (i.e. your hard disk just died,
like mine did last week).</p>

<p>If you just arrived here because your hard disk frazzled at 2am
last night, then my next post is a step-by-step walkthrough on how
to restore the image that (hopefully) you created from watching the
above video.</p>

<p>Watch this space!</p>
]]></description></item><item><title>Windows 8 previewed at BUILD Conference, Anaheim, California</title><link>http://www.benjaminhowarth.com/blog/2011/9/13/windows-8-previewed-at-build-conference,-anaheim,-california</link><pubDate>Tue, 13 Sep 2011 11:06:12 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2011/9/13/windows-8-previewed-at-build-conference,-anaheim,-california</guid><description><![CDATA[ 
<p>So Microsoft's BUILD conference kicked off today with the
keynote presented by Steven Sinofsky, VP of Windows and Windows
Live.</p>

<p><img src="/media/4189/event_buildfulllogo_web.jpg" width="300" height="108" alt="BUILD logo" style="float: right; clear: none;"/></p>

<p>As a developer by trade, I was excited as heck by the keynote,
and I'm frustrated as anything that I can't be there in person, to
get my hands on the Windows 8 tablets they're (allegedly) handing
out to attendees.<br />
 However, compared to keynotes by Google, Apple and all the other
giant multi-billion tech conglomerates, I was disappointed by the
lack of consumer focus. Microsoft wants people to upgrade to
Windows 8, they still have to sell to consumers. I appreciate their
value and their money is in pitching to developers who can add
value to the platform, but Apple comes out with devices "that just
work". I want to see more of that "it just works" stuff - I want to
see someone doing music playback, watching movies, writing school
essays, business reports, connecting to Wifi - all the stuff a
normal domestic user does.</p>

<p>Now that my big gripe is out of the way - WOWZERS. Windows 8
strikes me as a developer's dream. Here's a small selection of the
(pretty awesome) feature list from the first hour of the
keynote:</p>

<ul>
<li>Everything from Windows 7, runs on Windows 8;</li>

<li>Visual Studio 11 for writing Windows 8 apps, can handle both
native HTML5/JS apps, Silverlight, and Winforms/WPF - and pass code
back and forth between it and Expression Blend for making your apps
both beautiful and functional;</li>

<li>30% lower memory footprint than Windows 7 out-of-the-box (404MB
compared to 280~ish MB in the demo);</li>

<li>Windows APIs now support pushing services from apps
<strong>back into the native APIs</strong> for consuming from other
apps;</li>

<li>Support for 256TB hard disks... (that's about a quarter of
Google's index size);</li>

<li>A new Windows 8 App Store, which is <strong>optional</strong>
for developers to use - no more Apple- or Google-style monopolistic
control of the marketplace;</li>

<li>3 lines of code to hook up to device-centric sensors
(accelerometers, webcams, and so on).</li>
</ul>

<p>&nbsp;</p>

<p>So what does this actually mean for developers?</p>

<p>Well... I can write an HTML5/JS app, and it'll run natively on
the desktop, be consumed by the desktop's other apps, but can also
run standalone on my mobile phone, my tablet, my Linux PC, my Mac
PC (if you're so inclined... boo hiss). I can write a Silverlight
app and take full advantage of XNA on the desktop, XBox and Windows
Phone.</p>

<p>Windows has finally recognised that interoperability between
hardware platforms and applications is a must to stay ahead. This
is the most cohesive effort I've seen from them, Apple or Google to
unify the entire platform offering, giving developers (and their
employers) maximum freedom to experiment, innovate, and
inspire.</p>

<p>The Windows 8 developer preview <a href="http://dev.windows.com"
target="_blank" title="Windows 8 developer preview">will be
available at 8pm PST</a>. I'm tempted to go to bed early and get up
at 4am local time just to download it.</p>

<p>The keynote's still going on at the time of posting, so I might
be back to update it with more cool stuff later as it's
announced.</p>

<p>Benjamin</p>
]]></description></item><item><title>The &lt;xsl:key /&gt; element (and why it rocks)</title><link>http://www.benjaminhowarth.com/blog/2011/3/20/the-xsl-key-element-and-why-it-rocks</link><pubDate>Sun, 20 Mar 2011 01:14:10 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2011/3/20/the-xsl-key-element-and-why-it-rocks</guid><description><![CDATA[ 
<p>XSLT is fantastic for simple data structure queries - like WHERE
and ORDER BY clauses. But what about the XSLT equivalent of
DISTINCT?</p>

<p>Fortunately, there's a great answer to that - &lt;xsl:key
/&gt;.</p>

<p>Why would you want to use it? Well, I've been working on
Blog4Umbraco a lot lately, and I've implemented custom
datefoldering strategies. Now, that's great for flexibility on SEO
and human-readable URLs, but if I don't have date folders set up,
then the Blog Archive and Full Archive macros break, cause they
rely on the unique nature of datefoldering to work - by iterating
over the year folder in yyyy format, then month in mm format, then
date in dd format, then the posts themselves - four levels of
nested for-each's.<br />
 I would be using the Exslt.ExsltSets:distinct function to retrieve
all postDate values of all grandchildren, were it not broken with a
nice YSOD stacktrace in Umbraco 4.5.2.</p>

<p>So the question is: how do we retrieve every post, and then tell
XSLT only to iterate over the *unique* values, without resorting to
.NET? Answer: &lt;xsl:key /&gt;.</p>

<p>The &lt;xsl:key /&gt; element is designed to generate unique IDs
in-memory for matching nodes based on their properties.<br />
 In this instance I used:</p>

<div
style="text-align: center; font-family: arial, helvetica, sans-serif; color: #ffffff; font-size: 0.7em;">
XML, using GeSHi 1.0.8.8</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span><span>&lt;xsl:key</span>
<span>name</span>=<span>"ym"</span></span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span><span>match</span>=<span>"*[(@isDoc or name()='node') and
BlogLibrary:IsType('BlogPost', @id)]"</span></span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span><span>use</span>=<span>"umbraco.library:FormatDateTime(./postDate,
'MMMM yyyy')"</span> <span>/&gt;</span></span></div>

<div
style="text-align: center; font-family: arial, helvetica, sans-serif; color: #ffffff; font-size: 0.7em;">
Parsed in 0.001 seconds at 219.43 KB/s</div>

<p>(P.S. main reason for using (@isDoc or name()='node') is
compatibility between Umbraco &lt; 4.0 and 4.5+ schemas - it'll
work with both!)</p>

<p>So now I have a key called "ym", which will be created for every
Umbraco content node, where the key's value for that node will be
calculated from the <strong>use</strong> attribute.</p>

<p>This now means that I can do:</p>

<div
style="text-align: center; font-family: arial, helvetica, sans-serif; color: #ffffff; font-size: 0.7em;">
XML, using GeSHi 1.0.8.8</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span><span>&lt;xsl:if</span>
<span>test</span>=<span>"generate-id()=generate-id(key('ym',umbraco.library:FormatDateTime(./postDate,
'MMMM yyyy')))"</span><span>&gt;</span></span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
...</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span><span>&lt;/xsl:if<span>&gt;</span></span></span></div>

<div
style="text-align: center; font-family: arial, helvetica, sans-serif; color: #ffffff; font-size: 0.7em;">
Parsed in 0.001 seconds at 145.70 KB/s</div>

<p>This does a few things:</p>

<ol>
<li>Calls generate-id() for the given node, which creates an
ID;</li>

<li>Calls generate-id() for any node where the "ym" key matches the
given expression - in this case, passing in the current node's
postDate for evaluation;</li>

<li>Returns true if the two IDs match.</li>
</ol>

<p>If it matches, great, we render results.</p>

<p>Hope this is useful!</p>
]]></description></item><item><title>Happy birthday to me... bye-bye spam</title><link>http://www.benjaminhowarth.com/blog/2011/3/14/happy-birthday-to-me-bye-bye-spam</link><pubDate>Tue, 15 Mar 2011 22:19:06 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2011/3/14/happy-birthday-to-me-bye-bye-spam</guid><description><![CDATA[ 
<p>As a lot of you know, I've been busy <a
href="http://blog4umbraco.codeplex.com/SourceControl/list/changesets"
 target="_blank"
title="Source control changesets for Blog4Umbraco">refactoring</a>
<a href="http://blog4umbraco.codeplex.com/" target="_blank"
title="Blog4Umbraco">Blog4Umbraco</a> over the last few weeks and
months, trying to drastically improve it's fortunes to support lots
of new features.</p>

<p>One of the things that has bugged me is that <a
href="http://akismet.com/">Akismet</a>, the included comment spam
protection service, is only licensed for non-commercial usage, and
if you want to run a "commercial" blog (however you want to define
that), then you have to pay them some money. Now, that's all well
and good, but as a true Socialist (I vote <a
href="http://www.duncanhames.org.uk/" target="_blank"
title="Duncan Hames, MP - Liberal Democrat for Chippenham constituency">
Lib Dem</a>, for anyone who wants to know) I believe that certain
basics in life should either be free or be at substantially reduced
cost - healthcare, education, <a href="http://free.avg.com"
target="_blank"
title="Best free anti-virus software on the market - AVG Free">anti-virus
software</a>, <a href="http://www.ubuntu.com/"
title="Ubuntu - free Linux-based operating system">operating
systems</a>, <a href="http://www.umbraco.com" target="_blank"
title="Umbraco - open-source and .NET, the world's friendliest CMS">
CMS software</a>... OK mostly software. I think spam-fighting kit,
with 87% of the Internet's traffic being made up of spam in one
form or another, is now a basic essential, and the right of every
good Internet citizen to possess at the very least basic security
to guard against it.<br />
 Anyway, I've found Akismet's "commercial" clause a little wooly in
the past, as a site that isn't e-commerce, nor has adverts, may
still be run by a company (hey, we're putting a blog on the new <a
href="http://www.codegecko.net" target="_blank"
title="Code Gecko, Umbraco Certified Developers and WebsiteSpark technical marketing agency">
Code Gecko</a> site), and therefore is techncially "commercial" in
nature. Where does one draw the line? Answer: by making it free -
as in both free speech <strong>and</strong> free beer.</p>

<p>So on this note, I decided to hunt out some free blog comment
spam systems that would gladly allow commercial use, and found two
of particular note:</p>

<ul>
<li><a href="http://blogspam.net" target="_blank">BlogSpam.net</a>
- a nice open-source option, with a hosted service for those who
don't want to run their own service, written by <a
href="http://steve.org.uk/" target="_blank">Steve Kemp</a>. I
reckon this'd be ideal for high-traffic sites, cause it gives you
the option of running your own, which you can then
performance-tweak as and when you want. 

<ul>
<li>Pros - no API keys;</li>

<li>Cons - few .NET wrappers (see below).</li>
</ul>
</li>

<li><a href="http://antispam.typepad.com/" target="_blank"
title="TypePad AntiSpam">TypePad AntiSpam</a> by the guys from <a
href="http://www.sixapart.com" target="_blank"
title="Six Apart">Six Apart</a>. These are the same guys that
introduced the concept of <a
href="http://www.sixapart.com/pronet/docs/trackback_spec"
target="_blank"
title="TrackBack official specification">TrackBacks</a>, letting
bloggers "ping" each other instead of just pinging blog aggregate
services - an implementation of this is coming in Blog4Umbraco v3.
Their implementation is great cause it's the identical API to
Akismet, but with the "commercial use allowed" clause - great. 

<ul>
<li>Pros - Can use <a href="http://akismetapi.codeplex.com/"
target="_blank" title="Joel.Net's Akismet API">Joel.Net's Akismet
API</a> library with a simple modification;</li>

<li>Cons - said library requires recompilation, it's a hosted
service, and requires <a
href="http://antispam.typepad.com/info/get-api-key.html"
target="_blank" title="Get a TypePad AntiSpam API key">API
keys</a>.</li>
</ul>
</li>
</ul>

<p>Now, both of these are lovely, but there's no .NET wrappers for
either of them. And as <a href="http://www.hanselman.com/"
target="_blank" title="Scott Hanselman's Computer Zen">Scott
Hanselman</a> and <a
href="http://en.wikiquote.org/wiki/Linus_Torvalds" target="_blank"
title="Talk is cheap. Show me the code - Linus Torvalds">Linus
Torvalds</a> say, "talk is cheap, show me the code".</p>

<p><a href="http://blogspamnetapi.codeplex.com/releases/view/62472"
target="_blank" title="BlogSpam.net API library v1.0">BlogSpam.net
API wrapper</a></p>

<p><a href="/media/2238/codegecko.typepad.antispam.zip">Joel.Net's
Akismet wrapper for TypePad AntiSpam</a></p>

<p>The BlogSpam.net API wrapper is <a
href="http://blogspamnetapi.codeplex.com/license" target="_blank"
title="MIT X11 license">MIT-licensed</a>, as is pretty much all my
open-source stuff.<br />
 Joel.Net's Akismet wrapper is 3-clause-BSD-licensed, so I can't
take credit for anything except one line of code which <a
href="http://antispam.typepad.com/info/developers.html"
target="_blank">changes the API URL from api.akismet.com to
api.antispam.typepad.com - look Ma, no hands</a>.</p>

<p>I gladly released this on March 14th - aka International Pi Day
(3/14... <strong>*groan*</strong>), and the birthday of Albert
Einstein (14/3/1879, 132nd anniversary), Quincy Jones Jr. and Sir
Michael Caine (both 14/3/1933, aged 78), Jerry Greenfield of Ben
&amp; Jerry's (14/3/1951, aged 60), and yours truly (14/3/1987,
aged 24).</p>

<p>Happy birthday to me, and enjoy!</p>
]]></description></item><item><title>Fixing the "No document exists with Version '00000000-0000-0000-0000-000000000000'", once and for all</title><link>http://www.benjaminhowarth.com/blog/2011/2/23/fixing-the-no-document-exists-with-version-once-and-for-all</link><pubDate>Sun, 27 Feb 2011 00:27:47 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2011/2/23/fixing-the-no-document-exists-with-version-once-and-for-all</guid><description><![CDATA[ 
<p>I had a very frustrating situation the other day, whereupon
trying to delete nearly 300 nodes resulted in this pleasant error
when trying to empty the Content Recycle Bin in an Umbraco
instance.</p>

<p>After reading <a href="http://blog.degree.no/bloggere/">Njål
Gjermundshaug's</a> <a
href="http://blog.degree.no/2011/02/problems-with-recycle-bin-in-umbraco-no-document-exists-with-version-00000000-0000-0000-0000-000000000000/">
post on his attempts</a>, I came across a few more SQL errors. So,
in an attempt to finally fix it, once and for all, I came up with a
SQL cursor.</p>

<p>For those of you that aren't familiar with SQL cursors, they're
basically "for-each" loops in SQL. Really nifty, but can be a pain
to get right when they get more complex than single-statement
commands.</p>

<p>As the greats (well, Hanselman, Torvalds and Key at any rate)
say - talk is cheap, show me the code:</p>

<div
style="text-align: center; font-family: arial, helvetica, sans-serif; color: #ffffff; font-size: 0.7em;">
SQL, using GeSHi 1.0.8.8</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
DECLARE @TEMPID INT</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
DECLARE curs CURSOR <span>FOR</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>SELECT</span> umbracoNode<span>.</span>id <span>FROM</span>
umbracoNode<span>,</span> cmsContent <span>WHERE</span> trashed
<span>=</span> <span>1</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
OPEN curs</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
FETCH NEXT <span>FROM</span> curs <span>INTO</span> @TEMPID</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
WHILE @@FETCH_STATUS <span>=</span> <span>0</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
BEGIN</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>-- Delete parents</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsPreviewXml
<span>WHERE</span> nodeID <span>IN</span>
<span>(</span><span>SELECT</span> id <span>FROM</span> umbracoNode
<span>WHERE</span> parentID <span>=</span>
@TEMPID<span>)</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsContentVersion
<span>WHERE</span> ContentId <span>IN</span>
<span>(</span><span>SELECT</span> id <span>FROM</span> umbracoNode
<span>WHERE</span> parentID <span>=</span>
@TEMPID<span>)</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsContentXml
<span>WHERE</span> nodeID <span>IN</span>
<span>(</span><span>SELECT</span> id <span>FROM</span> umbracoNode
<span>WHERE</span> parentID <span>=</span>
@TEMPID<span>)</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsDocument
<span>WHERE</span> nodeID <span>IN</span>
<span>(</span><span>SELECT</span> id <span>FROM</span> umbracoNode
<span>WHERE</span> parentID <span>=</span>
@TEMPID<span>)</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsPropertyData
<span>WHERE</span> contentNodeId <span>IN</span>
<span>(</span><span>SELECT</span> Id <span>FROM</span> umbracoNode
<span>WHERE</span> parentID <span>=</span>
@TEMPID<span>)</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsContent <span>WHERE</span>
nodeId <span>IN</span> <span>(</span><span>SELECT</span> id
<span>FROM</span> umbracoNode <span>WHERE</span> parentID
<span>=</span> @TEMPID<span>)</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsPreviewXml
<span>WHERE</span> nodeID <span>=</span> @TEMPID</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsContentVersion
<span>WHERE</span> ContentId <span>=</span> @TEMPID</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsContentXml
<span>WHERE</span> nodeID <span>=</span> @TEMPID</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsDocument
<span>WHERE</span> nodeID <span>=</span> @TEMPID</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsPropertyData
<span>WHERE</span> contentNodeId <span>=</span> @TEMPID</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>DELETE</span> <span>FROM</span> cmsContent <span>WHERE</span>
nodeId <span>=</span> @TEMPID</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
FETCH NEXT <span>FROM</span> curs <span>INTO</span> @TEMPID</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
END</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
CLOSE curs</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
DEALLOCATE curs</div>

<div
style="text-align: center; font-family: arial, helvetica, sans-serif; color: #ffffff; font-size: 0.7em;">
Parsed in 0.014 seconds at 81.61 KB/s</div>

<p>So there you have it. You can also change the "WHERE trashed =
1" to "WHERE contentType = whateveryourdoctypeidishere" (replace
whateveryourdoctypeidishere with an <strong>integer</strong> value)
to delete all content of a specific type - say, blogposts - oh
wait, crap, wasn't meant to do that.</p>
]]></description></item><item><title>Publish and unpublish dates in Umbraco</title><link>http://www.benjaminhowarth.com/blog/2011/1/7/publish-and-unpublish-dates-in-umbraco</link><pubDate>Sun, 27 Feb 2011 00:30:16 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2011/1/7/publish-and-unpublish-dates-in-umbraco</guid><description><![CDATA[ 
<p>Sometimes the best answers are the simplest.</p>

<p>Umbraco has two really useful little fields - "Publish at" and
"Unpublish at" - that are properties for every node. They're used
for automatically pushing content into (or out of, respectively)
Umbraco's XML cache, in /App_Data/umbraco.config.</p>

<p>However, these properties, unlike a node's created and
last-updated dates (/&lt;node&gt;/@createDate and
/&lt;node&gt;/@updateDate with XSLT), are not stored in the XML
cache itself. So if you want to leverage it with XSLT, you have to
resort to writing two very simple helper methods like so:</p>

<div
style="text-align: center; font-family: arial, helvetica, sans-serif; color: #ffffff; font-size: 0.7em;">
C#, using GeSHi 1.0.8.8</div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>public</span> <span>static</span> <span>string</span>
ReleaseDate<span>(</span><span>int</span> nodeId<span>)</span>
<span>{</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
Document d <span>=</span> <a
href="http://www.google.com/search?q=new+msdn.microsoft.com"><span>new</span></a>
Document<span>(</span>nodeId<span>)</span><span>;</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>return</span>
d<span>.</span><span>ReleaseDate</span><span>.</span><span>ToString</span>
<span>(</span><span>)</span><span>;</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>}</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>public</span> <span>static</span> <span>string</span>
ExpireDate<span>(</span><span>int</span> nodeId<span>)</span>
<span>{</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
Document d <span>=</span> <a
href="http://www.google.com/search?q=new+msdn.microsoft.com"><span>new</span></a>
Document<span>(</span>nodeId<span>)</span><span>;</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>return</span>
d<span>.</span><span>ExpireDate</span><span>.</span><span>ToString</span>
<span>(</span><span>)</span><span>;</span></div>

<div
style="margin: 0px; font: 1em/1.2em monospace; background: none transparent scroll repeat 0% 0%; vertical-align: top; padding: 0px;">
<span>}</span></div>

<div
style="text-align: center; font-family: arial, helvetica, sans-serif; color: #ffffff; font-size: 0.7em;">
Parsed in 0.009 seconds at 26.45 KB/s</div>

<p>The publish and unpublish dates <strong>will</strong>
automatically clear themselves after the publish/unpublish event is
raised accordingly. So this could be useful for a countdown, say,
"x days until competition opens/closes" or whatever, but not
advised for general usage.</p>

<p>Hope it helps!</p>
]]></description></item><item><title>ASP.NET Social Controls 1.0.0.1 released</title><link>http://www.benjaminhowarth.com/blog/2011/1/5/aspnet-social-controls-1001-released</link><pubDate>Sun, 27 Feb 2011 00:31:23 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2011/1/5/aspnet-social-controls-1001-released</guid><description><![CDATA[ 
<p>After months of hibernating in a digital wonderland, I'm finally
blogging again.</p>

<p>Firstly, Happy New Year if you're reading this!</p>

<p>Secondly, my laptop, HD videocam, and portable hard disk got
stolen in June 09, shortly after the <a
href="http://umbraco.org/">Umbraco</a> <a
href="http://www.codegarden10.com/">CodeGarden10</a> conference.
The thieves, fortunately, didn't know that both the laptop BIOS and
Windows have got passwords... suckers.</p>

<p>Thirdly, I'll videoblog again soon thanks to my wonderful new
Sony Ericsson X1 with HD videocam (thanks Android 2.1!).</p>

<p>Oh, and finally, I've just written <a
href="http://aspnetsocialcontrols.codeplex.com/">an ASP.NET control
called Social Controls</a> which I'd love feedback on.<br />
 It stemmed from wanting to implement social sharing in <a
href="http://blog4umbraco.codeplex.com/">Blog4Umbraco</a>, with
server-side controls that could be configured through the Umbraco
dashboard (more on that later).</p>

<p>Reading through Javascript APIs sucked bigtime - I even wrote to
ShareThis after reading through their actual Javascript
implementation (80k of Javascript = 5k+ lines...
*<strong>shudder</strong>*) cause it's so horrendous.<br />
 Furthermore, ShareThis' documentation is spread over several pages
and it doesn't give you details on which settings are mutually
exclusive and so on. I'm working on my own documentation to detail
this in due course. Lastly, to add insult to injury, not only are
their configuration settings poorly set up, but their
implementation isn't X/HTML-compliant, making use of custom HTML
attributes.</p>

<p>So, to fix this, I wrote ASP.NET Social Controls, with:</p>

<ul>
<li>unobtrusive Javascript to fix ShareThis' faux-pas, without
relying on any third-party Javascript frameworks such as jQuery or
Prototype;</li>

<li>(currently draft) implementation for ConfigurationSections to
store site-wide sharing configurations in web.config (such as
publisher keys, but also for UI settings too);</li>

<li>cascading properties (so global settings can be over-riden per
control instance).</li>
</ul>

<p>Currently the ShareThis control is pre-release/"alpha" status,
and I would appreciate any feedback via <a
href="http://aspnetsocialcontrols.codeplex.com/">Codeplex</a>.</p>

<p>Happy new year!</p>
]]></description></item><item><title>"Upgrading" Windows Server 2008 x86 to x64</title><link>http://www.benjaminhowarth.com/blog/2009/11/25/upgrading-windows-server-2008-x86-to-x64</link><pubDate>Wed, 16 Mar 2011 00:28:32 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2009/11/25/upgrading-windows-server-2008-x86-to-x64</guid><description><![CDATA[ 
<p>OK, I use the term "upgrade" a bit loosely here. There is no
upgrade path from 32-bit to 64-bit versions of any edition of
Windows, period.<br />
 However, you can preserve your Active Directory domain, DNS
settings, websites, files, whatever, through a slightly long-winded
process and get from 32-bit to 64-bit without:</p>

<ol>
<li>Having to uninstall Active Directory from your network and then
piece-meal it back together workstation by workstation;</li>

<li>Having to do system state backup/restores which won't restore
all your other server applications (Exchange, SQL Server
etc.).</li>
</ol>

<p>I'm only running one physical server here - which was Windows
Server 2008 32-bit, and then I realised about 2 hours after
finishing installing, that my CPU was x64 (Intel Xeon 3.06GHz).
Damn.</p>

<p>So to get all the 64-bit shinyness and still preserve my Active
Directory, I figured out the following:</p>

<ol>
<li>Set up a new virtual server ("Bohr") on my laptop.</li>

<li>Clone the existing physical server ("Einstein") hard disk and
mount it on Bohr.</li>

<li>Configure Bohr as an additional domain controller (DC) and
global catalog server (GC).</li>

<li>All my users, computers and settings get replicated from
Einstein to Bohr.</li>

<li>Test all applications, databases and websites are working on
Bohr.</li>

<li>Demote Einstein and remove it from the domain, wipe the hard
disk with Eraser, format, and clean-install Windows Server 2008
x64, with new name "Brunel".</li>

<li>Install Active Directory on Brunel as an additional domain
controller again, and watch the settings replicate back
across.</li>

<li>Move databases and applications back.</li>

<li>Remove virtual server Bohr.</li>
</ol>

<p>It appears a bit long-winded but this is the first time I've had
to think about these kinds of migrations so if anyone can suggest
better alternatives then please do so in the comments.</p>

<p>Tools I used:</p>

<ul>
<li><a
href="http://technet.microsoft.com/en-us/sysinternals/ee656415.aspx">
Disk2VHD</a> by Microsoft's Sysinternals team;</li>

<li><a href="http://www.winimage.com">WinImage</a> (free 30-day
trial);</li>

<li><a href="http://www.vmware.com/products/server/">VMware
Server</a> (called ESX4 I think, but their product family names are
a little confusing);</li>
</ul>

<p>I looked at Virtual PC as I've used it before for virtualising
operating systems and servers whilst working for clients, and I
discovered that Windows 7 (which is what my laptop runs) has
Windows Virtual PC already installed, with an image of Windows XP
Pro for application backward-compatibility.<br />
 At this point I cloned Einstein's hard disk to a VHD file and went
to VPC to create a new machine and mount the VHD as the hard
disk.<br />
 First brick wall - VPC will only let you run XP, Vista, or Windows
7 inside VPC. So I couldn't create a virtual machine running my
cloned Server 2008 (as demonstrated by the lovely error message you
see here). Damn.</p>

<p><a rel="lightbox"
href="http://geekswithblogs.net/images/geekswithblogs_net/TheInspiredGecko/WindowsLiveWriter/UpgradingWindowsServer2008x86tox64_F8F5/blog1_2.png">
<img src="http://geekswithblogs.net/images/geekswithblogs_net/TheInspiredGecko/WindowsLiveWriter/UpgradingWindowsServer2008x86tox64_F8F5/blog1_thumb.png" width="445" height="23" alt="blog1" border="0" style="border-width: 0px; display: inline;"/></a></p>

<p>So I'm driven to the arms of VMware, and after a bit of reading,
I download and install VMware Server. With a bit of configuration,
I soon have my virtual machine.</p>

<p><a rel="lightbox"
href="http://geekswithblogs.net/images/geekswithblogs_net/TheInspiredGecko/WindowsLiveWriter/UpgradingWindowsServer2008x86tox64_F8F5/blog2_2.png">
<img src="http://geekswithblogs.net/images/geekswithblogs_net/TheInspiredGecko/WindowsLiveWriter/UpgradingWindowsServer2008x86tox64_F8F5/blog2_thumb.png" width="549" height="199" alt="blog2" border="0" style="border-width: 0px; display: inline;"/></a></p>

<p>Next brick wall - VHD is a Microsoft format, VMware won't read
it. Damn.<br />
 WinImage to the rescue, which performs conversion from VHD
(Virtual Hard Disk - Microsoft's hard disk virtualisation format)
to VMDK (VMware DisK - their format - gotta love competition for
different "standards", haven't you...) - my 20GB of data takes 36
minutes to convert, apparently.</p>

<p><a rel="lightbox"
href="http://geekswithblogs.net/images/geekswithblogs_net/TheInspiredGecko/WindowsLiveWriter/UpgradingWindowsServer2008x86tox64_F8F5/blog3_2.png">
<img src="http://geekswithblogs.net/images/geekswithblogs_net/TheInspiredGecko/WindowsLiveWriter/UpgradingWindowsServer2008x86tox64_F8F5/blog3_thumb.png" width="354" height="365" alt="blog3" border="0" style="border-width: 0px; display: inline;"/></a></p>

<p>Once I have my VMDK hard disk image, I mount it to the VM (Bohr)
I've created in VMware Server.</p>

<p>Here comes the fun bit where stuff actually gets moved around
and I feel like I'm making progress instead of just clogging up my
hard disk further by downloading and installing yet more software
to achieve the task at hand.</p>

<p>I configure Bohr with a static IP address so it'll sit on my
wireless network and interact with my server, laptop and router as
if it were just another physical box. My laptop is close to death
at this stage cause it only has 2GB of RAM (ouch) and I've
allocated 1GB of it to Bohr (yes, I'm planning on an upgrade as a
Christmas present to myself, along with a <a
href="http://www.hugoboss-store.co.uk/Stratus/50164973,en_GB,pd.html?color=001&amp;size=48&amp;cgid=Menswear-Clothing-Coats">
very nice Hugo Boss winter coat I've seen</a>. Donations and
Christmas cards accepted, preferably together :-D).</p>

<p><a rel="lightbox"
href="http://geekswithblogs.net/images/geekswithblogs_net/TheInspiredGecko/WindowsLiveWriter/UpgradingWindowsServer2008x86tox64_F8F5/taskmanager_2.png">
<img src="http://geekswithblogs.net/images/geekswithblogs_net/TheInspiredGecko/WindowsLiveWriter/UpgradingWindowsServer2008x86tox64_F8F5/taskmanager_thumb.png" width="324" height="361" alt="taskmanager" border="0" style="border-width: 0px; display: inline;"/></a></p>

<p>Once I'd powered on Bohr, I remoted in using VMware's Firefox
browser plugin (RDP was very flaky for some reason and Bohr's
network connection kept bombing) and renamed it using <a
href="http://www.petri.co.il/rename-windows-server-2008-domain-controllers.htm">
netdom and a little help</a>.<br />
 Once this was done I uninstall Active Directory Domain Services
entirely - Bohr can't see Einstein's domain services and so thinks
it's still the only DC on the domain. A reboot later, I reinstall
Active Directory as a new domain controller for the existing
forest, and hey presto, my user and computer accounts and DNS
settings replicate across! Shiny.</p>

<p>To summarise at this stage - I have two DC's on the network, one
physical "Einstein" and one virtual "Bohr", which is just a clone
of Einstein's hard disk, but reconfigured as an additional DC on
the whole domain rather than a primary domain controller.<br />
 I've also just run a quick test on Bohr and my websites and SQL
Server are functioning perfectly, including connecting via
Management Studio using Windows Authentication.<br />
 Lastly, I right-click on the domain in Active Directory Users
&amp; Computers and hit "Properties", then change all domain
control services from Einstein to Bohr, before uninstalling Active
Directory from Einstein.</p>

<p>Further steps to follow, but this is most of the
headache-inducing legwork done!</p>

<p>P.S. Please submit other interesting server names using the
comments box below!</p>

<p>Update: it's 0237 GMT and I'm installing Server 2008 x64 onto
"Brunel" now. W00t. Man I need a smoke. And some sleep. Or maybe
coffee.<br />
 Another update: it's now 0318 GMT and "Brunel" is installed.
Definitely time for sleep.</p>
]]></description></item><item><title>New clients, updated portfolio</title><link>http://www.benjaminhowarth.com/blog/2009/8/27/new-clients,-updated-portfolio</link><pubDate>Wed, 16 Mar 2011 08:24:12 GMT</pubDate><guid>http://www.benjaminhowarth.com/blog/2009/8/27/new-clients,-updated-portfolio</guid><description><![CDATA[ 
<p>Silence can get a bit deafening from time to time so I thought
I'd post up a quick update.</p>

<ul>
<li>New Joomla project launched!<br />
 Having been commissioned to do some "fixes" for a Joomla site,
handover was finally successfully achieved and the site launched
last Wednesday (19th August).<br />
 Unfortunately the site makes use of VirtueMart - a Joomla
e-commerce add-on with not the greatest of admin systems I've ever
seen. and I have another Joomla/VirtueMart project starting this
week! All the fun of the fair, as they say.</li>

<li>New e-commerce site launched!<br />
 <a href="/portfolio.aspx#resolutionrecords">Resolution
Records</a>' site finally went live on the 30th of June after
several weeks of design and re-architecture. With a solid online
marketing campaign, a new and fresh design by award-winning
designer <a href="http://www.needledesigns.co.uk/">Matt Needle</a>,
fully integrated stock management and online credit-card
processing, and a great Flash carousel on the homepage (supplied by
another "<a href="http://www.reinhart-redel.com/codegecko/">code
gecko</a>"), Mark's business is flourishing! Check out the <a
href="/portfolio.aspx">portfolio</a> to see the site in
action.</li>
</ul>

<p>Other news - I'm going to be buying my first ever MacBook soon,
because they can now run Windows thanks to the Intel chipset, which
means I get to run a triple-boot PC (Mac, Windows and Linux) for
all my development needs.</p>

<p>I'm also expanding my skill set into PHP and beginning to look
at WPF, WCF, Flash and Silverlight in more detail.</p>

<p>Migration to Umbraco is slow but steady - I intend to get my
portfolio of DotNetNuke sites onto Umbraco by Christmas with any
luck. If you haven't checked it out already I highly recommend it
due to it's powerful (and insanely fast) XSLT-based template
engine.</p>

<p>Watch this space!</p>
]]></description></item></channel></rss>

