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

<channel>
	<title>Pop Art Blog &#187; Scott Vandehey</title>
	<atom:link href="http://blogs.popart.com/author/scottvandehey/feed/" rel="self" type="application/rss+xml" />
	<link>http://blogs.popart.com</link>
	<description>Flashes of Pop, Wit and Reason</description>
	<lastBuildDate>Wed, 01 Sep 2010 17:18:06 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Google Chrome Frame for Internet&#160;Explorer</title>
		<link>http://blogs.popart.com/2009/09/google-chrome-frame-for-internet-explorer/</link>
		<comments>http://blogs.popart.com/2009/09/google-chrome-frame-for-internet-explorer/#comments</comments>
		<pubDate>Fri, 25 Sep 2009 16:42:22 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[browsers]]></category>
		<category><![CDATA[Chrome]]></category>
		<category><![CDATA[Google]]></category>
		<category><![CDATA[ie]]></category>
		<category><![CDATA[microsoft]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[standards]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=828</guid>
		<description><![CDATA[Long story short, Google released a plugin for IE 6, 7 and 8 that will run Google Chrome (which uses webkit) inside a frame in the IE browser. Now IE6 can be standards-compliant, and all versions of IE get blazing fast javascript and HTML 5 support. Sounds great, but there are some problems, as lifehacker [...]]]></description>
			<content:encoded><![CDATA[<p>Long story short, Google released a plugin for IE 6, 7 and 8 that will run Google Chrome (which uses webkit) inside a frame in the IE browser. Now IE6 can be standards-compliant, and all versions of IE get blazing fast javascript and HTML 5 support. Sounds great, but there are some problems, as <a href="http://lifehacker.com/5365253/google-chrome-frame-turns-internet-explorer-into-a-frankenchrome-browser">lifehacker</a> points out:</p>
<blockquote><p>“The (most) obvious question: Why would I install this plug-in rather than switch browsers to Chrome? The folks at Google point to IT lockdown that won&#8217;t allow users to install a new browser; Ars wonders whether such restrictive IT departments would be any more likely to approve this plug-in. If nothing else, it&#8217;s a pretty bold move on the part of Google.”</p></blockquote>
<p>If you’re interested, <a href="http://jimray.tumblr.com/post/194793633/more-technical-details-about-google-chrome-frame">Jim Ray</a> dug into the details of how it works. Personally, I don&#8217;t think this will solve anyone&#8217;s IE6 problems, but it&#8217;s a fascinating development, and worth keeping an eye on.</p>
<blockquote><p>“The irony here, as I see it, is that an old, insecure feature Microsoft built to try to beat Netscape is now being used by Microsoft’s biggest current rival to patch IE.”</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/09/google-chrome-frame-for-internet-explorer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>IE8 Compatibility Mode and IE7 are Not the Same&#160;Thing</title>
		<link>http://blogs.popart.com/2009/09/ie8-compatibility-mode-and-ie7-are-not-the-same-thing/</link>
		<comments>http://blogs.popart.com/2009/09/ie8-compatibility-mode-and-ie7-are-not-the-same-thing/#comments</comments>
		<pubDate>Tue, 15 Sep 2009 22:32:35 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[browsers]]></category>
		<category><![CDATA[compatibilitymode]]></category>
		<category><![CDATA[expression]]></category>
		<category><![CDATA[ie]]></category>
		<category><![CDATA[microsoft]]></category>
		<category><![CDATA[qa]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=754</guid>
		<description><![CDATA[Just so we&#8217;re clear, testing your website in an actual copy of IE7, and testing in IE8&#8217;s Compatibility Mode are not the same thing. Compatibility Mode does an acceptable job of imitating IE7, and for the average user who&#8217;s just trying to fix a site that looks broken under IE8, it&#8217;s good enough. However, there [...]]]></description>
			<content:encoded><![CDATA[<p>Just so we&#8217;re clear, testing your website in an actual copy of IE7, and testing in IE8&#8217;s Compatibility Mode are not the same thing. Compatibility Mode does an acceptable job of imitating IE7, and for the average user who&#8217;s just trying to fix a site that looks broken under IE8, it&#8217;s good enough. However, there are lots of small differences, and if you&#8217;re only testing your client sites with Compatibility Mode, it could come back to bite you.</p>
<p>On the IE Blog, Tony Ross published a <a href="http://blogs.msdn.com/ie/archive/2009/03/12/site-compatibility-and-ie8.aspx">list of mostly technical differences</a> between the two. Perhaps more useful for web developers is this <a href="http://www.evotech.net/blog/2009/03/ie8-css-support/">article by Estelle Weyl</a> outlining some of the presentation differences between the two, such as border handling and box model differences.</p>
<p>Why does this matter? Because I&#8217;ve heard some otherwise intelligent web developers (including <a href="http://blogs.msdn.com/xweb/archive/2009/03/18/Microsoft-Expression-Web-SuperPreview-for-Windows-Internet-Explorer.aspx">Microsoft&#8217;s Expression Web team</a>, which uses IE8&#8217;s Compatibility Mode for IE7 testing) claim that testing will be much easier now, since you can test everything in one place.</p>
<p>To be sure, tools like Expression Web or the old <a href="http://www.positioniseverything.net/articles/multiIE.html">Stand-Alone IE</a> installers are helpful, but don&#8217;t fool yourself into thinking that they are an accurate representation of a &#8220;clean&#8221; IE6 or IE7 installation. To test against those, you&#8217;ll still need to resort to more thorough measures like keeping separate machines around, or using the <a href="http://www.microsoft.com/Downloads/details.aspx?FamilyID=21eabb90-958f-4b64-b5f1-73d0a413c8ef&#038;displaylang=en">free Virtual PC images for IE6, IE7, and IE8</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/09/ie8-compatibility-mode-and-ie7-are-not-the-same-thing/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>How to Avoid Paragraph Gaps when Using Superscript and&#160;Subscript</title>
		<link>http://blogs.popart.com/2009/09/how-to-avoid-paragraph-gaps-when-using-superscript-and-subscript/</link>
		<comments>http://blogs.popart.com/2009/09/how-to-avoid-paragraph-gaps-when-using-superscript-and-subscript/#comments</comments>
		<pubDate>Thu, 10 Sep 2009 15:00:27 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[howto]]></category>
		<category><![CDATA[layout]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[subscript]]></category>
		<category><![CDATA[superscript]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=749</guid>
		<description><![CDATA[Frequently, when I see a webpage with superscript or subscript text, I see associated gaps in the paragraph. This is caused because the default way browsers render super and subscript text is to add enough vertical space in the paragraph to show them. The result is ugly, but as you can see in the following [...]]]></description>
			<content:encoded><![CDATA[<p>Frequently, when I see a webpage with superscript or subscript text, I see associated gaps in the paragraph. This is caused because the default way browsers render super and subscript text is to add enough vertical space in the paragraph to show them. The result is ugly, but as you can see in the following screenshot, you can easily fix the problem with just a few lines of CSS.</p>
<p><a href="http://www.flickr.com/photos/spaceninja/3905394294/"><img src="http://farm3.static.flickr.com/2424/3905394294_f862faaa8d.jpg" class="photo" alt="HTML Superscript and Subscript Handling" /></a></p>
<p>In the first paragraph, you can see the layout gap problem, and in the second paragraph, you can see the paragraph as it should be displayed, by using the following CSS rules.</p>
<pre class="brush: css;">
sup {
	vertical-align: baseline;
	position: relative;
	bottom: .33em;
}
sub {
	vertical-align: baseline;
	position: relative;
	bottom: -.33em;
}
</pre>
<p>The browser shifts the super and subscript text by using the <code>vertical-align</code> CSS property, which leaves gaps in the paragraph. By resetting this property to the defaul value of <code>baseline</code>, we get rid of the gaps. Then we restore the appearance of the text by using <code>position: relative;</code> and shifting the bottom up or down by <code>.33em</code>. Since this uses ems, you can use these lines in your reset stylesheet, no matter what font treatment you use on your site. Now go forth, and may paragraph gaps never plague you again!</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/09/how-to-avoid-paragraph-gaps-when-using-superscript-and-subscript/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Intelligent Defaults Save&#160;Time</title>
		<link>http://blogs.popart.com/2009/08/intelligent-defaults-save-time/</link>
		<comments>http://blogs.popart.com/2009/08/intelligent-defaults-save-time/#comments</comments>
		<pubDate>Tue, 04 Aug 2009 20:41:42 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Creative]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[apple]]></category>
		<category><![CDATA[coffee]]></category>
		<category><![CDATA[defaults]]></category>
		<category><![CDATA[intelligentdefaults]]></category>
		<category><![CDATA[leopard]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[macintosh]]></category>
		<category><![CDATA[microsoft]]></category>
		<category><![CDATA[osx]]></category>
		<category><![CDATA[tortoisesvn]]></category>
		<category><![CDATA[UI]]></category>
		<category><![CDATA[usability]]></category>
		<category><![CDATA[xbox]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=656</guid>
		<description><![CDATA[Have you ever been a regular at a coffee shop? The barista knows you by name, and every morning when you come by, she&#8217;s already got your Triple Non-Fat Sugar-Free Vanilla Latte waiting for you. That&#8217;s an intelligent default. She doesn&#8217;t know for sure that&#8217;s what you want, or even that you&#8217;ll come in today, [...]]]></description>
			<content:encoded><![CDATA[<p>Have you ever been a regular at a coffee shop? The barista knows you by name, and every morning when you come by, she&#8217;s already got your Triple Non-Fat Sugar-Free Vanilla Latte waiting for you. That&#8217;s an intelligent default. She doesn&#8217;t know for sure that&#8217;s what you want, or even that you&#8217;ll come in today, but you&#8217;ve ordered it enough times before that she&#8217;s confident making it.</p>
<p>Well-written software can have intelligent defaults, too, and you can give your users that same feeling of anticipating their needs. Here are a few examples of programs that found a clever way to save their users&#8217; time with common, repetitive tasks.</p>
<h3>Leopard&#8217;s Smart File Rename&nbsp;Feature</h3>
<p>Traditionally, to rename a file, you click once, and then again in the filename, and then the entire thing is highlighted, including the extension. If you then start typing, the extension is lost, so every time you have to re-highlight just the name portion.</p>
<p>But in Leopard, Apple changed the function to automatically highlight everything <em>but</em> the extension. They recognized that most of the time, you&#8217;re only changing the filename and not the extension. This is a tiny, tiny change, but it saves you a highlight action every time you rename a file.</p>
<h3>TortoiseSVN&#8217;s Auto-Expanding Collapsed&nbsp;Directories</h3>
<p>TortoiseSVN includes a browser to navigate the SVN tree to find the directory you want to check out. Since loading all the directories takes time, when you first open the browser, it just shows you the part of the filetree that you&#8217;re in. To access the full tree, you just collapse the top directory, and then expand it again, at which point the program loads everything else.</p>
<p>A recent version of the program changed the behavior so that when you collapse the top-level directory, they automatically open it right away, and load the full tree. Saving a single click isn&#8217;t a big deal, but it makes you feel like the program is anticipating your needs.</p>
<h3>Xbox 360&#8217;s Annoying Device Selection&nbsp;Prompt</h3>
<p>Okay, now for an example of where an intelligent default is desperately needed. The Xbox 360 has an optional hard drive to save your games to. You can also use a memory unit. As a result, games need to include a prompt asking you whether to save the game to your hard drive or the memory unit. The problem arises when you have only one storage device connected.</p>
<p>If you only have a hard drive, then you only have one option when they ask you where to save your game, but they ask anyways. Here&#8217;s a suggestion for the UI guys developing all those games: <strong>If there&#8217;s only one possible answer to a question, don&#8217;t bother asking!</strong></p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/08/intelligent-defaults-save-time/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Key Takeaways from An Event&#160;Apart</title>
		<link>http://blogs.popart.com/2009/07/key-takeaways-from-an-event-apart/</link>
		<comments>http://blogs.popart.com/2009/07/key-takeaways-from-an-event-apart/#comments</comments>
		<pubDate>Tue, 28 Jul 2009 23:58:01 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Creative]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[aneventapart]]></category>
		<category><![CDATA[conferences]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[ericmeyer]]></category>
		<category><![CDATA[jeffreyzeldman]]></category>
		<category><![CDATA[standards]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=624</guid>
		<description><![CDATA[
I&#8217;ve attended An Event Apart four years running now. It is, hands-down, the finest web conference around, and if you work on the web at all, whether you&#8217;re a designer, developer, copywriter, or client-services, I cannot recommend it highly enough.
Reviewing my notes from previous conferences, I noticed that there were some running themes. Each year, [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.flickr.com/photos/spaceninja/740950682/" title="Zeldman on Choosing Clients by spaceninja, on Flickr"><img src="http://farm2.static.flickr.com/1192/740950682_20e863541e_o.jpg" width="500" height="249" alt="Zeldman on Choosing Clients" /></a></p>
<p>I&#8217;ve attended <a href="http://www.aneventapart.com/">An Event Apart</a> four years running now. It is, hands-down, the finest web conference around, and if you work on the web at all, whether you&#8217;re a designer, developer, copywriter, or client-services, I cannot recommend it highly enough.</p>
<p>Reviewing my notes from previous conferences, I noticed that there were some running themes. Each year, I&#8217;ve come to expect that Jeffrey Zeldman will discuss how to manage relationships with clients who sometimes have little or no respect for what we do. Eric Meyer will talk about a currently hot topic in the CSS arena (usually one he is at the forefront of), and a bevvy of talented designers, copywriters, usability experts and other roles will share their insights.</p>
<p>So, here is my take on the running themes of the conference, as well as my individual key takeaways from each year. Naturally, these reflect my interest in front-end code, so you might have gotten more from different speakers than I did, but I think it will still help give you an idea of what you can expect from An Event Apart.<span id="more-624"></span></p>
<h3>Running&nbsp;Themes:</h3>
<p><strong>Sell ideas, not pixels:</strong> Every year, this message comes up in one form or another. Simply put, if you just show your clients comps, without explaining the reason behind them, they&#8217;ll get hung on little details like &#8220;I don&#8217;t like orange,&#8221; or &#8220;that font is ugly.&#8221; Instead, be clear about the theme behind your comps, and then they will discuss the ideas, like &#8220;The corporate one is too impersonal, but we really liked the human one with all the photos.&#8221;</p>
<p><strong>Avoid bad clients:</strong> After sharing horror stories of clients who were a bad fit for him (or vice-versa), Zeldman always hammers this point &#8212; working with problem clients is rarely worth the effort it takes. Cut them loose and focus on the ones that mesh well with your organization.</p>
<p><strong>Start with the user:</strong> Always strong advocates of user-centered design, this point has come up again and again. At the end of the day, it doesn&#8217;t really matter if the designer and client are happy &#8212; it matters whether the users are happy. If your site doesn&#8217;t start by asking what the user needs, you&#8217;ll have troubles.</p>
<p><strong>Start with the content:</strong> I include this because Zeldman changed his slides around this year, and now says to start with the content and end with the user, but I think the point is ultimately the same. Design should be driven by the content, which should be driven by the user&#8217;s needs.</p>
<h4>Seattle&nbsp;2009</h4>
<ul>
<li>Jeffrey Zeldman says &#8220;Research produces achievable goals and makes you a credible partner.&#8221;</li>
<li>Eric Meyer says &#8220;HTML 5 allows interesting new patterns, but <a href="http://meyerweb.com/eric/thoughts/2009/01/02/an-event-apart-and-html-5/">there are still limitations</a>.&#8221;</li>
<li>Tara Hunt says &#8220;Use your competitor&#8217;s tools until you love them.&#8221;</li>
<li>Kristina Halvorson says &#8220;Repeat after me: I will never again say Launch. Instead, I will say Life Cycle.&#8221;</li>
<li>Dan Cederholm asks if IE6 is the new Netscape 4 (to wild applause). Concludes with a quote from Eric Meyer saying that you can stop supporting IE6 only when <em>your</em> site&#8217;s stats say you can. Stats from other sites are &#8220;worse than useless.&#8221;</li>
</ul>
<h4>San Francisco&nbsp;2008</h4>
<ul>
<li>Zeldman says good web design starts with the user &#8212; focus on their needs, not the client&#8217;s.</li>
<li>Eric Meyer discussed <a href="http://adactio.com/journal/1498">CSS Frameworks</a>, and how they grew out of reset stylesheets. He recommends avoiding other people&#8217;s frameworks except as a jumping-off point. Ideally, you should be creating your own.</li>
<li>Dan Cederholm says &#8220;Progressive Enhancement means really cool stuff that doesn&#8217;t work in IE.&#8221;</li>
<li>Tantek Çelik discussed microformats.</li>
<li>Eric Meyer talked about reset and <a href="http://meyerweb.com/eric/tools/css/diagnostics/index.html">debug stylesheets</a>.</li>
</ul>
<h4>Seattle&nbsp;2007</h4>
<ul>
<li>Eric Meyer illustrated that the default behavior of all HTML elements is defined by the browser, and each browser defines things slightly differently – especially font handling – which is why he advocates the use of a <a href="http://meyerweb.com/eric/tools/css/reset/">reset stylesheet</a>.</li>
<li>Zeldman says that content drives traffic, freshness counts, and language is the main interface on most sites, which is why all sites have large writing and editorial budgets. When everyone laughed, he asked for raised hands if you have a writing budget or a copy czar. When only a few people raised their hands, he said “In conclusion, get a writing budget and hire a copy czar.”</li>
<li>Khoi Vinh discussed <a href="http://www.subtraction.com/2004/12/31/grid-computi">grid-based layouts</a>.</li>
<li>Shaun Inman discussed progressive disclosure of information in <a href="http://haveamint.com/">Mint</a>.</li>
<li>Eric Meyer recommended <a href="http://dean.edwards.name/IE7/">Dean Edwards&#8217; IE7 scripts</a> to make IE6 behave.</li>
</ul>
<h4>Seattle&nbsp;2006</h4>
<ul>
<li>Eric Meyer discussed <a href="http://meyerweb.com/eric/thoughts/2005/12/20/bar-graphs-with-style/">CSS bar graphs</a> and child selectors.</li>
<li>Jeffrey Zeldman says &#8220;The client is not an idiot. This is very important, and I want you all to write this down. To put it another way, Don’t choose clients who are idiots. To put a marketing spin on it, Choose good clients.&#8221;</li>
<li>Jeffrey Zeldman says &#8220;Copy is a precious opportunity to define your brand. It’s also the cheapest and easiest thing on your site to fix, generally.&#8221;</li>
<li>Eric Meyer discussed the <a href="http://www.positioniseverything.net/articles/onetruelayout/">One True Layout</a>, saying &#8220;Alex published this technique last year in October 2005, nine years after the CSS1 rules were published. As far as I can tell, no one else thought of this before him, including me, which is why I hate him.&#8221;</li>
<li>Kello Goto recommended user testing.</li>
</ul>
<blockquote><p>
&#8220;This isn&#8217;t a super-fun conference where you come here to be entertained and then forget everything and get drunk.&#8221;<br />
<small>&#8211; Jeffrey Zeldman, An Event Apart: Seattle &#8216;09</small>
</p></blockquote>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/07/key-takeaways-from-an-event-apart/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to Convert from Community Server 2007 to&#160;Wordpress</title>
		<link>http://blogs.popart.com/2009/07/convert-cs2007-to-wordpress/</link>
		<comments>http://blogs.popart.com/2009/07/convert-cs2007-to-wordpress/#comments</comments>
		<pubDate>Mon, 20 Jul 2009 18:30:35 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[blogs]]></category>
		<category><![CDATA[CommunityServer]]></category>
		<category><![CDATA[cs2007]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=555</guid>
		<description><![CDATA[It&#8217;s safe to say that no one at Pop Art was ever really happy with Community Server. We selected it as a platform for a variety of reasons, some of which turned out to be based on faulty assumptions. Once we finally made the decision to switch to Wordpress, the conversion was a huge pain, [...]]]></description>
			<content:encoded><![CDATA[<p>It&#8217;s safe to say that no one at Pop Art was ever really happy with Community Server. We selected it as a platform for a variety of reasons, some of which turned out to be based on faulty assumptions. Once we finally made the decision to switch to Wordpress, the conversion was a huge pain, but ultimately worth the effort.<span id="more-555"></span></p>
<h3>How We Got Into This&nbsp;Mess</h3>
<p>When I was hired at Pop Art in early 2006, there was no official Pop Art blog. The design team had their own site, which was sporadically updated and not branded as a Pop Art site. Several members of the dev team also had work blogs, but again, they were not branded. As somewhat of a blogging zealot, I pushed hard to standardize these onto one site, and in August, I got the go-ahead to set something up.</p>
<p>When I started looking into solutions, I was working with a few assumptions. First, that the existing blogs would stay up, and the central blog would syndicate their posts by pulling in their RSS feeds. Secondly, despite my strong preference for Wordpress, that it was not a viable option for us. That&#8217;s because we&#8217;re a Microsoft shop, so we don&#8217;t have a lot of PHP experience in-house, and we don&#8217;t have any Apache boxes to host the site on. Also, since I was assuming we wanted multiple blogs syndicated at one domain (rather than a single blog with multiple authors), there wasn&#8217;t a good way to do that with Wordpress at the time (Wordpress MU hadn&#8217;t really taken off at the time).</p>
<p>Given those assumptions, we ended up installing Community Server. I felt good about this because it was the product used to host all the blogs at Microsoft, and appeared to be well-supported and active.</p>
<h3>Increasing Difficulties with Community&nbsp;Server</h3>
<p>However, <a href="/2007/10/skinning-community-server-2007/">installation and customization were a nightmare</a>. After a lengthy struggle to get everything set up correctly, it became increasingly clear that some of our assumptions were inaccurate.</p>
<p>Although we started with independent blogs being syndicated, our CEO eventually wrote a company blogging policy disallowing this, so we ended up with a Community Server site that had 20+ single-author blogs being aggregated onto a single homepage to give the appearance of a standard multiple-author blog. We weren&#8217;t taking advantage of anything the multiple-blog scenario was originally set up to do.</p>
<p>Over time, we found out that Community Server was not only difficult to customize, it was also poorly supported. After we purchased and installed CS2007, they released two further versions, and stopped supporting ours. (When we contacted them about purchasing an updated for our license to add more blogs, we were told our version was no longer supported, and they offered to let us upgrade at a discounted price of $900. We didn&#8217;t purchase that upgrade, and three months later, they announced that they were changing their license structure again, and we would now have to pay full price for a brand new license if we wanted to upgrade.) I know that companies make money by releasing new versions, but it seems crazy that less than two years after we bought their product, they wouldn&#8217;t support us at all without a full upgrade.</p>
<p>To cut a long story short, the straw that finally broke the camel&#8217;s back was when we decided to integrate Twitter into our blogs. We were able to add the javascript twitter script to the sidebar to display the Pop Art feed easily, but in order to add individual twitter feeds to the profile page, we were going to need to bring in the .NET programmers, who are always really busy &#8212; and I realized that if we were using Wordpress, I could do everything myself.</p>
<p>There were other factors, including our frustration with the licensing situation, and the fact that we didn&#8217;t need the overly complicated multiple blog situation anymore, but the customization difficulties were the kicker. Once I was able to assure our CEO that switching to Wordpress wouldn&#8217;t lead to a repeat of this situation in a few years, we had the green light to make the switch.</p>
<h3>Exporting Posts from Community&nbsp;Server</h3>
<p>Getting our posts from Community Server to Wordpress was our first hurdle. Out of the box, there&#8217;s no way to make it work, other than having Wordpress scrape the RSS feed &#8212; and that would lose all the comments. Thankfully, other people had already done the legwork for me. Bear with me here.</p>
<p><a href="http://www.aaronlerch.com/blog/2007/08/23/breaking-up-moving-blog-engines/">Aaron Lerch</a> wrote a great post in August 2007 about switching from Blogger to Wordpress using BlogML. I already knew from when we originally set it up that <a href="http://blogs.popart.com/2006/08/community-server-is-grrrreat/">Community Server supports BlogML</a>, and Aaron wrote a module for Wordpress to import from BlogML format.</p>
<p>Then, in October 2007, <a href="http://www.softwarebyrob.com/2007/10/05/a-tale-of-moving-blog-engines-community-server-to-wordpress/">Rob Walling</a> wrote a post about switching from Community Server 2007 to Wordpress. He updated Aaron&#8217;s Wordpress importer because Wordpress 2.3 switched from categories to tags.</p>
<p>Finally, in October 2008, <a href="http://www.kavinda.net/2008/10/23/migrating-from-dasblog-to-wordpress.html">Kavinda Munasinghe</a> wrote a post about switching from DasBlog to Wordpress, and once again, updated Aaron&#8217;s Wordpress importer.</p>
<p>So, I grabbed the <a href="http://nayyeri.net/blog/community-server-2007-blogml-converter/">BlogML Export module for Community Server 2007</a>, and Kavinda&#8217;s updated <a href="http://www.kavinda.net/2008/10/23/migrating-from-dasblog-to-wordpress.html">BlogML Importer for Wordpress</a>, and set about my business.</p>
<h3>Importing Posts to&nbsp;Wordpress</h3>
<p>The first problem I ran into is that I couldn&#8217;t get one big export file of every blog in our Community Server installation, I had to create an export file for each one.</p>
<p>The second problem I ran into is that my exports didn&#8217;t import correctly. Now, I have no idea if the problem was from one of the versions of the importer, or if the exporter wasn&#8217;t working correctly, or even if the latest version of Wordpress just has a slightly different database. What it boiled down to is that I had to manually reformat each of my 32 export files.</p>
<p>The biggest issue was with categories/tags. First of all, all my tags were being imported as categories. I was willing to ignore this, since Wordpress has a &#8220;convert categories to tags&#8221; feature, but it was still annoying. In addition to that, however, all the categories were importing as numbers. Turns out that Wordpress was grabbing the category ID instead of the category name.</p>
<p>I looked at both the importer and the exporter, but I didn&#8217;t have the programming know-how to fix it, so ultimately, I just wrote a simple regular expression to reformat all the categories so that their names were in the ID attribute so they would import correctly.</p>
<p>In addition to that, author names weren&#8217;t importing correctly, draft status posts were being published, and a couple of older blogs where the authors had copy-and-pasted from Word were bringing in a bunch of crazy custom microsoft markup. All in all, I probably spent about 3-4 hours manually cleaning up all my export files. This all sounds like a huge pain (and it was!), but I&#8217;m happy to say that once I was done, everything imported perfectly.</p>
<h3>Redirecting Old&nbsp;Posts</h3>
<p>The final thing I needed to do was set up a 301 redirect for all the old posts. Aaron&#8217;s importer script gives you a file that you can use to build a list of redirects in your .htaccess file, but I was sure there was a better way. After a bit of digging, I found a forum post about <a href="http://www.webmasterworld.com/forum92/6079.htm">using mod_rewrite to change dynamic to static URLs</a> that helped me write the following regular expression to redirect from our old permalinks to our new ones.</p>
<pre><code>RewriteRule
     ^([^/]+)/archive/([^/]+)/([^/]+)/([^/]+)/([^/]+).aspx$
     http://blogs.popart.com/$2/$3/$5 [R=301,L]
</code></pre>
<p>Note that normally, that would all appear on one line. I&#8217;ve broken it up for readability. That looks for urls like <kbd>/scott-vandehey/archive/2006/11/09/post-title.aspx</kbd> and redirects them to <kbd>/2006/11/post-title/</kbd>. With that one rule in our .htaccess file, all our old blogs posts would automatically get a 301 redirect to the new URLs.</p>
<p>Now that we&#8217;ve made the switch, I&#8217;m really happy we spent the time on it. We took the opportunity to redo the design to better match our main site, and the template engine in Wordpress is a real joy to work with &#8212; and well-documented, more importantly! I&#8217;m confident that we&#8217;ll be able to handle the evolving needs of our company in the future, and informally, I can say that everyone seems happier using the new system. The transition was a pain, but the results were worth it.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/07/convert-cs2007-to-wordpress/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Six Wordpress Tips from the Pop Art Blog&#160;Redesign</title>
		<link>http://blogs.popart.com/2009/07/six-wordpress-tips/</link>
		<comments>http://blogs.popart.com/2009/07/six-wordpress-tips/#comments</comments>
		<pubDate>Fri, 10 Jul 2009 00:03:54 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Software]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[atom]]></category>
		<category><![CDATA[excerpts]]></category>
		<category><![CDATA[feeds]]></category>
		<category><![CDATA[gravatars]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[profiles]]></category>
		<category><![CDATA[rss]]></category>
		<category><![CDATA[tips]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=527</guid>
		<description><![CDATA[When we converted the Pop Art Blog to use Wordpress, I learned some clever tricks that I would like to share with you. If you like what we&#8217;ve done around here, you might be interested in some of these techniques for your own site.
Use RSS Excerpts to Strip All&#160;HTML

/*
	just like the_excerpt, but really strips all [...]]]></description>
			<content:encoded><![CDATA[<p>When we converted the Pop Art Blog to use Wordpress, I learned some clever tricks that I would like to share with you. If you like what we&#8217;ve done around here, you might be interested in some of these techniques for your own site.<span id="more-527"></span></p>
<h3>Use RSS Excerpts to Strip All&nbsp;HTML</h3>
<pre class="brush: php;">
/*
	just like the_excerpt, but really strips all the HTML, including BR tags
*/
&lt;p&gt;&lt;?php the_excerpt_rss(); ?&gt;&lt;/p&gt;
</pre>
<p>We wanted to display a post excerpt on most pages. Normally, I would just use wordpress&#8217; <code>the_excerpt</code> function. It automatically displays the post excerpt if it exists, and if not, then it grabs the first 50 words of the post, and even strips the HTML! Sounds great, right? Except for one thing &#8212; it doesn&#8217;t strip line breaks, so instead of a page full of single-paragraph excerpts, I had several posts with two small paragraphs displaying. Not a big deal, but it was messing up our design.</p>
<p>After doing a little digging in the <a href="http://codex.wordpress.org/">wordpress documentation</a>, I found that <code>the_excerpt_rss</code> does exactly what I was looking for. It&#8217;s intended to be used in RSS templates, but you can use it in your regular pages and it works just as well. The only hiccup is that you&#8217;ll need to wrap it in a <code>&lt;p&gt;</code> yourself.</p>
<h3>Use the Nickname Field for Job&nbsp;Titles</h3>
<pre class="brush: php;">
&lt;?php the_author_meta('nickname'); ?&gt;
</pre>
<p>Our design shows the job title under everyone&#8217;s name. There&#8217;s no &#8220;job title&#8221; field in the user accounts, but I was able to repurpose the nickname field. To display it in the site, just use the code shown above in your post templates. Remember, the people looking at your site won&#8217;t know that you&#8217;re using the &#8220;wrong&#8221; database field! (You know, unless you tell them, like I&#8217;m doing.)</p>
<h3>Bit.ly Shortener for Twitter&nbsp;Links</h3>
<pre class="brush: php;">
/*
	Bit.ly URL Shortener
	Automatically shorten a URL using the Bit.ly API
	based on code from http://davidwalsh.name/bitly-php
*/
function make_bitly_url($url) {
	$login = 'YOUR BIT.LY LOGIN';
	$appkey = 'YOUR BIT.LY API KEY';
	$format = 'xml';
	$version = '2.0.1';
	$bitly = 'http://api.bit.ly/shorten?version='.$version.'&amp;longUrl='.urlencode($url).'&amp;login='.$login.'&amp;apiKey='.$appkey.'&amp;format='.$format;
	$response = file_get_contents($bitly);
	//parse depending on desired format
	if(strtolower($format) == 'json') {
		$json = @json_decode($response,true);
		return $json['results'][$url]['shortUrl'];
	} else { //xml
		$xml = simplexml_load_string($response);
		return 'http://bit.ly/'.$xml-&gt;results-&gt;nodeKeyVal-&gt;hash;
	}
}
</pre>
<p>Adding a &#8220;retweet this&#8221; link at the bottom of every post is pretty easy. You just link to twitter.com and pass a &#8220;status&#8221; query string in the URL with whatever you want their tweet to include &#8212; namely, the URL to the post they&#8217;re retweeting. But our URLs can get pretty lengthy, since they include the post title. Obviously, with Twitter&#8217;s 140-character limit, every character counts. To get around this, I found some code by <a href="http://davidwalsh.name/bitly-php">David Walsh</a> to make a <a href="http://bit.ly/">bit.ly</a> short URL. It works great, but you&#8217;ll need to register for an API key on their site to use it. This code goes in your <code>functions.php</code> file, and then you can call it from any post loop like this:</p>
<pre class="brush: php;">
&lt;?php echo make_bitly_url(get_permalink()); ?&gt;
</pre>
<h3>Use a Custom Default&nbsp;Gravatar</h3>
<pre class="brush: php;">
/*
	Add new default avatars
	based on code from http://wpengineer.com/add-avatar-to-wordpress-default/
*/
if ( !function_exists('fb_addgravatar') ) {
	function fb_addgravatar( $avatar_defaults ) {
		$stormtrooper = get_bloginfo('template_directory').'/images/avatar.png';
		$avatar_defaults[$stormtrooper] = 'Storm Trooper';
		return $avatar_defaults;
	}
	add_filter( 'avatar_defaults', 'fb_addgravatar' );
}
</pre>
<p>Wordpress&#8217; built-in support for gravatars in comments is fantastic, but if your commenters don&#8217;t have a gravatar account, the default avatar will be displayed. Wordpress lets you choose between a simple gray silhouette or several dynamically generated icons, none of which really fit our design. Luckily, I found this code that lets you add your own default avatar to the list &#8212; in our case, a lovely photo of a storm trooper. This code goes in your <code>functions.php</code> file, and it adds a new option to the default gravatar list in your admin area.</p>
<h3>Use HTML in User&nbsp;Profiles</h3>
<pre class="brush: php;">
/*
	Turn off HTML filter for user profiles
*/
remove_filter('pre_user_description', 'wp_filter_kses');
</pre>
<p>Our <a href="http://blogs.popart.com/author/scottvandehey/">fancy profile pages</a> use the &#8220;Biographical Info&#8221; field in the user profiles. The only problem is that by default, wordpress strips any HTML from it. You can disable that feature by adding this bit of code to your <code>functions.php</code> file.</p>
<h3>Add More RSS&nbsp;Feeds</h3>
<pre class="brush: php;">
&lt;link rel=&quot;alternate&quot; type=&quot;application/atom+xml&quot; title=&quot;&lt;?php bloginfo('name'); ?&gt; Full Posts&quot; href=&quot;&lt;?php bloginfo('atom_url'); ?&gt;&quot; /&gt;
&lt;?php
	// add comments feed on single-post pages
	if (is_single()) {
		while (have_posts()) : the_post();
			if ('open' == $post-&gt;comment_status) : /* If comments are open */
?&gt;
&lt;link rel=&quot;alternate&quot; type=&quot;application/atom+xml&quot; title=&quot;Comments on &lt;?php the_title(); ?&gt;&quot; href=&quot;&lt;?php bloginfo('url'); ?&gt;/index.php?feed=atom&amp;amp;p=&lt;?php the_ID(); ?&gt;&quot; /&gt;
&lt;?php
			endif;
		endwhile;
		rewind_posts();
	// add category feed on category archives
	} else if (is_category()) {
		$category = get_the_category();
?&gt;
&lt;link rel=&quot;alternate&quot; type=&quot;application/atom+xml&quot; title=&quot;Posts in the &lt;?php echo $category[0]-&gt;cat_name; ?&gt; category&quot; href=&quot;&lt;?php echo get_category_feed_link( $category[0]-&gt;cat_ID, 'atom' ); ?&gt;&quot; /&gt;
&lt;?php
	// add tag feed on tag archives
	} else if (is_tag()) {
?&gt;
&lt;link rel=&quot;alternate&quot; type=&quot;application/atom+xml&quot; title=&quot;Posts tagged with &lt;?php single_tag_title(); ?&gt;&quot; href=&quot;&lt;?php echo get_tag_feed_link( get_query_var('tag_id'), 'atom' ); ?&gt;&quot; /&gt;
&lt;?php
	// add author feed on author pages
	} else if (is_author()) {
		if(isset($_GET['author_name'])) :
		$curauth = get_userdatabylogin($author_name);
		else :
		$curauth = get_userdata(intval($author));
		endif;
		$authorfeedlink = get_author_feed_link( $curauth-&gt;ID, 'atom' );
	?&gt;
&lt;link rel=&quot;alternate&quot; type=&quot;application/atom+xml&quot; title=&quot;Posts by &lt;?php echo $curauth-&gt;display_name; ?&gt;&quot; href=&quot;&lt;?php echo $authorfeedlink; ?&gt;&quot; /&gt;
&lt;?php
	}
?&gt;
</pre>
<p>Finally, one of the best (and relatively unknown) features of wordpress is that you can get an RSS feed for just about every page on the site, just by adding <code>/feed/</code> to the end of the URL. I wanted to take advantage of this feature to add <code>link</code> elements for those special feeds on different pages &#8212; for example, a feed for an author&#8217;s posts on their profile page, or for comments on a post on that post&#8217;s page. The above code is a little convoluted, but it includes all the logic to add the following feeds on the appropriate pages:</p>
<ul>
<li>Complete blog feed on every page</li>
<li>Comments feed on individual post pages</li>
<li>Category feeds on category pages</li>
<li>Tag feeds on tag pages</li>
<li>Author feeds on author profile pages</li>
</ul>
<p>Note that I&#8217;ve set this up to provide Atom feeds, but you can change it by replacing every instance of <code>atom</code> with <code>rss2</code>. This code goes in your <code>header.php</code> file, near the rest of your <code>meta</code> and <code>link</code> elements.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/07/six-wordpress-tips/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>XHTML 2 is&#160;Dead</title>
		<link>http://blogs.popart.com/2009/07/xhtml-2-is-dead/</link>
		<comments>http://blogs.popart.com/2009/07/xhtml-2-is-dead/#comments</comments>
		<pubDate>Thu, 02 Jul 2009 20:50:37 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[w3c]]></category>
		<category><![CDATA[XHTML]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=507</guid>
		<description><![CDATA[Wow, I didn&#8217;t see this coming. Zeldman reports that the W3C is not going to renew the XHTML 2 working group&#8217;s charter this year. That effectively kills XHTML 2 in favor of devoting the resources to the HTML 5 working group.
This makes sense in that HTML 5 is already gaining traction, and we&#8217;ve seen lots [...]]]></description>
			<content:encoded><![CDATA[<p>Wow, I didn&#8217;t see this coming. <a href="http://www.zeldman.com/2009/07/02/xhtml-wtf/">Zeldman reports</a> that the <a href="http://www.w3.org/News/2009#item119">W3C is not going to renew the XHTML 2 working group</a>&#8217;s charter this year. That effectively kills XHTML 2 in favor of devoting the resources to the HTML 5 working group.</p>
<p>This makes sense in that HTML 5 is already gaining traction, and we&#8217;ve seen lots of talk in the web community about switching back to HTML, but I never expected to see development halted like this.</p>
<blockquote><p>
&#8220;W3C hopes to accelerate the progress of HTML 5 and clarify W3C&#8217;s position regarding the future of HTML.&#8221;
</p></blockquote>
<p>Nice to know that the W3C is clearly picking a side and throwing their support behind one product, though.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/07/xhtml-2-is-dead/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>How to Get Your Most Recent Twitter Posts Using PHP with&#160;Caching</title>
		<link>http://blogs.popart.com/2009/07/twitter-php-caching/</link>
		<comments>http://blogs.popart.com/2009/07/twitter-php-caching/#comments</comments>
		<pubDate>Wed, 01 Jul 2009 23:18:45 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Web Development]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[cache]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://blogs.popart.com/?p=492</guid>
		<description><![CDATA[When we started redesigning the Pop Art blog, one of the chief requirements was to integrate everyone&#8217;s Twitter feeds into the site. In addition to the Pop Art Twitter feed in the sidebar, we wanted to add individual twitter feeds on the profile pages. The problem is that the javascript code that Twitter provides can [...]]]></description>
			<content:encoded><![CDATA[<p>When we started redesigning the Pop Art blog, one of the chief requirements was to integrate everyone&#8217;s Twitter feeds into the site. In addition to the Pop Art Twitter feed in the sidebar, we wanted to add individual twitter feeds on the profile pages. The problem is that the javascript code that Twitter provides can only be called once in a single page, or it gets confused.</p>
<p>Since we were switching to Wordpress, I checked out a bunch of Twitter plugins, but ultimately found them all to be unreliable or just missing features. In the end, I hacked together one of my own, based heavily on code by <a href="http://spookyismy.name/old-entries/2009/1/25/latest-twitter-update-with-phprss-part-three.html">Ryan Barr</a>. His PHP script was very nearly perfect, but I ran into three problems.</p>
<p>First, his script echoed out the exact date and time of the tweet, but I wanted the fancy &#8220;2 hours ago&#8221; style dates. To do that, I used a chunk of code from <a href="http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time/501415#501415">Stack Overflow</a>. Now, I&#8217;m far from an advanced PHP programmer, so I&#8217;m sure that this code could be cleaned up and condensed down to something like 12 characters, but I like this because it works, and it&#8217;s very easy for a mid-level programmer like myself to understand.</p>
<pre class="brush: php;">&lt;?php
/*
	Relative Time Function
	based on code from http://stackoverflow.com/questions/11/how-do-i-calculate-relative-time/501415#501415
	For use in the &quot;Parse Twitter Feeds&quot; code below
*/
define(&quot;SECOND&quot;, 1);
define(&quot;MINUTE&quot;, 60 * SECOND);
define(&quot;HOUR&quot;, 60 * MINUTE);
define(&quot;DAY&quot;, 24 * HOUR);
define(&quot;MONTH&quot;, 30 * DAY);
function relativeTime($time)
{
	$delta = strtotime('+2 hours') - $time;
	if ($delta &lt; 2 * MINUTE) {
		return &quot;1 min ago&quot;;
	}
	if ($delta &lt; 45 * MINUTE) {
		return floor($delta / MINUTE) . &quot; min ago&quot;;
	}
	if ($delta &lt; 90 * MINUTE) {
		return &quot;1 hour ago&quot;;
	}
	if ($delta &lt; 24 * HOUR) {
		return floor($delta / HOUR) . &quot; hours ago&quot;;
	}
	if ($delta &lt; 48 * HOUR) {
		return &quot;yesterday&quot;;
	}
	if ($delta &lt; 30 * DAY) {
		return floor($delta / DAY) . &quot; days ago&quot;;
	}
	if ($delta &lt; 12 * MONTH) {
		$months = floor($delta / DAY / 30);
		return $months &lt;= 1 ? &quot;1 month ago&quot; : $months . &quot; months ago&quot;;
	} else {
		$years = floor($delta / DAY / 365);
		return $years &lt;= 1 ? &quot;1 year ago&quot; : $years . &quot; years ago&quot;;
	}
}
?&gt;</pre>
<p>Secondly, and most important, Ryan&#8217;s code didn&#8217;t cache the results. That&#8217;s obviously bad form, but I didn&#8217;t really think about trying to fix it until <a href="http://twitter.com/spaceninja/status/2079700468">Apple&#8217;s WWDC traffic drove Twitter to a standstill</a> while I was testing our new site. Realizing that our Twitter feeds would die anytime anything big happened on the internet motivated me, and I was able to integrate some caching code from <a href="http://wiki.kientran.com/doku.php?id=projects:twitterbadge">Kien Tran</a> and <a href="http://snipplr.com/view/8156/twitter-cache/">Snipplr</a> that meshed well with Ryan&#8217;s existing code.</p>
<p>When I was done, I had a fully functional script that created a cache file for each twitter feed, and only tried to update it every ten minutes. I was especially pleased to discover that it worked when Twitter again ground to a halt due to the Iran elections and Michael Jackson&#8217;s death in the last few weeks.</p>
<p>So here&#8217;s the final code that we&#8217;re using on the Pop Art blog. It looks pretty overwhelming, but it&#8217;s basically broken down into three sections. First, it checks to see if the cache file exists, and whether it needs to be updated. Second, it parses the XML from the cache file to create a series of variables. Finally, it echos out a chunk of HTML for each tweet, or an error message if there aren&#8217;t any.</p>
<pre class="brush: php;">&lt;?php
/*
	Parse Twitter Feeds
	based on code from http://spookyismy.name/old-entries/2009/1/25/latest-twitter-update-with-phprss-part-three.html
	and cache code from http://snipplr.com/view/8156/twitter-cache/
	and other cache code from http://wiki.kientran.com/doku.php?id=projects:twitterbadge
*/
function parse_cache_feed($usernames, $limit) {
	$username_for_feed = str_replace(&quot; &quot;, &quot;+OR+from%3A&quot;, $usernames);
	$feed = &quot;http://search.twitter.com/search.atom?q=from%3A&quot; . $username_for_feed . &quot;&amp;rpp=&quot; . $limit;
	$usernames_for_file = str_replace(&quot; &quot;, &quot;-&quot;, $usernames);
	$cache_file = dirname(__FILE__).'/cache/' . $usernames_for_file . '-twitter-cache';
	$last = filemtime($cache_file);
	$now = time();
	$interval = 600; // ten minutes
	// check the cache file
	if ( !$last || (( $now - $last ) &gt; $interval) ) {
		// cache file doesn't exist, or is old, so refresh it
		$cache_rss = file_get_contents($feed);
		if (!$cache_rss) {
			// we didn't get anything back from twitter
			echo &quot;&lt;!-- ERROR: Twitter feed was blank! Using cache file. --&gt;&quot;;
		} else {
			// we got good results from twitter
			echo &quot;&lt;!-- SUCCESS: Twitter feed used to update cache file --&gt;&quot;;
			$cache_static = fopen($cache_file, 'wb');
			fwrite($cache_static, serialize($cache_rss));
			fclose($cache_static);
		}
		// read from the cache file
		$rss = @unserialize(file_get_contents($cache_file));
	}
	else {
		// cache file is fresh enough, so read from it
		echo &quot;&lt;!-- SUCCESS: Cache file was recent enough to read from --&gt;&quot;;
		$rss = @unserialize(file_get_contents($cache_file));
	}
	// clean up and output the twitter feed
	$feed = str_replace(&quot;&amp;amp;&quot;, &quot;&amp;&quot;, $rss);
	$feed = str_replace(&quot;&amp;lt;&quot;, &quot;&lt; &quot;, $feed);
	$feed = str_replace(&quot;&amp;gt;&quot;, &quot;&gt;&quot;, $feed);
	$clean = explode(&quot;&lt;entry&gt;&quot;, $feed);
	$amount = count($clean) - 1;
	if ($amount) { // are there any tweets?
		for ($i = 1; $i &lt; = $amount; $i++) {
			$entry_close = explode(&quot;&lt;/entry&gt;&quot;, $clean[$i]);
			$clean_content_1 = explode(&quot;&lt;content type=\&quot;html\&quot;&gt;&quot;, $entry_close[0]);
			$clean_content = explode(&quot;&lt;/content&gt;&quot;, $clean_content_1[1]);
			$clean_name_2 = explode(&quot;&lt;name&gt;&quot;, $entry_close[0]);
			$clean_name_1 = explode(&quot;(&quot;, $clean_name_2[1]);
			$clean_name = explode(&quot;)&lt;/name&gt;&quot;, $clean_name_1[1]);
			$clean_user = explode(&quot; (&quot;, $clean_name_2[1]);
			$clean_lower_user = strtolower($clean_user[0]);
			$clean_uri_1 = explode(&quot;&lt;uri&gt;&quot;, $entry_close[0]);
			$clean_uri = explode(&quot;&lt;/uri&gt;&quot;, $clean_uri_1[1]);
			$clean_time_1 = explode(&quot;&lt;published&gt;&quot;, $entry_close[0]);
			$clean_time = explode(&quot;&lt;/published&gt;&quot;, $clean_time_1[1]);
			$unix_time = strtotime($clean_time[0]);
			$pretty_time = relativeTime($unix_time);
			?&gt;
				&lt;li&gt;
					&lt;p class=&quot;tweet&quot;&gt;
						&lt; ?php echo $clean_content[0]; ?&gt;
						&lt;small&gt;&lt; ?php echo $pretty_time; ?&gt;&lt;/small&gt;
					&lt;/p&gt;
				&lt;/li&gt;
			&lt;?php
		}
	} else { // if there aren't any tweets
		?&gt;
			&lt;li&gt;
				&lt;p class=&quot;tweet&quot;&gt;
					I have been terribly busy recently shoveling pixels and
					clearing out the tubes that make up the Internet, so I
					haven't had a chance to tweet recently. I am truly very
					sorry about this, so with just a bit more prodding I'll
					update as soon as possible.
				&lt;/p&gt;
			&lt;/li&gt;
		&lt;?php
	}
}
?&gt;</pre>
<p>You&#8217;ll notice that I&#8217;m creating more variables than I actually need, noticeably the ones for the user names. On the Pop Art blog, we&#8217;re always showing just one person&#8217;s tweets at a time, but the code is built to allow you to pass a list of twitter accounts, and it&#8217;ll pull them all in. We used that code on another website, and I just left it in place here in case I wanted to use it at some point in the future. Since the variables already exist, I would just need to add them to the output HTML and they would show up.</p>
<p>Which brings me to the final problem I ran into. Ryan&#8217;s script uses the Twitter search API, which is great because it gives you the option of pulling in multiple twitter accounts. The downside of the search API is that it will only return recent tweets (the documentation says 7 days, but it looks like it actually returns two weeks).</p>
<p>I did some digging into the API documentation, and there&#8217;s no way around this. Twitter has two APIs, the search API and the REST API. The search API is more full-featured, including search operators and automatic link highlighting, with the downside that it only searches recent tweets. The REST API will return all tweets for a user, but it won&#8217;t highlight links, merge multiple twitter feeds, and worst of all, it requires a login.</p>
<p>Given those restrictions, I left the code using the search API, despite the restriction. To make the best of the situation, we wrote a funny error message for users with no tweets, and asked everyone linking their Twitter account to post at least once a week.</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/07/twitter-php-caching/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Welcome to the New Pop Art&#160;Blog!</title>
		<link>http://blogs.popart.com/2009/06/welcome-to-the-new-pop-art-blog/</link>
		<comments>http://blogs.popart.com/2009/06/welcome-to-the-new-pop-art-blog/#comments</comments>
		<pubDate>Thu, 18 Jun 2009 01:11:45 +0000</pubDate>
		<dc:creator>Scott Vandehey</dc:creator>
				<category><![CDATA[Creative]]></category>
		<category><![CDATA[Web Development]]></category>
		<category><![CDATA[CommunityServer]]></category>
		<category><![CDATA[cs2007]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[profiles]]></category>
		<category><![CDATA[redesign]]></category>
		<category><![CDATA[standards]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://70.96.242.8/~popartblog/?p=472</guid>
		<description><![CDATA[The new Pop Art blog is live! On the front end, it&#8217;s sporting a brand-new, and oh-so-spiffy, design, and on the back-end it&#8217;s been converted to the titan of the blogging world, Wordpress. Please look around, and enjoy the new scenery.  And make sure you take a peek at the new profile pages by clicking [...]]]></description>
			<content:encoded><![CDATA[<p>The new Pop Art blog is live! On the front end, it&#8217;s sporting a brand-new, and oh-so-spiffy, design, and on the back-end it&#8217;s been converted to the titan of the blogging world, Wordpress. Please look around, and enjoy the new scenery.  And make sure you take a peek at the new profile pages by clicking the photos above. It&#8217;s the only way you&#8217;ll find out who hates Gloria Estefan, and who describes himself as an &#8220;urban nomad.&#8221;</p>
<p>If you&#8217;re reading this in your feed reader, then our RSS feed has automatically redirected, and you shouldn&#8217;t have any problems. But in case you&#8217;re a stickler for official URLs, or you haven&#8217;t subscribed, our new feed can be found at <a href="http://blogs.popart.com/feed/atom/">blogs.popart.com/feed/atom/</a>.</p>
<p>In the next few days, there are some more posts coming, including one from Mark about the new design, one about how we integrated Twitter, one about some nifty Wordpress tricks we learned, and a long one about our experience converting from Community Server 2007 to Wordpress (long story short: it makes scaling Everest look like walking up the stairs).</p>
<p>So please: snoop, dig, stop and smell the roses. And welcome to the redesigned and improved Pop Art Blog, it&#8217;s where the magic happens (or something like that).</p>
]]></content:encoded>
			<wfw:commentRss>http://blogs.popart.com/2009/06/welcome-to-the-new-pop-art-blog/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
	</channel>
</rss>
