<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/atom10full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><feed xmlns="http://www.w3.org/2005/Atom"><title>tech.agilitynerd</title><link href="http://tech.agilitynerd.com/" rel="alternate" /><id>http://tech.agilitynerd.com/</id><updated>2013-03-17T12:00:00-05:00</updated><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/atom+xml" href="http://feeds.feedburner.com/TechAgilityNerd" /><feedburner:info xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" uri="techagilitynerd" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><feedburner:emailServiceId xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">TechAgilityNerd</feedburner:emailServiceId><feedburner:feedburnerHostname xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0">http://feedburner.google.com</feedburner:feedburnerHostname><entry><title>Moved To Pelican</title><link href="http://tech.agilitynerd.com/moved-to-pelican.html" rel="alternate" /><updated>2013-03-17T12:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2013-03-17:moved-to-pelican.html</id><summary type="html">&lt;p&gt;Way back in 2004, when the content in this blog was a category in the main &lt;a class="reference external" href="http://agilitynerd.com"&gt;AglityNerd blog&lt;/a&gt;, I used the Perl &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; application to serve the blog. When my dog agility readers complained/were confused by the sprinkling of tech postings I split off tech.agilitynerd in another Blosxom instance.&lt;/p&gt;
&lt;p&gt;Once modern web hosted blogs came in to existence I wanted to see how they worked and moved the tech content to &lt;a class="reference external" href="http://posterous.com"&gt;posterous&lt;/a&gt;. That was only OK, the source code formatting was painful but it did support email and web based content creation. I was reasonably content, not bugged enough to move again.&lt;/p&gt;
&lt;p&gt;Then posterous announced &lt;a class="reference external" href="http://blog.posterous.com/thanks-from-posterous"&gt;it is closing on April 30&lt;/a&gt; so I finally had to do something. This site really only needs a few features:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;easy format in which to write content&lt;/li&gt;
&lt;li&gt;source code formatting&lt;/li&gt;
&lt;li&gt;Atom/RSS feeds&lt;/li&gt;
&lt;li&gt;comments&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I use Disqus for comments on my other sites so that meant I could go with a statically generated site. Also since I primarily code in Python I wanted a platform to which I could contribute. I came across &lt;a class="reference external" href="http://blog.getpelican.com/"&gt;Pelican&lt;/a&gt; and it fit the bill nicely.&lt;/p&gt;
&lt;p&gt;I exported the posterous posts, imported them through a temporary wordpress.com site using their posterous importer and finally re-exported them in wordpress format. After a quick bug fix to the pelican wordpress importer I had reStructured Text files for each post. After a few days of editing the embeded source code in the content files and fixing long broken links, I had the blog running and not looking bad at all.&lt;/p&gt;
&lt;p&gt;I wanted a Twitter Bootstrap based responsive layout and I found &lt;a class="reference external" href="https://github.com/azizmb/pelican-bootstrap-responsive-theme"&gt;azizmb's pelican-bootstrap-responsive-them&lt;/a&gt; which had a very pleasing layout. I tweaked it to have category and tag feeds and some other enhancements to get what you see today. I will see if Aziz is interested in pulling any of my changes back.&lt;/p&gt;
&lt;p&gt;So far I like using Pelican and I've made the &lt;a class="reference external" href="https://github.com/saschwarz/tech-agilitynerd"&gt;content of the blog&lt;/a&gt; and my &lt;a class="reference external" href="https://github.com/saschwarz/pelican-bootstrap-responsive-theme"&gt;edits to the theme&lt;/a&gt; available on github in case anyone is interested.&lt;/p&gt;
</summary><category term="posterous" /><category term="pelican" /><category term="blosxom" /><category term="bootstrap" /></entry><entry><title>Google Analytics for JQuery Mobile With Internal and AJAX Pages</title><link href="http://tech.agilitynerd.com/google-analytics-for-jquery-mobile-withwithou.html" rel="alternate" /><updated>2013-01-21T02:00:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2013-01-21:google-analytics-for-jquery-mobile-withwithou.html</id><summary type="html">&lt;p&gt;I added google analytics to my &lt;a class="reference external" href="http://agilitycourses.com"&gt;agilitycourses.com&lt;/a&gt;&amp;nbsp;website and was
having problems getting the mobile version of the site to log analytics
data after the first page. Google found this very helpful blog
post:&amp;nbsp;&lt;a class="reference external" href="http://www.jongales.com/blog/2011/01/10/google-analytics-and-jquery-mobile/"&gt;Using Google Analytics with jQuery Mobile&lt;/a&gt;&amp;nbsp;and it got me very
close to my final solution.&lt;/p&gt;
&lt;p&gt;Since that time jQuery Mobile has changed to&amp;nbsp;&lt;a class="reference external" href="http://jquerymobile.com/demos/1.0/docs/api/events.html"&gt;pageinit events&lt;/a&gt; and
&lt;a class="reference external" href="http://jquerymobile.com/demos/1.0/docs/api/methods.html"&gt;jqmData&lt;/a&gt;&amp;nbsp;for page selectors, so I had to change the first line below
to match the recomendations for the 1.x version of jQuery Mobile that
I'm using:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;div:jqmData(role='page')&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;live&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pageinit'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="cm"&gt;/* urls begin with locale but track urls independent of locale: strip off leading locale */&lt;/span&gt;
      &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pathname&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^/&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;zA&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;Z_&lt;/span&gt;&lt;span class="p"&gt;]{&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="c1"&gt;//, '');
&lt;/span&gt;      &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="cm"&gt;/* strip # from hash */&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;_gaq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'_setAccount'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'UA-XXXXXXX-X'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'_setDomainName'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'example.com'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'_setSiteSpeedSampleRate'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'_trackPageLoadTime'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
                &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'_trackPageview'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;My mobile site uses both internal pages and AJAX loaded pages. So unlike
the blog post's solution, I needed to use URLs with and without
hashtags. Per the &lt;a class="reference external" href="http://_trackPageview"&gt;Google docs on _trackPageview&lt;/a&gt;&amp;nbsp;I also added a
leading slash to the hashed URL.&lt;/p&gt;
&lt;p&gt;Another change for my site is urls are localized, they begin with the
locale for the user's language via &lt;a class="reference external" href="https://github.com/carljm/django-localeurl"&gt;django-localeurl&lt;/a&gt;. So I strip the
leading &amp;quot;/es/&amp;quot;, &amp;quot;/ca-FR/&amp;quot; or &amp;quot;/en_GB/&amp;quot; from the URL before sending it
to Google.&lt;/p&gt;
&lt;p&gt;There are a couple other variables I push on the _gaq array. I&amp;nbsp;log my
mobile site (m.agilitycourses.com) as a subdomain of my desktop site so
I followed the settings from the &lt;a class="reference external" href="https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingSite#domainAndSubDirectory"&gt;Google analytics&lt;/a&gt;&amp;nbsp;and set the domain
name. I also enabled client side page load and site speed settings.&lt;/p&gt;
&lt;p&gt;So I put the code in the head of the original post in the head of my
pages and the code snippet above in the foot of the pages.&lt;/p&gt;
&lt;p&gt;Hope this is helpful for others.&lt;/p&gt;
</summary><category term="ajax" /><category term="googleanalytics" /><category term="jquery" /><category term="jquerymobile" /></entry><entry><title>Create Posterous Posts from Google Forms</title><link href="http://tech.agilitynerd.com/create-posterous-posts-from-google-forms.html" rel="alternate" /><updated>2012-05-12T23:57:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2012-05-12:create-posterous-posts-from-google-forms.html</id><summary type="html">&lt;p&gt;I've been organizing dog agility bloggers for&amp;nbsp;&lt;a class="reference external" href="http://dog-agility-blog-events.posterous.com/"&gt;Dog Agility Blog Action
Days&lt;/a&gt;&amp;nbsp;and wanted to automate updating our group's blog when each
blogger posted their article on the event/action days. For past events I
had the bloggers email me with the information and manually created each
entry in the blog.&lt;/p&gt;
&lt;p&gt;Since &lt;a class="reference external" href="http://posterous.com"&gt;posterous&lt;/a&gt; supports posting via email I knew I could create a
form on one of my web servers and have the page format the data so
posterous could turn it into a post. Here are the details for formatting
emails for posterous: &lt;a class="reference external" href="http://howdoi.posterous.com/how-to-get-the-most-out-of-posting-by-email"&gt;how to get the most out of posting by email&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Instead I stumbled upon an article on using Google Forms to send emails
via the Google infrastructure:&amp;nbsp;&lt;a class="reference external" href="http://www.labnol.org/internet/google-docs-email-form/20884/"&gt;Get Form Data from Google Docs in an
Email Message [Video Tutorial]&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So all I had to do was create a form for my bloggers to fill out and
then have the script run by their submission format and send an email to
posterous to post.&lt;/p&gt;
&lt;p&gt;Here's my modified version of the form script:&lt;/p&gt;
&lt;pre class="code javascript literal-block"&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;sendFormByEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// create a draft post - posterous will send you an email
&lt;/span&gt;  &lt;span class="c1"&gt;// when the draft is created.  var email = &amp;quot;draft&amp;#64;YOURBLOGNAMEHERE.posterous.com&amp;quot;;
&lt;/span&gt;  &lt;span class="c1"&gt;// e.namedValues is a dictionary where the keys
&lt;/span&gt;  &lt;span class="c1"&gt;// are the names of your form's questions. So you can pick
&lt;/span&gt;  &lt;span class="c1"&gt;// them out and combine them as you wish.
&lt;/span&gt;  &lt;span class="c1"&gt;//
&lt;/span&gt;  &lt;span class="c1"&gt;// My form contained five questions:
&lt;/span&gt;  &lt;span class="c1"&gt;// 1. 'Title of your blog as you want it to appear'
&lt;/span&gt;  &lt;span class="c1"&gt;// 2. 'The URL of your blog as you type it in your browser'
&lt;/span&gt;  &lt;span class="c1"&gt;// 3. 'Title of your article'
&lt;/span&gt;  &lt;span class="c1"&gt;// 4. 'The URL of your article copied from your browser'
&lt;/span&gt;  &lt;span class="c1"&gt;// 5. 'Short description/teaser about your article'
&lt;/span&gt;  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;namedValues&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Title of your article'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;subject&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;((tag: attitude))&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// add tags as you wish
&lt;/span&gt;  &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;a href=&amp;quot;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'The URL of your blog as you type it in your browser'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'&amp;quot;&amp;gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Title of your blog as you want it to appear'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt;/a &amp;gt; wrote:'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;nn&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'Short description/teaser about your article'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;lt; a href=&amp;quot;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'The URL of your article copied from your browser'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s1"&gt;'&amp;quot;&amp;gt; Read the full article &amp;lt; /a &amp;gt;nn'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="s2"&gt;&amp;quot;n#end&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// so your email signature won't cause a problem
&lt;/span&gt;  &lt;span class="c1"&gt;// This is the MailApp service of Google Apps Script
&lt;/span&gt;  &lt;span class="c1"&gt;// that sends the email. You can also use GmailApp here.
&lt;/span&gt;  &lt;span class="nx"&gt;MailApp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// Watch the following video for details   // http://youtu.be/z6klwUxRwQI
&lt;/span&gt;  &lt;span class="c1"&gt;// By Amit Agarwal - www.labnol.org
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;In the script you can see how I've accessed the form fields by the full
name and woven them into an email with links embedded - just have to
watch the use of double quotes within single quotes. Which produces blog
entries similar &lt;a class="reference external" href="http://dogagilityblogevents.wordpress.com/if-i-knew-then-proactive-handling"&gt;to this one I created manually&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So now I can just send the link to the Google form to participants and
they'll create drafts that I can tweak and/or just submit. It took
longer to write this post than it took to modify and test the script!&lt;/p&gt;
</summary><category term="email" /><category term="google" /><category term="posterous" /></entry><entry><title>My Favorite ORM and Python Anti-Patterns</title><link href="http://tech.agilitynerd.com/my-favorite-orm-anti-pattern.html" rel="alternate" /><updated>2012-05-12T23:57:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2012-05-12:my-favorite-orm-anti-pattern.html</id><summary type="html">&lt;p&gt;At work I was looking at improving the performance of one of our slower
web pages. It can be rewarding to find a little piece of code that can
be easily optimized. This time there were several functions that were
adding 10+ sec to the page in worst case. It wasn't a problem for most
clients, but when clients with who are related to many other clients hit
the page they'd experience terrible performance. Here's pseudo code for
the combination of anti-patterns that caused the problem:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Projects have users and users are in different organizations
# (project can contain multiple organization's users)
activeOrganizationProjectUsers = [x for x in project.users
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if x.active and x.organization == organization]
if activeOrganizationProjectUsers:
&amp;nbsp;&amp;nbsp;&amp;nbsp; # do something *NOT* using activeOrganizationProjectUsers
&lt;/pre&gt;
&lt;p&gt;There are two main problems with this code:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;It ignores the fact the project, users, and organization are backed
by an ORM&lt;/li&gt;
&lt;li&gt;The list comprehension is being used to find all matching elements
when only a single element is needed.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="section" id="ignoring-the-orm"&gt;
&lt;h2&gt;Ignoring the ORM&lt;/h2&gt;
&lt;p&gt;The code above wouldn't be too bad if these were just lists of objects
in memory. But being objects that are instantiated by an ORM a number of
database queries will be issued. In this particular case (w/o eager
loading across user to the organization table) the following queries
where executed:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Join project to user and get all users for the project's id&lt;/li&gt;
&lt;li&gt;For each user load their organization (one by one) if the user is
active&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So in the case where there were hundreds of users on a project there
were hundreds of queries executed and hundreds of User and Organization
instances were instantiated. Depending on the size of the objects (and
the ORM's behavior) it can take &amp;quot;real time&amp;quot; to fetch and instantiate all
these large objects.&lt;/p&gt;
&lt;p&gt;This code base has this kind of code sprinkled through out it. At one
time during it's development the developers were encouraged to treat ORM
backed objects as though they were Plain Old Python Objects (POPOs). The
developer wouldn't necessarily see the performance degradation using
small data sets either. This is one of the reasons why I like to tail
the database log (or use &lt;a class="reference external" href="http://github.com/robhudson/django-debug-toolbar"&gt;django-debug-toolbar&lt;/a&gt; if I''m using Django)
to see the queries go by.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="using-list-comprehensions-when-a-single-value-is-needed"&gt;
&lt;h2&gt;Using List Comprehensions When a Single Value is Needed&lt;/h2&gt;
&lt;p&gt;To make this situation worse, the activeOrganizationProjectUsers list
wasn't actually used. This is a combination of a Python anti-pattern and
the ORM anti-pattern. What was required was to determine if a single
active organization user existed.&lt;/p&gt;
&lt;p&gt;I believe the original developer(s) used the list comprehension solution
in a combination of ignorance and syntactic sugar. They didn't want to
write a new function to do the query and put it in the User class so
they used the existing class's API. The syntactic sugar was using the
list comprehension to get more values than the one that was needed. If
this wasn't a (potentially) expensive ORM backed operation the original
code could have been:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
activeOrganizationProjectUsers = False
for x in project.users:
&amp;nbsp;&amp;nbsp;&amp;nbsp; if x.active and x.organization == organization:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; activeOrganizationProjectUsers = True
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; break
if activeOrganizationProjectUsers:
&amp;nbsp;&amp;nbsp;&amp;nbsp; # do something
&lt;/pre&gt;
&lt;p&gt;But this solution could still query all possible user/organization
combinations. The other question would be: which set is larger the
organization users or the project users? It is likely looping over the
organization's users looking for active ones would be more efficient
anyway.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="remember-the-underlying-representation"&gt;
&lt;h2&gt;Remember the Underlying Representation&lt;/h2&gt;
&lt;p&gt;When performance matters remembering the objects are ORM backed is
important. So in this case a single query was all that was required
(SqlObject pseudo syntax):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
activeOrganizationProjectUsers = Users.selectBy(project=project,
                     &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; active=True,
                     &amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; organization=organization).count() &amp;gt; 0
&lt;/pre&gt;
&lt;p&gt;If abstracting out the ORM's methods is important this new function
could be added to the appropriate class as a method. In my case making a
change to use a query resulted in cutting the page load time by two
orders of magnitude.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="antipattern" /><category term="development" /><category term="orm" /><category term="python" /></entry><entry><title>Reducing the Cost of Client Side Analytics</title><link href="http://tech.agilitynerd.com/reducing-the-impact-of-client-side-analytics.html" rel="alternate" /><updated>2012-01-12T22:10:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2012-01-12:reducing-the-impact-of-client-side-analytics.html</id><summary type="html">&lt;p&gt;I read&amp;nbsp;&lt;a class="reference external" href="http://blog.mozilla.com/webdev/2012/01/06/timing-amo-user-experience/"&gt;Andy McKay's blog post on timing user experience&lt;/a&gt;&amp;nbsp;on the
&lt;a class="reference external" href="http://blog.mozilla.com/webdev"&gt;Mozilla Webdev blog&lt;/a&gt;&amp;nbsp;the other day and it reminded me of an idea I was
thinking about for measuring client side timings at work. I had been
toying with the idea of rolling our own library to capture JavaScript
rendering time for our JS heavy pages (grids of hundreds of lines of
data).&lt;/p&gt;
&lt;p&gt;Andy's post mentions the &lt;a class="reference external" href="http://yahoo.github.com/boomerang/doc/"&gt;boomerang JavaScript library&lt;/a&gt;&amp;nbsp;and when I was
reading it's docs they pointed out potential for abuse/load on the URL
used to report the timings. For each instrumented page boomerang can hit
the &amp;quot;beacon&amp;quot; URL to report the statistics it collects. So in the worst
case you could double your page hits - although for specific
pages/samples recording a few statistics shouldn't be too costly for low
volume sites.&lt;/p&gt;
&lt;p&gt;One solution is to only sample the pages/users of interest; selecting
the sample could occur on the server and/or client. But another solution
would be to collect statistics across multiple pages and periodically
send batches of analytics to the beacon URL.&lt;/p&gt;
&lt;p&gt;I've been playing with mobile web development for
&lt;a class="reference external" href="http://m.agilitycourses.com/"&gt;agilitycourses.com&lt;/a&gt;&amp;nbsp;lately and will soon let users store the courses
they create in localStorage on their browser. That got me thinking that
&lt;a class="reference external" href="http://en.wikipedia.org/wiki/Web_Storage#Local_and_session_storage"&gt;sessionStorage&lt;/a&gt; could be used to store analytics across pages and then
periodically send the stats to the server. This would reduce the number
of hits on the beacon, allowing deployment to a larger sample of
clients. It also gets flushed once the session is closed and (if kept
small) doesn't prompt the user to approve storing the data.&lt;/p&gt;
&lt;p&gt;A lot of modern &lt;a class="reference external" href="http://www.delicious.com/redirect?url=http%3A//dev-test.nemikor.com/web-storage/support-test/"&gt;browsers support session storage&lt;/a&gt;&amp;nbsp;and for my purposes
only ones with support would be relevant - due to our browser support
policy at work.&lt;/p&gt;
&lt;p&gt;The other problem the boomerang docs discuss is&lt;a class="reference external" href="http://yahoo.github.com/boomerang/doc/howtos/howto-7.html"&gt;abuse of the beacon&lt;/a&gt;
(accidental or malicious). A solution would be to piggyback reporting of
analytics into application form post payloads. This is trickier to
implement and it suffers from coupling analytic reporting into the
application itself.&lt;/p&gt;
&lt;p&gt;To try to solve it some what generally... The client side JS library
could add a hidden field to any/some/specific forms into which it writes
the analytics data collected thus far. If it hooked the form submit
callback it could know if the form was successfully submitted and clear
the session storage.&lt;/p&gt;
&lt;p&gt;Server side middleware could detect the hidden analytics field in the
form and extract/store the data. It could also remove the field before
passing the request data along to the app server.&lt;/p&gt;
&lt;p&gt;All in all a fair amount of twiddling to keep from exposing a recording
URL to the outside world.&lt;/p&gt;
&lt;p&gt;Of course if an authenticated session was being used then abusers would
have to have a valid session to post to the beacon URL.&lt;/p&gt;
&lt;p&gt;I don't know if I will have time to play with the sessionStorage idea
but I think it might be a worthwhile extension to boomerang or other
client side analytics capture libraries.&lt;/p&gt;
</summary><category term="analytics" /><category term="boomerang" /><category term="javascript" /><category term="sessionstorage" /><category term="webdevelopment" /></entry><entry><title>YellowGrass - Web Based Issue Tracking</title><link href="http://tech.agilitynerd.com/yellowgrass-web-based-issue-tracking.html" rel="alternate" /><updated>2011-06-07T12:24:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2011-06-07:yellowgrass-web-based-issue-tracking.html</id><summary type="html">&lt;p&gt;I was doing some reading on &lt;a class="reference external" href="http://www.mobl-lang.org/"&gt;mobl&lt;/a&gt;&amp;nbsp;and saw&amp;nbsp;that they&amp;nbsp;are using a free
web based service called &lt;a class="reference external" href="http://yellowgrass.org/features"&gt;YellowGrass&lt;/a&gt;&amp;nbsp;for issue tracking. It has some
nice features and seems easy to use.&amp;nbsp;Everything is tag based.&amp;nbsp;I think
I'll try to use it for tracking enhancements to &lt;a class="reference external" href="http://agilitycourses.com"&gt;agilitycourses.com&lt;/a&gt;.&lt;/p&gt;
</summary><category term="development" /><category term="issuetracking" /></entry><entry><title>Django Shrink The Web django-stw 0.2.0 Released</title><link href="http://tech.agilitynerd.com/django-shrink-the-web-django-stw-020-released.html" rel="alternate" /><updated>2011-04-23T13:32:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2011-04-23:django-shrink-the-web-django-stw-020-released.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="http://www.shrinktheweb.com/"&gt;Shrink The Web&lt;/a&gt; has announced a new API for free users using their new
&lt;a class="reference external" href="http://www.shrinktheweb.com/content/what-stw-preview-verification.html"&gt;preview verification&lt;/a&gt; feature. This change required changes to my
django-stw package.&lt;/p&gt;
&lt;p&gt;The changes (lifted from the &lt;a class="reference external" href="https://github.com/saschwarz/django-stw/blob/master/CHANGELOG.txt"&gt;CHANGELOG.txt&lt;/a&gt;):&lt;/p&gt;
&lt;p&gt;Changes to the &lt;tt class="docutils literal"&gt;shrinkthewebimage&lt;/tt&gt; template tag:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;shrinkthewebimage&lt;/tt&gt; template tag is NOT backward compatible with
version 0.0.1. The alt argument is no longer accepted.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;shrinkthewebimage&lt;/tt&gt; template tag is now intended for use by free
accounts, it adds the required preview feature. It can also be used
by PRO account users wanting the preview functionality.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;shrinkthewebimage&lt;/tt&gt; template tag now accepts PRO key-value
arguments in the same manner as the stwimage tag. This functionality
is shown in theexample template but may not yet be fully implemented
by the STW web service.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Changes to the &lt;tt class="docutils literal"&gt;stwimage&lt;/tt&gt; template tag:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;stwimage&lt;/tt&gt; can now only be used for PRO features.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Common changes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Template tags now throw exceptions in their constructors instead of
in the render function so configuration errors are visible during
development.&lt;/li&gt;
&lt;li&gt;django-stw defines a key 'lang' for the SHRINK_THE_WEB dictionary
that can be passed along as a default to the preview tag. Alternately
a 'lang' keyword can be supplied in each template tag invocation.
django-stw defaults it to 'en'. This functionality is not yet
implemented by the STW web service.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The v 0.2.0 package is &lt;a class="reference external" href="http://pypi.python.org/pypi?:action=display&amp;amp;name=django-stw&amp;amp;version=0.2.0"&gt;available on PyPi&lt;/a&gt;, as a &lt;a class="reference external" href="https://github.com/saschwarz/django-stw/archives/v0.2.0"&gt;source download on
github&lt;/a&gt;, or via &lt;a class="reference external" href="https://github.com/saschwarz/django-stw"&gt;git clone&lt;/a&gt;.&lt;/p&gt;
</summary><category term="django" /><category term="shrinktheweb" /></entry><entry><title>Python dict.get's Default Value is Always Evaluated</title><link href="http://tech.agilitynerd.com/python-dictgets-default-value-is-always-evalu.html" rel="alternate" /><updated>2011-04-05T17:16:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2011-04-05:python-dictgets-default-value-is-always-evalu.html</id><summary type="html">&lt;p&gt;This is a gotcha I ran across in some production code that is obvious in
retrospect.&amp;nbsp;I was profiling the code to find places where we were
calling &lt;tt class="docutils literal"&gt;an_expensive_database_function&lt;/tt&gt; and came across code like
this:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;doit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
     &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;an_expensive_database_function&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The original author probably assumed that if 'key' was present in the
&lt;tt class="docutils literal"&gt;kwargs&lt;/tt&gt; dictionary &lt;tt class="docutils literal"&gt;an_expensive_database_function&lt;/tt&gt; wouldn't be called;
that it would be short circuited in the same manner as Boolean
expressions. But since get is a function the arguments are always
evaluated on the way into the function. So in this case even if the
value of &lt;tt class="docutils literal"&gt;an_expensive_database_function&lt;/tt&gt; was already present in the
&lt;tt class="docutils literal"&gt;kwargs&lt;/tt&gt; dictionary the database function would be called again.&lt;/p&gt;
&lt;p&gt;Here is a &amp;quot;look before you leap&amp;quot; solution:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;doit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="c"&gt;# assuming default value None isn't a valid value&lt;/span&gt;
         &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;an_expensive_database_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Here is the &amp;quot;easier to ask forgiveness than permission&amp;quot; solution:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;doit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt;&lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'key'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="ne"&gt;KeyError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;an_expensive_database_function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;
&lt;/pre&gt;
</summary><category term="python" /></entry><entry><title>Blosxom Plugin for Generating Facebook Comment xids</title><link href="http://tech.agilitynerd.com/blosxom-plugin-for-generating-facebook-commen.html" rel="alternate" /><updated>2010-11-23T05:00:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-11-23:blosxom-plugin-for-generating-facebook-commen.html</id><summary type="html">&lt;p&gt;I've been using &lt;a class="reference external" href="http://www.blosxom.com/"&gt;Blosxom&lt;/a&gt; to power my dog agility blog for over 6
years. In the past year or so I've enabled &lt;a class="reference external" href="http://developers.facebook.com/docs/reference/plugins/comments"&gt;Facebook comments&lt;/a&gt; in
addition to my site's own comment plugin. I ran into a problem using
Facebook's comments: if the user enters a comment on a page and the URL
has any additional URL parameters then the comment is only associated
with the page when accessed with those parameters, others hitting the
page w/o parameters won't see the comments.&lt;/p&gt;
&lt;p&gt;This behavior is &lt;a class="reference external" href="http://developers.facebook.com/docs/reference/fbml/comments_%28XFBML%29"&gt;documented by Facebook&lt;/a&gt; when the &lt;tt class="docutils literal"&gt;xid&lt;/tt&gt; attribute
isn't set in &lt;tt class="docutils literal"&gt;fb:comments&lt;/tt&gt; HTML element. I didn't think I'd encounter this
situation since my blog post URLs don't contain any parameters. However,
when people link to one of my articles within Facebook, Facebook appends
various parameters to the base URL.&lt;/p&gt;
&lt;p&gt;The solution is to specify an &lt;tt class="docutils literal"&gt;xid&lt;/tt&gt; attribute in the &lt;tt class="docutils literal"&gt;fb:comments&lt;/tt&gt; element
containing the URL encoded URL of the page (Facebook's default &lt;tt class="docutils literal"&gt;xid&lt;/tt&gt;).
This causes existing comments to show up and causes comments created
when the page is loaded with URL parameters to use the same encoded URL.&lt;/p&gt;
&lt;p&gt;So I created a simple Blosxom plugin to perform the encoding so the
encoded URL can be placed in the story.html template:&lt;/p&gt;
&lt;pre class="code perl literal-block"&gt;
&lt;span class="c1"&gt;# Blosxom Plugin: urlencode -*- perl -*-&lt;/span&gt;
&lt;span class="c1"&gt;# Author: Steve Schwarz &amp;lt;http://agilitynerd.com/&amp;gt;&lt;/span&gt;
&lt;span class="c1"&gt;# 2010-NOV-22    0.1 initial version.package urlencode;&lt;/span&gt;
&lt;span class="c1"&gt;# puts the urlencoded string of the URL for this page&lt;/span&gt;
&lt;span class="c1"&gt;# into $urlencode::url without any params# use this in fb.comments xid to give the same xid&lt;/span&gt;
&lt;span class="c1"&gt;# even when query params are provided&lt;/span&gt;
&lt;span class="c1"&gt;# -------------------&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;CGI&lt;/span&gt; &lt;span class="sx"&gt;qw/:standard/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;&lt;/span&gt;&lt;span class="nn"&gt;URI::&lt;/span&gt;&lt;span class="n"&gt;Escape&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;start&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;$url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;sub &lt;/span&gt;&lt;span class="nf"&gt;story&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;my&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pkg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$filename&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$story_ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$title_ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$body_ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;&amp;#64;_&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="nn"&gt;urlencode::&lt;/span&gt;&lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;uri_escape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;$blosxom::url/${blosxom::path_info}&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Then it is used in the story template:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;fb:comments width=&amp;quot;550&amp;quot; numposts=&amp;quot;5&amp;quot; xid=&amp;quot;$urlencode::url&amp;quot;&amp;gt;&amp;lt;/fb:comments&amp;gt;
&lt;/pre&gt;
&lt;p&gt;Now my readers won't have to worry that their comments won't show up.&lt;/p&gt;
</summary><category term="blosxom" /><category term="comments" /><category term="facebook" /><category term="perl" /></entry><entry><title>Obtain Short URLs and QR-Codes for Django Apps</title><link href="http://tech.agilitynerd.com/obtain-short-urls-and-their-qr-codes-for-djan.html" rel="alternate" /><updated>2010-10-22T04:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-10-22:obtain-short-urls-and-their-qr-codes-for-djan.html</id><summary type="html">&lt;p&gt;Lately I've been interested in improving the interaction of my
&lt;a class="reference external" href="http://agilitycourses.com"&gt;agilitycourses&lt;/a&gt; website for mobile users. One such improvement is to add
&lt;a class="reference external" href="http://en.wikipedia.org/wiki/QR_Code"&gt;QR Codes&lt;/a&gt; (aka 2D barcodes) representing the page URLs to the printed
representations of pages served as PDFs.&lt;/p&gt;
&lt;p&gt;I found that developers have reverse engineered the &amp;quot;api&amp;quot; of the
&lt;a class="reference external" href="http://goo.gl"&gt;goo.gl&lt;/a&gt; URL shortening web site. In my brief testing it is very fast.
What makes that service extra useful is by adding &amp;quot;.qr&amp;quot; to a shortened
URL it returns a PNG image of the QR Code for the shortened URL. That
made it perfect for providing both short text and QR Code URL
representations for my printed documents.&lt;/p&gt;
&lt;p&gt;I threw together a few functions and put them in a module to make it
easy to shorten a long URL, obtain the QR Code PNG and store it using
&lt;a class="reference external" href="http://docs.djangoproject.com/en/dev/topics/files/"&gt;Django's Storage functionality&lt;/a&gt;:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;osimport&lt;/span&gt; &lt;span class="nn"&gt;urllib&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.utils&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;simplejson&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;googl_shorten_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;long_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;
    Returns goo.gl shortened url for the provided long_url.
    Code taken from: http://djangosnippets.org/snippets/2220/
    Parameters:
    - `long_url`: the url to supply to goo.gl to be shortened.
    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;params&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="s"&gt;'security_token'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'url'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;long_url&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://goo.gl/api/shorten'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;simplejson&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())[&lt;/span&gt;&lt;span class="s"&gt;'short_url'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;googl_qrcode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;googl_url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;
    Return file containing qr code image file for the given goo.gl url.
    Parameters:
    - `googl_url`: url from which to obtain the qr code.
    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;googl_url&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.qr'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_url_qr_code_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;long_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;storage_image_file_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;
    Return goo.gl shortened url and storage name of qr code corresponding to
    the shortened url for the supplied full url. Contacts goo.gl to shorten
    the supplied long url then downloads and stores the qr code image file
    in the storage instance using the file path and the shortened url name
    as the storage name.
    Parameters:
    - `long_url`: the url to shorten.
    - `storage': a Django storage instance into which to store the qr code
    image.
    - `storage_image_file_path`: file system path to prepend to shortened
    url. This path must exist prior to calling this function.
    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;googl_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;googl_shorten_url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;long_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;qr_file_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;googl_url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;'.qr'&lt;/span&gt;
        &lt;span class="n"&gt;qr_code_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;storage_image_file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qr_file_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qr_code_name&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;qr_buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qr_code_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'wb'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;qr_buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;googl_qrcode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;googl_url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="n"&gt;qr_buffer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;googl_url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
        &lt;span class="n"&gt;qr_code_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;googl_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qr_code_name&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Yes, it has a nasty bare try/except. For my uses this is optional
functionality so I never want a failure to stop the main functionality
of the views that use it. Add exception handling appropriate for your
needs.&lt;/p&gt;
&lt;p&gt;The main entry point is &lt;tt class="docutils literal"&gt;get_url_qr_code_image()&lt;/tt&gt;. Here is an example
of its use (assuming you save the code in googl.py):&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;googl&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.files.storage&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;default_storage&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;short_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;qr_code_storage_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;googl&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_url_qr_code_image&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'http://google.com'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;default_storage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;short_urlu&lt;/span&gt;&lt;span class="s"&gt;'http://goo.gl/mR2d'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;qr_code_storage_nameu&lt;/span&gt;&lt;span class="s"&gt;'mR2d.qr'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;default_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qr_code_storage_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s"&gt;u'/home/dev/agilitycourses/static/mR2d.qr'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;default_storage&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;qr_code_storage_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s"&gt;u'mR2d.qr'&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Hope you find this useful.&lt;/p&gt;
</summary><category term="django" /><category term="googl" /><category term="python" /><category term="qrcode" /></entry><entry><title>Adding pyrsvg to a virtualenv created with --no-site-packages</title><link href="http://tech.agilitynerd.com/adding-rsvg-to-a-virtualenv-created-with-no-s.html" rel="alternate" /><updated>2010-10-12T03:02:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-10-12:adding-rsvg-to-a-virtualenv-created-with-no-s.html</id><summary type="html">&lt;p&gt;I set up my development and deployment environments on Ubuntu with
&lt;a class="reference external" href="http://pypi.python.org/pypi/virtualenv"&gt;virtualenv&lt;/a&gt;with the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--no-site-packages&lt;/span&gt;&lt;/tt&gt; option to isolate them from
packages in the system installation. My application uses &lt;a class="reference external" href="http://cairographics.org/pyrsvg/"&gt;pyrsvg&lt;/a&gt;and
it is installed by default as a system package. Consequently I had to
link the shared libraries it installs (w/in gtk) into my virtualenv.&lt;/p&gt;
&lt;p&gt;Here are the links I created (workon and cdsitepackages are
&lt;a class="reference external" href="http://www.doughellmann.com/projects/virtualenvwrapper/"&gt;virtualenvwrapper&lt;/a&gt;shell aliases):&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ workon project
$ cdsitepackages
$ ln -s /var/lib/python-support/python2.6/gtk-2.0/rsvg.so .
$ ln -s /var/lib/python-support/python2.6/gtk-2.0/gobject .
$ ln -s /var/lib/python-support/python2.6/gtk-2.0/glib .
&lt;/pre&gt;
</summary><category term="python" /><category term="rsvg" /><category term="virtualenv" /></entry><entry><title>Mobile Web Site Redirects in Django</title><link href="http://tech.agilitynerd.com/conditional-mobile-web-site-redirect-in-djang.html" rel="alternate" /><updated>2010-10-05T14:34:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-10-05:conditional-mobile-web-site-redirect-in-djang.html</id><summary type="html">&lt;p&gt;For the mobile version of agilitycourses.com I wanted to follow the
approach Google appears to be using on some of its sites:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;If the user views agilitycourses.com from a desktop browser they
should see the standard/desktop version of the site.&lt;/li&gt;
&lt;li&gt;If the user views agilitycourses.com from a mobile browser they
should be redirected to a mobile domain (m.agilitycourses.com).&lt;/li&gt;
&lt;li&gt;The mobile version of the website includes a link to the standard
version.&lt;/li&gt;
&lt;li&gt;If the mobile user chooses the standard website they should &amp;quot;stick&amp;quot;
on that site and not be redirected to the mobile site.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I wanted to run two different websites but share templates and have the
templates and css change for the mobile site. That meant that I'd need
to set a variable(s) in the request to use to generate the appropriate
HTML. So I found the simplest mobile device detector &lt;a class="reference external" href="http://code.google.com/p/minidetector/"&gt;minidetector&lt;/a&gt; and
initially used that. I later found &lt;a class="reference external" href="http://github.com/shelfworthy/minidetector"&gt;Chris Drackett's fork&lt;/a&gt; has a number
of useful enhancements and switched to it.&lt;/p&gt;
&lt;p&gt;But minidetector didn't provide the ability to redirect to another site.
I found &lt;a class="reference external" href="http://www.packtpub.com/article/multiple-templates-in-django"&gt;Scott Newman's article&lt;/a&gt; on using multiple templates which had
a section on performing the redirect and storing the user's selection in
the session. So I forked Chris' minidetector and modified it to include
the redirect and session storage. At the same time I decided to store
all the minidetector variables into the session and add them, via
middleware, to the request so the raw request wouldn't have to be parsed
each time. My fork is &lt;a class="reference external" href="http://github.com/saschwarz/minidetector"&gt;available here&lt;/a&gt; with details on the new
configuration options.&lt;/p&gt;
&lt;p&gt;I'm using two domains so I can track analytics for the mobile and
non-mobile sites separately and allow users to bookmark the desired
site's pages. I use Google Analytics (via django-google-analytics) and
Awstats for analytics.&lt;/p&gt;
&lt;p&gt;Since I'm using two separate domain and sharing everything else I'm
using a setup similar to the one described by &lt;a class="reference external" href="http://www.nerdydork.com/mobile-app-on-subdomain-with-django.html"&gt;Dustin Davis&lt;/a&gt;. I have a
settings.py file and a mobile_settings.py that only overrides the
features I need:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
from settings import *
SITE_ID = 2
CACHE_MIDDLEWARE_KEY_PREFIX = &amp;quot;m.ac-&amp;quot;
&lt;/pre&gt;
&lt;p&gt;I use a different memcached key prefix so the cached pages for the
mobile site don't clash with those for the desktop site.&lt;/p&gt;
&lt;p&gt;I setup m.agilitycourses on my server using the same &lt;a class="reference external" href="http://tech.agilitynerd.com/configuring-runit-for-gunicorn-and-django-ins"&gt;Gunicorn setup I
used for agilitycourses.com&lt;/a&gt; with the only changes being specifying the
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;--bind&lt;/span&gt; address/port&lt;/tt&gt; and the name of the mobile settings file:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
#!/bin/sh
GUNICORN=/home/user/virtualenvs/myapp/bin/gunicorn_django
ROOT=/home/user/source/myapp
PID=/var/run/myapp.pid
if [ -f $PID ]
&amp;nbsp;&amp;nbsp;&amp;nbsp; then rm $PID fi
cd $ROOT
exec $GUNICORN --bind 127.0.0.1:8001 -c $ROOT/gunicorn.conf.py --pid=$PID $ROOT/mobile_settings.py
&lt;/pre&gt;
&lt;p&gt;If my templates/content start to diverge more significantly between the
mobile and desktop sites I may set the TEMPLATE_DIRS differently in the
mobile_settings file. Or I can move to Dustin's approach and create a
new application containing the urls.py and views.py specific to my
mobile deployment. I would think diverging further would call for a
refactoring of the common functionality to its own application which
could be imported into separate code branches for each domain.&lt;/p&gt;
</summary><category term="django" /><category term="gunicorn" /><category term="minidetector" /><category term="mobile" /></entry><entry><title>Debug Site for Website Redirects By Referer String</title><link href="http://tech.agilitynerd.com/debug-tool-for-mobile-website-selection-by-re.html" rel="alternate" /><updated>2010-09-29T18:42:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-09-29:debug-tool-for-mobile-website-selection-by-re.html</id><summary type="html">&lt;p&gt;I'm adding an &amp;quot;m&amp;quot; subdomain to agilitycourses.com to provide a better
mobile browsing experience. I'm using the referrer string in Django
middleware (currently using &lt;a class="reference external" href="http://code.google.com/p/minidetector/"&gt;minidetector&lt;/a&gt;) to detect whether the
client is mobile and redirect them to the mobile site. Since it is
likely that some folks will/won't get appropriately redirected I was
looking for an easy way for them to tell me when they were incorrectly
redirected. I'd need to know their referer string.&lt;/p&gt;
&lt;p&gt;A little googling turned up a nice one purpose website:
&lt;a class="reference external" href="http://www.whatismyreferrer.com/"&gt;www.whatismyreferrer.com/&lt;/a&gt;&lt;/p&gt;
</summary><category term="django" /><category term="mobile" /><category term="referrer" /><category term="webdevelopment" /></entry><entry><title>Configuring Runit for Gunicorn and Django Installed in a Virtualenv on Ubuntu</title><link href="http://tech.agilitynerd.com/configuring-runit-for-gunicorn-and-django-ins.html" rel="alternate" /><updated>2010-09-08T03:08:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-09-08:configuring-runit-for-gunicorn-and-django-ins.html</id><summary type="html">&lt;p&gt;I couldn't find any documentation that covered all the pieces for
configuring my latest Django site so I hope this helps someone else out.&lt;/p&gt;
&lt;p&gt;I had used mod_wsgi under Apache for my other Django sites. But now I'm
using different python versions for the sites (until if/when I update
the older sites) and I wasn't getting the correct versions of some
python libraries (even though virtualenv apeared to be putting the
appropriate python packages at the start of the sys.path). So I decided
to configure Apache to ProxyPass to &lt;a class="reference external" href="http://gunicorn.org/"&gt;Gunicorn&lt;/a&gt;so I could run my
Django app in its virtualenv without it getting any other python
modules.&lt;/p&gt;
&lt;div class="section" id="installing-gunicorn"&gt;
&lt;h2&gt;Installing Gunicorn&lt;/h2&gt;
&lt;p&gt;I installed Gunicorn into the virtualenv for my application, which
simplifies using gunicorn from the command line. Assuming
&lt;tt class="docutils literal"&gt;/home/user/virtualenvs/myapp&lt;/tt&gt; is the location of the virtualenv:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
$ source /home/user/virtualenvs/myapp/bin/activate
$ pip install gunicorn

# or
$ easy_install gunicorn
&lt;/pre&gt;
&lt;p&gt;This copies gunicorn_django to the /home/user/virtualenvs/myapp/bin
directory. Test gunicorn with your app, assuming your Django app is
located at /home/user/source/myapp, as follows:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
$ source /home/user/virtualenvs/myapp/bin/activate
(myapp)$ cd /home/user/source/myapp
(myapp)$ gunicorn_django
&lt;/pre&gt;
&lt;p&gt;Gunicorn starts myapp using the &lt;tt class="docutils literal"&gt;settings.py&lt;/tt&gt; file in the current
directory on 127.0.0.1:8000. Ctrl-C to stop the process.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="installing-runit-on-ubuntu"&gt;
&lt;h2&gt;Installing Runit on Ubuntu&lt;/h2&gt;
&lt;p&gt;There are two &lt;a class="reference external" href="http://smarden.org/runit/index.html"&gt;runit&lt;/a&gt; packages. You want the one that only runs
services you add to it:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
$ sudo apt-get install runit
Reading package lists... Done
Building dependency tree
Reading state information... Done
Suggested packages:
&amp;nbsp; runit-run socklog-run
The following NEW packages will be installed:
&amp;nbsp; runit0 upgraded, 1 newly installed, 0 to remove and 0 not upgraded.
Need to get 0B/113kB of archives.After this operation, 537kB of additional disk space will be used.
Selecting previously deselected package runit.
(Reading database ... 209845 files and directories currently installed.)
Unpacking runit (from .../runit_2.0.0-1ubuntu2_i386.deb) ...
Processing triggers for man-db ...
Setting up runit (2.0.0-1ubuntu2) ...
runsvdir (start) waiting
runsvdir (start) startingrunsvdir (start) pre-start
runsvdir (start) spawned, process 9575
runsvdir (start) post-start, (main) process 9575
runsvdir (start) running, process 9575
&lt;/pre&gt;
&lt;p&gt;You'll want to create a directory for the application and a run script
in &lt;tt class="docutils literal"&gt;/etc/service:&lt;/tt&gt;&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
$ sudo mkdir /etc/service/myapp
$ sudo vi /etc/service/myapp/run
# enter the run script I'll show below
$ sudo chmod +x /etc/service/myapp/run
# stop runit from trying to run gunicorn until we are ready
$ sudo sv stop myappok: down: myapp: 0s, normally up
&lt;/pre&gt;
&lt;p&gt;The example run script checked into Gunicorn had some syntax errors
and wasn't quite what I wanted. Here's my version:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
#!/bin/sh
GUNICORN=/home/user/virtualenvs/myapp/bin/gunicorn_django
ROOT=/home/user/source/myapp
PID=/var/run/myapp.pid

if [ -f $PID ]
&amp;nbsp;&amp;nbsp;&amp;nbsp; then rm $PID
fi

cd $ROOT
exec $GUNICORN -c $ROOT/gunicorn.conf.py --pid=$PID
&lt;/pre&gt;
&lt;p&gt;You can create a &lt;a class="reference external" href="http://gunicorn.org/configure.html"&gt;configuration file for gunicorn&lt;/a&gt;to use or just
create an empty file for now:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
$ touch /home/user/source/myapp/gunicorn.conf.py
&lt;/pre&gt;
&lt;p&gt;If you have multiple appserver you'll need to run gunicorn on
different ports, you can put the configuration in the gunicorn.conf.py
file:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
bind = &amp;quot;127.0.0.1:8111&amp;quot;
&lt;/pre&gt;
&lt;/div&gt;
&lt;div class="section" id="putting-it-together"&gt;
&lt;h2&gt;Putting it Together&lt;/h2&gt;
&lt;p&gt;Now you can test that the run script works when run as root:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo /etc/service/myapp/run
&lt;/pre&gt;
&lt;p&gt;Gunicorn should start and start the appserver. If it fails you can
debug the script via:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo bash -x /etc/service/myapp/run
&lt;/pre&gt;
&lt;p&gt;Tell runit to start and keep gunicorn running:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo sv start myapp
ok: run: myapp: (pid 7540) 0s
$ sudo sv status myapp
run: myapp: (pid 7543) 1s
&lt;/pre&gt;
&lt;/div&gt;
</summary><category term="apache" /><category term="django" /><category term="gunicorn" /><category term="runit" /><category term="ubuntu" /><category term="virtualenv" /></entry><entry><title>MOTD</title><link href="http://tech.agilitynerd.com/motd.html" rel="alternate" /><updated>2010-08-31T20:21:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-08-31:motd.html</id><summary type="html">&lt;p&gt;I've always preferred maintainable code over clever code:&lt;/p&gt;
&lt;blockquote&gt;
Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it. - Brian W. Kernighan&lt;/blockquote&gt;
&lt;p&gt;Kernighan had it right all those years ago.&lt;/p&gt;
</summary></entry><entry><title>Confidently Refactoring Django URLs, Views, and Templates</title><link href="http://tech.agilitynerd.com/confidently-refactoring-django-urls-views-and.html" rel="alternate" /><updated>2010-08-22T05:12:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-08-22:confidently-refactoring-django-urls-views-and.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="http://googility.com/"&gt;Googility.com&lt;/a&gt;is my first Django website and under the covers the
oldest code looked like it. I had originally written it with the sole
intent of allowing people to enter dog agility businesses and websites
into a database that I could use to create a Dog Agility &lt;a class="reference external" href="http://www.google.com/cse/"&gt;Google Custom
Search Engine&lt;/a&gt;. The primary mistake I made was making the &amp;quot;project&amp;quot; (in
Django speak) effectively equivalent to the primary application. In
other words I didn't divide the major features of the site into
standalone applications (which would allow them to be more easily
reused, extended and tested).&lt;/p&gt;
&lt;p&gt;As I continued to work on it I learned more about organizing Django
projects. When I added the periodical search to the website I created it
as a standalone application. I recently split out my
&lt;a class="reference external" href="http://github.com/saschwarz/django-stw"&gt;django-shrinktheweb&lt;/a&gt; application from the main code base.&lt;/p&gt;
&lt;p&gt;The Custom Search Engine (CSE) functionality is a worthwhile application
that I'm planning on releasing as its own reusable application. I had
already created an application directory called &amp;quot;cse&amp;quot; into which I had
placed my models, views, urls, and tests specific to the CSE
functionality. But I wanted to make the following changes:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Move CSE templates into a cse template subdirectory&lt;/li&gt;
&lt;li&gt;Name the templates to match the views that use them&lt;/li&gt;
&lt;li&gt;Name the urls in the urls.py prefixed with the application name
(&amp;quot;cse_&amp;quot;)&lt;/li&gt;
&lt;li&gt;Covert all reverse() calls in the views and url template tags to use
the named urls&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those are enough changes that I was concerned that I might miss
something that would fail either in the view code or in rendering of the
templates.&lt;/p&gt;
&lt;p&gt;The Django test client makes it easy to test the forward and reverse url
matching, calling the view and rendering the template. It is kind of a
coarse grained test but the changes I was making were perfect for this
tool. Given a urls.py:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'cse.views'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r'^site/view/(?P&amp;lt;id&amp;gt;d+)/$'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'view'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'cse_view'&lt;/span&gt;&lt;span class="p"&gt;),)&lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;and a view:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'cse/view.html'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="sd"&gt;&amp;quot;&amp;quot;&amp;quot;Display an end user read only view of the site information&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class="n"&gt;site&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_object_or_404&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Annotation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;render_to_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                          &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'site'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="s"&gt;'labels'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;get_labels_for&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cap&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                           &lt;span class="p"&gt;},&lt;/span&gt;
                          &lt;span class="n"&gt;context_instance&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;RequestContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;I then wrote a test class to create the required test instances and
tests for each url to verify that the url can be found by name (via
reverse()), the url maps to a view, the view invokes the desired
template(s), and the {% url %} calls within the template can all be
resolved:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.test&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TestCase&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.test.client&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.conf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.core.urlresolvers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;reverse&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;cse.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Annotation&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ViewsTestCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TestCase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;setUp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ROOT_URLCONF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ROOT_URLCONF&lt;/span&gt;
        &lt;span class="c"&gt;# can provide a custom urls.py for testing so the tests can be run when&lt;/span&gt;
        &lt;span class="c"&gt;# the application is incorporated into another project&lt;/span&gt;
        &lt;span class="c"&gt;# settings.ROOT_URLCONF = 'cse.tests.cse_test_urls'&lt;/span&gt;
        &lt;span class="c"&gt;# override the template context processors if there are special ones in place&lt;/span&gt;
        &lt;span class="c"&gt;# that either you want to test or want to avoid&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt;
        &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="c"&gt;# Create some instances on which we can invoke views&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Label&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'description'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Annotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;comment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'Site Name'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;original_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'http://example.com/'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;labels&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;tearDown&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c"&gt;# put settings back so the next tests aren't effected&lt;/span&gt;
        &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ROOT_URLCONF&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ROOT_URLCONF&lt;/span&gt;
        &lt;span class="n"&gt;settings&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;test_view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'cse_view'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;kwargs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;id&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;annotation&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status_code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assertTemplateUsed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;'cse/view.html'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The normal unittest asserts are available in the tests. I'm using one of
the &lt;a class="reference external" href="http://docs.djangoproject.com/en/dev/topics/testing/#assertions"&gt;special asserts provided by the Django test Client&lt;/a&gt; to verify that
the template I expected was used. All the templates used (due to
template inheritance) are collected by the client and can also be
verified.&lt;/p&gt;
&lt;p&gt;I used these tests in a TDD-ish manner, I wrote the test for a view, ran
the tests and kept resolving errors in the templates as I made the
changes in my bullet list. It made a tedious job simple and gave me good
confidence that I'd found all the renamed urls, views, and templates.&lt;/p&gt;
</summary><category term="django" /><category term="googility" /><category term="python" /><category term="tdd" /><category term="testing" /></entry><entry><title>Haystack Search Result Ordering and Pre-Rendering Results</title><link href="http://tech.agilitynerd.com/haystack-search-result-ordering-and-pre-rende.html" rel="alternate" /><updated>2010-08-10T03:41:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-08-10:haystack-search-result-ordering-and-pre-rende.html</id><summary type="html">&lt;p&gt;I use &lt;a class="reference external" href="http://haystacksearch.org/"&gt;Haystack&lt;/a&gt; and the Python &lt;a class="reference external" href="http://whoosh.ca/"&gt;Whoosh&lt;/a&gt; project to provide search
over ~3400 articles in my &lt;a class="reference external" href="http://googility.com"&gt;Googility.com&lt;/a&gt; database. I had originally
implemented the search in the &amp;quot;simplest way that works&amp;quot;. I was making
some other enhancement to Googility and noticed the search result page
had two undesirable&amp;nbsp; behaviors:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;The ordering of results was basically random for all matching
articles. For the domain of magazine article search having a bias
toward the most recent publications would be more desirable.&lt;/li&gt;
&lt;li&gt;Looking at the django-debug-toolbar output each element in the search
results was hitting the database twice (once for the Article instance
and again for its corresponding Periodical). So a single result page
was making as many as 60 database selects.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Haystack provides mechanisms to help with both of these issues.&lt;/p&gt;
&lt;p&gt;Imposing an Order on the SearchQuerySet&lt;/p&gt;
&lt;p&gt;Haystack models search using an API based on Django's QuerySet. The
only thing to remember is it performs its queries over the Haystack
SearchIndex subclass(es) you create instead of over the Django ORM. So
you define a SearchIndex subclass that contains the data from the
application's model overwhich you'd like to search. You can also define
additional fields that can be used to modify the results of the query.
Here is my magazine Article search index:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.sites&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;periodicals.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Article&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchIndex&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pub_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_attr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'issue__pub_date'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ArticleIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The text field contains the &amp;quot;document&amp;quot; over which the search engine
(Whoosh) will actually perform the search. I'm using the template
feature that allows me to use Django templates to format the data
presented to the search engine.&lt;/p&gt;
&lt;p&gt;I added the pub_date field to the index to allow the matching search
results to be ordered by the pub_date field. The 'issue__pub_date'
syntax mirrors the Django QuerySet syntax and means extract the
&amp;quot;pub_date&amp;quot; attribute of the Article's &amp;quot;issue&amp;quot; attribute (it joins
Article to Publication and get's the Publication's published date).&lt;/p&gt;
&lt;p&gt;Then the urls.py is modified to change the SearchQuerySet passed to
the default haystacksearch view to order by the ArticleIndex's pub_date
attribute:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.views&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SearchView&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.query&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SearchQuerySet&lt;/span&gt;

&lt;span class="c"&gt;# query results with most recent publication date first&lt;/span&gt;
&lt;span class="n"&gt;sqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;SearchQuerySet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_by&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'-pub_date'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;urlpatterns&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;patterns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;r'^search/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="n"&gt;SearchView&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                               &lt;span class="n"&gt;load_all&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                               &lt;span class="n"&gt;searchqueryset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sqs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                               &lt;span class="p"&gt;),&lt;/span&gt;
                           &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'haystack_search'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                           &lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;snip&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;Pre-Rendering Result HTML&lt;/p&gt;
&lt;p&gt;Since I have only a few thousand records I decided to follow the
&lt;a class="reference external" href="http://docs.haystacksearch.org/dev/best_practices.html#avoid-hitting-the-database"&gt;Haystack Best Practices for Not Hitting the Database&lt;/a&gt;. This solution
trades space in the Whoosh index files by generating the HTML that will
be displayed when each article matches along with the data used by
Whoosh to match articles to search keywords. The changes were pretty
simple. In the ArticleIndex:&lt;/p&gt;
&lt;pre class="code python literal-block"&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack.sites&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;site&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;haystack&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;periodicals.models&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Article&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ArticleIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SearchIndex&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;pub_date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;model_attr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'issue__pub_date'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c"&gt;# pregenerate the search result HTML for an Article&lt;/span&gt;
    &lt;span class="c"&gt;# this avoids any database hits when results are processed&lt;/span&gt;
    &lt;span class="c"&gt;# at the cost of storing all the data in the Haystack index&lt;/span&gt;
    &lt;span class="n"&gt;result_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;indexes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;CharField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;indexed&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;use_template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Article&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ArticleIndex&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="sb"&gt;``&lt;/span&gt;
&lt;/pre&gt;
&lt;p&gt;The use_template keyword requires you to create a Django template file
that is used during index creation to build the HTML that will be
displayed. The only peculiarity I found was figuring out where the
template should live. On my system it was at
templates/search/indexes/periodicals/article_result_text.txt. I
understand the periodicals/article_result_text part but I haven't
looked into where the search/indexes is generated from. I imagine a
reverse() to find the url for the view and &amp;quot;indexes&amp;quot; is appended to
that...&lt;/p&gt;
&lt;p&gt;The final change is the template used to display the search results.
In order to not hit the database the object list generated by the
haystack SearchView is placed into the context used by the template and
only the result_text attribute should be accessed:&lt;/p&gt;
&lt;pre class="code xml literal-block"&gt;
{% if page.object_list %}
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;search-results-title&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Results &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;{{page.start_index}}&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt;  - &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;{{page.end_index}}&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&lt;/span&gt; for &lt;span class="nt"&gt;&amp;lt;b&amp;gt;&lt;/span&gt;{{query}}&lt;span class="nt"&gt;&amp;lt;/b&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;search-results-list&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
{% for result in page.object_list %}
  {{result.result_text|safe}}
{% endfor %}
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;pagination&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;step-links&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      {% if page.has_previous %}
          previous
      {% endif %}
      &lt;span class="nt"&gt;&amp;lt;span&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;&amp;quot;current&amp;quot;&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          Page {{ page.number }} of {{ page.paginator.num_pages }}
      &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
      {% if page.has_next %}
          next
      {% endif %}
    &lt;span class="nt"&gt;&amp;lt;/span&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;{% else %}&lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;No matching articles found.&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
{% endif %}
&lt;/pre&gt;
&lt;p&gt;The actual result is placed in the template via
{{result.result_text|safe}} the safe filter is required since the HTML
doesn't need to be escaped again - it was escaped by Django when it was
placed into the SearchIndex.&lt;/p&gt;
&lt;p&gt;So now my search results are in reverse chronological order and they
render using only 3 database queries and at least 10x faster than
before.&lt;/p&gt;
</summary><category term="django" /><category term="haystack" /><category term="search" /><category term="whoosh" /></entry><entry><title>Improving Google Ads and Google Search Descriptions</title><link href="http://tech.agilitynerd.com/improving-google-ads-and-google-search-descri.html" rel="alternate" /><updated>2010-08-03T01:46:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-08-03:improving-google-ads-and-google-search-descri.html</id><summary type="html">&lt;p&gt;I was looking at the google search results for my &lt;a class="reference external" href="http://googility.com/"&gt;Googility web site&lt;/a&gt;
and noticed that the descriptions shown underneath the title often
contained text from my navigation links instead of content from the body
of the page:&lt;/p&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="Google_description" src="/static/images/2010/08/13127405-google_description.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;I did some searching and found the Google Webmaster blog post about
&lt;a class="reference external" href="http://googlewebmastercentral.blogspot.com/2007/09/improve-snippets-with-meta-description.html"&gt;description meta tags&lt;/a&gt;. Since almost all of the pages on Googility are
generated by fewer than a dozen Django templates I edited the templates
and inserted meta tags and filled the description in with data from each
database entry. This avoids boilerplate information that would be
ignored by Google and improves the descriptions shown to Google
searchers. Some of my pages have already been reindexed:&lt;/p&gt;
&lt;/p&gt;&lt;div class="p_embed p_image_embed"&gt;&lt;p&gt;&lt;img alt="Google_description_after" src="/static/images/2010/08/13127843-google_description_after.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/p&gt;&lt;p&gt;Yahoo and some other search sites use a &lt;a class="reference external" href="http://help.yahoo.com/l/us/yahoo/search/indexing/slurp-14.html"&gt;class robots-nocontent&lt;/a&gt; on any
page elements it should ignore for it's index, Unfortunately, Google
doesn't follow this standard. So I might end up making that edit to the
templates also. Looking at my site's log files it appears the Yahoo
spider is hitting my site more frequently than Google's and the Yahoo
index is more up to date. Looking at my analytics reports though Google
refers far more readers to my site than Yahoo...&lt;/p&gt;
&lt;p&gt;I also noticed that the ads served on pages containing mostly links
appeared to be using words in my navigation or other boilerplate instead
of the few lines of valuable content. More searching to the rescue and I
found this &lt;a class="reference external" href="https://www.google.com/adsense/support/bin/answer.py?answer=23168"&gt;Google Adsense article on section targeting&lt;/a&gt;. Once again
editing the dozen or so templates I used were easy to edit to add in
these HTML comment tags. Checking back a couple days later showed
improvements in the ads being generated for those pages. I keep an eye
on my Adsense click rate and see if there is any increase in ad clicks.&lt;/p&gt;
&lt;p&gt;So a couple simple edits made noticeable improvements not bad for a
couple hours investigation and implementation.&lt;/p&gt;
&lt;/p&gt;</summary><category term="adsense" /><category term="django" /><category term="google" /><category term="search" /><category term="webdevelopment" /></entry><entry><title>Firediff: track and save CSS edits in Firebug</title><link href="http://tech.agilitynerd.com/firediff-track-and-save-css-edits-in-firebug.html" rel="alternate" /><updated>2010-07-25T16:36:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-07-25:firediff-track-and-save-css-edits-in-firebug.html</id><summary type="html">&lt;p&gt;When I'm making fiddly changes to a web page I like to tweak the CSS
using the Firefox Firebug plugin. It has the advantage of letting you
try changes quickly and see the effect. The downside has always been
that you had to then change the source CSS file to include your changes.
Which increases the risk that you forget to include a change.&lt;/p&gt;
&lt;p&gt;I was thinking about that recently when I changed the color scheme on my
AgilityNerd blog and went searching for a way to at least identify the
changes. Turns out there is a nice plugin that provides exactly that
functionality. The &lt;a class="reference external" href="https://addons.mozilla.org/en-US/firefox/addon/13179/"&gt;Firediff plugin&lt;/a&gt; not only tracks changes to the CSS
it also allows you to save those changes for overwriting or diff'ing
into you CSS file(s).&lt;/p&gt;
&lt;p&gt;Like other firebug related extensions you need to enable this feature
per page by clicking on the arrow next to the new Changes tab.&lt;/p&gt;
&lt;p&gt;Here is a screenshot of the plugin in action:&lt;/p&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="Firediff" src="/static/images/2010/07/12587332-firediff.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;You can see I've changed the line-height attribute - it shows the
previous and final values. A right mouse menu lets you save those
changes to a file or revert them.&lt;/p&gt;
&lt;p&gt;Another feature that can be configured in the Changes menu is whether to
track changes made by other JavaScript to the page. This is an
interesting feature if you were wondering how a JQuery or other
JavaScript modified the page for a given effect.&lt;/p&gt;
&lt;p&gt;This is a great plugin that I'll continue to use regularly.&lt;/p&gt;
</summary><category term="css" /><category term="firefoxaddon" /><category term="webdevelopment" /></entry><entry><title>Initial Release of django-stw</title><link href="http://tech.agilitynerd.com/initial-release-of-django-stw.html" rel="alternate" /><updated>2010-07-11T15:47:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-07-11:initial-release-of-django-stw.html</id><summary type="html">&lt;p&gt;I have been using the free website thumbnail service from &lt;a class="reference external" href="http://www.shrinktheweb.com?a=988"&gt;Shrink The
Web&lt;/a&gt; on my dog agility search website &lt;a class="reference external" href="http://googility.com"&gt;Googility&lt;/a&gt; since I launched it.
It is quick and easy to use and it adds a lot to the look of the pages.&lt;/p&gt;
&lt;p&gt;I had created a simple &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; template tag for inserting the little
snippet of HTML needed by their service.&lt;/p&gt;
&lt;p&gt;Recently they asked me to add support for their advanced features to my
template tag. I used this opportunity to convert my templatetag to a
Django application. This mostly makes it a lot easier to install but it
also let me to bundle tests and an example template with the template
tag.&lt;/p&gt;
&lt;p&gt;I kept the existing &lt;tt class="docutils literal"&gt;shrinkthewebimage&lt;/tt&gt; template tag and added a new
tag called &lt;tt class="docutils literal"&gt;stwimage&lt;/tt&gt; to enable the new features.&lt;/p&gt;
&lt;p&gt;I'm hosting the example page included in the package &lt;a class="reference external" href="http://googility.com/django-stw/"&gt;here&lt;/a&gt; so you can
see how the template tags work.&lt;/p&gt;
&lt;p&gt;I've hosted the &lt;a class="reference external" href="http://github.com/saschwarz/django-stw"&gt;project source on github&lt;/a&gt; and uploaded the &lt;a class="reference external" href="http://pypi.python.org/pypi/django-stw/"&gt;initial
release to the CheeseShop&lt;/a&gt; for easy installation.&lt;/p&gt;
</summary><category term="django" /><category term="github" /><category term="googility" /><category term="pypi" /><category term="python" /><category term="shrinktheweb" /><category term="webdevelopment" /></entry><entry><title>Embedding JSON Within Generated HTML</title><link href="http://tech.agilitynerd.com/embedding-json-within-generated-html.html" rel="alternate" /><updated>2010-07-08T21:03:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-07-08:embedding-json-within-generated-html.html</id><summary type="html">&lt;p&gt;Ran into an interesting problem at work this past week that had a simple
and pleasing resolution. We have an in house developed JavaScript grid
on some of our pages and when users entered some text strings we'd
generate invalid &lt;a class="reference external" href="http://www.json.org/js.html"&gt;JSON&lt;/a&gt; payloads that would give the user an error
page. If they entered strings that looked like an HTML Entity i.e. &amp;amp;#13
which (with the addition of a trailing ; ) is a non-visible HTML
character (carriage return) the text wasn't displayed in the widget. To
further complicate things some of the content displayed in the grid is
HTML which is inserted into the grid as is and can contain escaped HTML
characters.&lt;/p&gt;
&lt;p&gt;The grid gets its content as a JSON payload from within a hidden div in
the HTML which is generated via a template mechanism. Heres a portion of
the template where &amp;lt;%= and %&amp;gt; stringifying of the value of the Python
variable(s)/code they surround:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;div style=&amp;quot;display:none;&amp;quot; id=&amp;quot;grid-init-args-&amp;lt;%= count %&amp;gt;&amp;quot;&amp;gt;
  &amp;lt;textarea&amp;gt;
  &amp;lt;!-- this is the JSON payload loaded via the grid JavaScript --&amp;gt;
  &amp;lt;%= [ columnsIndex, indexColumns, columns, rowBuffer, footerRows, formulas] %&amp;gt;
  &amp;lt;/textarea&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;This approach has a number of problems:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;By using the template mechanism to create the JSON payload this
template was relying on the similarity of the string representation
of Python objects to JSON. After some testing I found the following
scenarios: If a string contained a single quote character the string
representation was a double quoted string around the text and the
single quote; a valid JSON string. If the string contained a double
quote character the string representation was a single quoted string
around the text and the double quote; &lt;a class="reference external" href="http://www.bennadel.com/blog/388-People-Please-Stop-Using-Single-Quotes-.htm"&gt;an invalid JSON string&lt;/a&gt;. If
the string contained both a single and a double quote the string
representation would be a single quoted string containing a slash
escaped single quote and the double quote; an invalid JSON string.
Depending on the browser (of course) the JSON string would fail to
parse correctly when the double quote was encountered within the
single quoted string.&lt;/li&gt;
&lt;li&gt;The JSON payload had to be HTML encoded (converting &amp;lt;, &amp;gt;, &amp;quot;, and &amp;amp;)
since it was parsed by the browser as HTML.&lt;/li&gt;
&lt;li&gt;The HTML encoding would encode or double encode HTML to be inserted
directly into the grid's DOM.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The variation in single/double quoting was an easy fix, I changed to
&lt;a class="reference external" href="http://pypi.python.org/pypi/simplejson/"&gt;simplejson&lt;/a&gt;.dumps() which correctly double quotes key/values in dicts
and escapes embedded double quotes (single quotes don't need to be
escaped). I didn't time it but with the C extension it may be faster
than the template engine for our larger datasets.&lt;/p&gt;
&lt;p&gt;I played around with (not) encoding various portions of the payload and
then it hit me that I should change the grid to get its payload from a
non HTML element so that only HTML destined for insertion into the DOM
would be HTML encoded (which is as you'd expect for normal HTML
handling). I started changing the payload to be stored in JavaScript
generated in the template but didn't like the impact the change would
have on all the existing templates. So I started Googling and found &lt;a class="reference external" href="http://www.bennadel.com/blog/1603-jQuery-And-Script-Tags-As-Data-Containers.htm"&gt;Ben
Nadel's blog post on using script tags as data containers&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So here's my solution:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;div style=&amp;quot;display:none;&amp;quot; id=&amp;quot;grid-init-args-&amp;lt;%= count %&amp;gt;&amp;quot;&amp;gt;
&amp;lt;script type=&amp;quot;application/json&amp;quot;&amp;gt;
&amp;lt;%= simplejson.dumps([ columnsIndex, indexColumns, columns, rowBuffer, footerRows, formulas]) %&amp;gt;
&amp;lt;/script&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/pre&gt;
&lt;p&gt;There were two changes:&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Used &lt;tt class="docutils literal"&gt;simplejson.dumps&lt;/tt&gt; to correctly double quote and escape double
quotes within the variables in the payload.&lt;/li&gt;
&lt;li&gt;Change the textarea to a script element.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;By converting to a script tag within the hidden div the HTML parser no
longer parsed the content of the JSON payload. so the JSON payload only
needed to HTML encode HTML elements that were being inserted into the
DOM created by the grid.&lt;/p&gt;
&lt;p&gt;This change also meant I was able to delete the unnecessary HTML
encoding of non-HTML JSON payload data. Got to love solutions that
involve deleting code.&lt;/p&gt;
&lt;p&gt;Ultimately, we'll convert to loading the JSON payload as a separate AJAX
request from the page to the server, but for now this simplifies the
markup and handles all types of user input and HTML encoded characters
correctly.&lt;/p&gt;
</summary><category term="html" /><category term="javascript" /><category term="json" /><category term="python" /><category term="webdevelopment" /></entry><entry><title>Django Shrink The Web Template Tag Updated</title><link href="http://tech.agilitynerd.com/django-shrink-the-web-template-tag-updated.html" rel="alternate" /><updated>2010-07-01T23:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-07-01:django-shrink-the-web-template-tag-updated.html</id><summary type="html">&lt;p&gt;I recently updated my &lt;a class="reference external" href="http://djangoproject.com/"&gt;Django&lt;/a&gt; template tag for simplifying the use of
&lt;a class="reference external" href="http://www.shrinktheweb.com/"&gt;Shrink The Web&lt;/a&gt; images. They recently announced a CDN based
distribution of images and they took the opportunity to modify their
API.&lt;/p&gt;
&lt;p&gt;The updated template tag &lt;a class="reference external" href="http://djangosnippets.org/snippets/1744/"&gt;is on django snippets&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The STW folks have asked be to extend my template tag with support for
&lt;a class="reference external" href="http://www.shrinktheweb.com/content/shrinktheweb-pagepix-documentation.html"&gt;their PRO features&lt;/a&gt;. With luck I'll make that available sometime this
weekend.&lt;/p&gt;
</summary><category term="django" /><category term="python" /><category term="shrinktheweb" /><category term="webdevelopment" /></entry><entry><title>Multiple YouTube Videos per page using VideoLightBox</title><link href="http://tech.agilitynerd.com/multiple-youtube-videos-per-page-using-videol.html" rel="alternate" /><updated>2010-06-04T22:58:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-06-04:multiple-youtube-videos-per-page-using-videol.html</id><summary type="html">&lt;p&gt;I decided to stop displaying the default &lt;a class="reference external" href="http://youtube.com"&gt;YouTube&lt;/a&gt; video players within
posts on my &lt;a class="reference external" href="http://agilitynerd.com/"&gt;AgilityNerd blog&lt;/a&gt; and I started looking for a light boxed
player. Their were two main reasons. The smallest video playback window
provided by YouTube for HD videos is too wide for my two column layout
and now that I'm posting more videos the load time of the page is
delayed by the communication with all the off site webservers; serving
the YouTube static image of the video will be much faster/lighter
weight.&lt;/p&gt;
&lt;p&gt;I looked around and really liked the lightbox containing the default
YouTube player provided by &lt;a class="reference external" href="http://videolightbox.com"&gt;VideoLightBox&lt;/a&gt; and started playing around
with their demo. VideoLightBox (VLB) has an interesting approach. You
download an application (PC or Mac), configure how you want your
video(s) to look and it generates a directory of files on your local
disk (or uploads files to your website via FTP) along with an index.html
file from which you copy the code to put in the &amp;lt;head&amp;gt; and &amp;lt;body&amp;gt; of
your web page. For YouTube it also downloads a static image for each
selected video which is used as the image link within the HTML page.
Straight forward and works well.&lt;/p&gt;
&lt;p&gt;For my purposes there was a problem with their approach, its locates the
image used to launch the light box using an element id. This assumes a
single video or gallery of videos per web page. On my blog's main page
or the category pages there will be multiple videos (possibly multiple
videos within a single post). I figured a little bit of CSS and JQuery
hacking would solve the problem and it did.&lt;/p&gt;
&lt;p&gt;I decided to modify their HTML/CSS/JS to use a CSS class instead of an
element id to allow for multiple videos per page. At first I just
modified the generated files. Then I saw that VLB has template files in
their installation. So I started modifying the templates to output the
new code. Two hours later I bailed. Using &lt;a class="reference external" href="http://technet.microsoft.com/en-us/sysinternals/bb896645.aspx"&gt;procmon&lt;/a&gt; it looks like the
client app reads the template files but then doesn't actually use the
files to generate the output files(?). I was only able to modify one of
the three template files that needed to change and have it effect the
generated files.&lt;/p&gt;
&lt;p&gt;I'm going to provide my edits to the VLB developers in case they are
interested.&lt;/p&gt;
&lt;p&gt;So the solution is to edit one of the template files and then edit two
of the generated files; not ideal but once you put the generated files
on your webserver you'll probably not touch them unless you are changing
CSS styles. The modifications aren't hard but you need to be careful and
typos will definitely break things. You should backup the VLB directory
before your start or be prepared to uninstall and reinstall from their
installation program.&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Navigate to the VideoLightBox directory (i.e. C:Program Files
(x86)VideoLightBox)&lt;/li&gt;
&lt;li&gt;Change the permissions on the templates subdirectory to give your
user full access to overwrite the files&lt;/li&gt;
&lt;li&gt;For each directory in the templates subdirectory open the
videolightbox.js file in an programming editor (a keyboard macro
makes this trivial):&lt;ol class="arabic"&gt;
&lt;li&gt;Globally replace $(&amp;quot;#videogallery a[rel]&amp;quot;) with
$(&amp;quot;.videogallery a[rel]&amp;quot;).each(function(idx){$(this)&lt;/li&gt;
&lt;li&gt;Go to the end of the line and add });&lt;/li&gt;
&lt;li&gt;Save the file&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then generate the output files using the VLB executable for one or more
videos, saving the results to your local file system&lt;/p&gt;
&lt;ol class="arabic simple"&gt;
&lt;li&gt;Navigate to the output directory&lt;/li&gt;
&lt;li&gt;Open the index.html file in an programming editor&lt;ol class="arabic"&gt;
&lt;li&gt;Globally replace #videogallery with .videogallery&lt;/li&gt;
&lt;li&gt;Globally replace id=&amp;quot;videogallery&amp;quot; with class=&amp;quot;videogallery&amp;quot;&lt;/li&gt;
&lt;li&gt;Save the file&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;In the engine/css subdirectory open the videolightbox.css file in an
programming editor&lt;ol class="arabic"&gt;
&lt;li&gt;Globally replace #videogallery with .videogallery&lt;/li&gt;
&lt;li&gt;Save the file&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Then you can copy the files just as specified by the VLB installation
instructions.&lt;/p&gt;
&lt;p&gt;The other change I'll be making for my deployment is to rename the video
images. They are named 0.png, 1.png, etc. I'm going to put them all in a
directory on my resource webserver so I'll rename the files and their
references in the code copied from the index.html to use the YouTube
video id.&lt;/p&gt;
&lt;p&gt;I'll be changing my existing web posts over to this new scheme over
time...&lt;/p&gt;
</summary><category term="css" /><category term="html" /><category term="javascript" /><category term="jquery" /><category term="video" /><category term="visuallightbox" /><category term="webdevelopment" /><category term="youtube" /></entry><entry><title>Blosxom hitcounter, favorites and lastread plugins for MongoDB</title><link href="http://tech.agilitynerd.com/blosxom-hitcounter-favorites-and-lastread-plugins-for-mongodb.html" rel="alternate" /><updated>2010-05-17T10:48:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2010-05-17:blosxom-hitcounter-favorites-and-lastread-plugins-for-mongodb.html</id><summary type="html">&lt;p&gt;When I wrote my plugins for tracking the visits to each page
(&lt;tt class="docutils literal"&gt;hitcounter&lt;/tt&gt;), presenting the counts by popularity (&lt;tt class="docutils literal"&gt;favorites&lt;/tt&gt;), and
displaying the most recently read posts (&lt;tt class="docutils literal"&gt;lastread&lt;/tt&gt;) I used Perl's
&lt;tt class="docutils literal"&gt;Storable&lt;/tt&gt; module as a simple way to serialize the data structure to and
from disk. At the time I wasn't too concerned with performance, my blog
&lt;a class="reference external" href="http://agilitynerd.com"&gt;agilitynerd.com&lt;/a&gt; didn't suffer under the load of the disk reads/writes.&lt;/p&gt;
&lt;p&gt;But periodically I get DOS'd and my website becomes unresponsive. Not
too suprising since Blosxom has to run as a plain CGI script; but I'm
certain the disk writes of these plugins weren't helping. Occasionally
the Storable on disk would become corrupted which would take down the
site (not enough exception handling in the plugins).&lt;/p&gt;
&lt;p&gt;I decided to go with &lt;a class="reference external" href="http://mongodb.org"&gt;Mongo&lt;/a&gt; as the backing data store to remove the
disk reads/write from the cgi script. It supports an increment operation
(ala memcached) so multiple threads can update the hit count and MongoDB
will &amp;quot;do the right thing&amp;quot;. It also has a query API that made rewriting
favorites and lastread trivial.&lt;/p&gt;
&lt;div style="float:right;padding 20px;"&gt;&lt;p&gt;&lt;img alt="Poweredmongodbgreen75" src="/static/images/2010/05/9077760-PoweredMongoDBgreen75.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;I haven't tested the performance but my website, especially the
favorites page, seems faster. Once I write a JavaScript client to update
the hitcounts I'll be able to move the website to a fully cached
deployment while still tracking hits which should make the load of
running the agilitynerd blog very low.&lt;/p&gt;
&lt;p&gt;I've hosted the plugins on github:
&lt;a class="reference external" href="http://github.com/saschwarz/blosxom-mongodb-plugins"&gt;http://github.com/saschwarz/blosxom-mongodb-plugins&lt;/a&gt; The mongohitcounter
plugin can be configured to automatically import existing count data
from the hitcounter plugin's Storable. So conversion to the new plugins
just requires updating the template's variables to the new plugin names.&lt;/p&gt;
</summary><category term="blosxom" /><category term="mongodb" /><category term="perl" /><category term="webdevelopment" /></entry><entry><title>Using django-sitemap with django-tagging</title><link href="http://tech.agilitynerd.com/using-django-sitemap-with-django-tagging.html" rel="alternate" /><updated>2009-11-27T17:33:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2009-11-27:using-django-sitemap-with-django-tagging.html</id><summary type="html">&lt;p&gt;I was adding &lt;a class="reference external" href="http://docs.djangoproject.com/en/dev/ref/contrib/sitemaps/"&gt;django-sitemap&lt;/a&gt; to &lt;a class="reference external" href="http://googility.com/"&gt;googility.com&lt;/a&gt; yesterday and found
that Tags don't implement &lt;tt class="docutils literal"&gt;get\_absolute\_url()&lt;/tt&gt;. Which makes sense since
the site developer would want to decide how to expose them in the URL
space.&lt;/p&gt;
&lt;p&gt;It is also arguable that links to pages displaying the tag view already
exist in the page for models that are already in the sitemap so they
don't need to be put in the sitemap explicitly. For example, a page for
an Article might be at &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;/article/django-11-release&lt;/span&gt;&lt;/tt&gt; and that page would
contain the links to pages linked with the tags for that article e.g.
&lt;tt class="docutils literal"&gt;/tag/django/&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;/tag/python/&lt;/tt&gt;&lt;/p&gt;
&lt;p&gt;But I figured having the tag pages indexed by Google would be useful. It
also allows a different priority to be specified for the pages. So I
made a little class that derives from &lt;tt class="docutils literal"&gt;GenericSitemap&lt;/tt&gt; that allows the url
and suffix for the Tag name to be specified:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
class SlugSitemap(GenericSitemap):
&amp;quot;&amp;quot;&amp;quot;Use for objects that don't implement get_absolute_url
   but have a slug field used in creating their url&amp;quot;&amp;quot;&amp;quot;

def __init__(self, info_dict, priority=None, changefreq=None):
    GenericSitemap.__init__(self, info_dict,
                            priority=priority,
                            changefreq=changefreq)
    self.url = info_dict.get('url', '/')
    self.slugfield = info_dict['slugfield']
    self.suffix = info_dict.get('suffix', '')

def location(self, obj):
    return &amp;quot;%s%s%s&amp;quot; % (self.url,
                       getattr(obj, self.slugfield),
                       self.suffix)
&lt;/pre&gt;
&lt;p&gt;Here's how I use it:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sitemaps = {'tag_detail': SlugSitemap({'queryset':Tag.objects,
                                       'url':'/tag/',
                                       'slugfield':'name',
                                       'suffix':'/'},
                                       changefreq='monthly',
                                       priority='0.5'),
}
&lt;/pre&gt;
&lt;p&gt;The urls for tags are at /tag/&lt;em&gt;slugname&lt;/em&gt;/ where /tag/ is prepended to
tag.name and / is appended to the end&lt;/p&gt;
&lt;p&gt;This class can be used to create sitemap entries for any url
parameterized on a single field of an instance returned by the QuerySet.&lt;/p&gt;
</summary><category term="django" /><category term="djangositemap" /><category term="djangotagging" /><category term="python" /><category term="webdevelopment" /></entry><entry><title>Firefox Web Developer Add-on AJAX NON-Caching Problem</title><link href="http://tech.agilitynerd.com/firefox-web-developer-add-on-ajax-non-caching-1.html" rel="alternate" /><updated>2008-12-30T04:08:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-12-30:firefox-web-developer-add-on-ajax-non-caching-1.html</id><summary type="html">&lt;p&gt;I ran across some &amp;quot;interesting&amp;quot; behavior of the Firefox &lt;a class="reference external" href="https://addons.mozilla.org/en-US/firefox/addon/60"&gt;Web Developer
Add-on&lt;/a&gt; today. When editing JavaScript code I normally leave Web
Developer set to &amp;quot;disable cache&amp;quot;. This causes all images and,
significantly for my purposes, the JavaScript files to be downloaded
from the server on every page request. But I ran into a unexpected
problem with this setting that cost me a couple hours today.&lt;/p&gt;
&lt;p&gt;I was working on a legacy page today that uses an AJAX POST and the
response is sent to an IFRAME. The POST was successful, the results were
valid and present in the IFRAME. The problem was as soon as the content
of the IFRAME was eval'd() a GET was being sent to the server for the
URL of the page. This GET was a problem because it had a side effect of
clearing the session data (which contained the POSTed AJAX data) for the
current page. So when the page's FORM was finally POSTed the server
couldn't find the data in the session.&lt;/p&gt;
&lt;p&gt;After trying numerous code changes including disabling event handlers
and events, it finally occurred to me that the cache disable feature of
the add-on might be causing the unexpected GET. Sure enough, once I
re-enabled caching the GET stopped being sent.&lt;/p&gt;
&lt;p&gt;It was strange that the &lt;tt class="docutils literal"&gt;eval()&lt;/tt&gt; caused the GET to be requested. All
the other code around processing the IFRAME's data didn't trigger it,
only the eval. Strange but true.&lt;/p&gt;
&lt;p&gt;So just something to keep in mind when strange behavior occurs on AJAX
pages where session data is being used.&lt;/p&gt;
</summary><category term="ajax" /><category term="firefox" /><category term="firefoxaddon" /><category term="webdevelopment" /></entry><entry><title>reCAPTCHA in Django</title><link href="http://tech.agilitynerd.com/recaptcha-in-django-1.html" rel="alternate" /><updated>2008-12-23T22:04:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-12-23:recaptcha-in-django-1.html</id><summary type="html">&lt;p&gt;I first read about &lt;a class="reference external" href="http://recaptcha.net/"&gt;ReCAPTCHA&lt;/a&gt; in &lt;a class="reference external" href="http://www.wired.com/techbiz/it/magazine/15-07/ff_humancomp?currentPage=all"&gt;this article in Wired magazine&lt;/a&gt;
last year.&lt;/p&gt;
&lt;p&gt;reCAPTCHA provides a free CAPTCHA web service that
pairs together two words from OCR scanned books. One of the words is
known and the other couldn't be recognized. The user types in both words
not knowing which is unknown to the system. As reCAPTCHA collects the
responses for the unknown word they get human verified character
recognition. So the millions of users of the system are clearing up
millions of unrecognized words. It is a very clever human &amp;quot;cloud
computing&amp;quot; system using only seconds of human effort for each use of the
system.&lt;/p&gt;
&lt;div class="thumbnail" style="float:right;"&gt;&lt;p&gt;&lt;img alt="recaptchalogo" src="/static/images/2009/11/5014940-media_httpdataagilitynerdcomimagesrecaptchalogogif_IkgnpackrkjkaCy.gif" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;I'm using a &lt;a class="reference external" href="/comment-spam-and-wbcaptcha-plugin-enhancement-1.html"&gt;FIGLet based ASCII CAPTCHA&lt;/a&gt;on my websites since it was
easy to integrate into the &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; writeback plugin. But I wanted to
give reCAPTCHA a try while converting my &lt;a class="reference external" href="http://googility.com/"&gt;Googility&lt;/a&gt; site to &lt;a class="reference external" href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;.
&lt;a class="reference external" href="http://seeknuance.com/"&gt;John DeRosa&lt;/a&gt; made my job trivial by &lt;a class="reference external" href="http://seeknuance.com/2008/03/18/integrating-recaptcha-with-django/"&gt;writing up the steps with a clear example&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So I followed his directions which involved installing the
recaptcha-client python library on my dev and production systems and
obtaining a free public/private license key from the reCAPTCHA site.
Then I updated my Django view and template files for the one form that
needed CAPTCHA protection. It was dead simple and working within
minutes. The only minor addition I'd make to John's article is of course
you need to pass the &lt;tt class="docutils literal"&gt;captcha_error&lt;/tt&gt; variable from your view to the
template:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
return render_to_response('edit.html', {'form': form, 'captcha_error':captcha_error})
&lt;/pre&gt;
&lt;p&gt;So give reCAPTCHA a try for your next project. It was so easy to do I
might even convert my Blosxom blogs to use it via Lars Engel's
&lt;a class="reference external" href="http://blog.berlund.de/public/other/recaptcha"&gt;recaptcha plugin&lt;/a&gt;.&lt;/p&gt;
</summary><category term="django" /><category term="recaptcha" /></entry><entry><title>Improving Site Navigation - Add Titles to Blosxom Pages With storytitle</title><link href="http://tech.agilitynerd.com/improving-site-navigation-add-titles-to-blosx-1.html" rel="alternate" /><updated>2008-06-20T22:03:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-06-20:improving-site-navigation-add-titles-to-blosx-1.html</id><summary type="html">&lt;p&gt;I've always wanted my &lt;a class="reference external" href="http://agilitynerd.com/blog/"&gt;AgilityNerd&lt;/a&gt; blog to serve as a reference for
interesting dog agility subjects. Consequently, I'm interested in making
it as easy as possible for readers to locate, explore, and learn more
about the sport. So improving navigation is a strong interest to me.&lt;/p&gt;
&lt;p&gt;I have been reading &lt;a class="reference external" href="http://www.amazon.com/Designing-Web-Navigation-Optimizing-Experience/dp/0596528108?t=agili-20"&gt;Designing Web Navigation by James Kalbach&lt;/a&gt;. In the
section discussing Browser Mechanisms Kalbach discusses the back and
forward buttons and the Session History drop down. The session history
is usually a menu that drops down from the back button (or near it). It
lists the pages the user has visited in reverse order showing their page
titles. Kalbach states: &amp;quot;Session history is a good reason to supply
meaningful browser titles&amp;quot;. That reminded me that for my blog the
session history has always just displayed &amp;quot;AgilityNerd&amp;quot; for each page
visited on my site. I was leaving out a valuable aid to my readers who
use the session history to find where they had been previously.&lt;/p&gt;
&lt;p&gt;Another benefit of setting the title in pages is in tracking site
statistics. It had always bothered me that GoogleAnalytics never showed
statistics for my pages by page title.&lt;/p&gt;
&lt;p&gt;So these two things finally moved me to take some action. I had been
thinking about writing a &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; plugin to set the title element in
the HTML head section but I've always been too busy. Luckily I didn't
have to.&lt;/p&gt;
&lt;p&gt;I found out about the storytitle plugin from Nick Leverton's
&lt;a class="reference external" href="http://www.leverton.org/blosxom"&gt;Serendipity&lt;/a&gt; blog. He had made some changes and released &lt;a class="reference external" href="http://www.leverton.org/blosxom/Software/Projects/Blosxom/storytitle.html"&gt;version 0.7
of the plugin&lt;/a&gt;. It was trivial to setup, I just followed the directions
in the plugin and had this site and agilitynerd.com/blog working within
minutes.&lt;/p&gt;
&lt;p&gt;So if you are a Blosxom user just grab this plugin. Otherwise, take the
time to set your page title sections so you can help your readers and
help analyzing your own site's statistics&lt;/p&gt;
</summary><category term="agilitynerd" /><category term="blosxom" /><category term="googleanalytics" /></entry><entry><title>Blosxom Plugin lastcommented Enhancement</title><link href="http://tech.agilitynerd.com/blosxom-plugin-lastcommented-enhancement-1.html" rel="alternate" /><updated>2008-02-29T18:50:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-02-29:blosxom-plugin-lastcommented-enhancement-1.html</id><summary type="html">&lt;p&gt;I had a comment entered on my &lt;a class="reference external" href="http://agilitynerd.com/blog"&gt;dog agility blog&lt;/a&gt; that I
subsequently deleted and of course my &lt;tt class="docutils literal"&gt;lastcommented&lt;/tt&gt; plugin recorded
the comment as the most recent comment. Since the plugin uses
&lt;tt class="docutils literal"&gt;Storable&lt;/tt&gt; to store the data I couldn't manually edit the file to
remove the entry.&lt;/p&gt;
&lt;p&gt;So I modified my &lt;tt class="docutils literal"&gt;lastcommented&lt;/tt&gt; plugin to allow you to specify the,
base zero, index of the entry on the URL and delete the entry. For
example to delete the second entry in the list (index 1):&lt;/p&gt;
&lt;blockquote&gt;
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;http://example.com/blog/?lastcommentremove=1&lt;/span&gt;&lt;/tt&gt;&lt;/blockquote&gt;
&lt;p&gt;To keep mischief makers from deleting all your comments the plugin has a
variable in its configuration section called &lt;tt class="docutils literal"&gt;lastcommentremove&lt;/tt&gt; that
you set to a non zero value to enable this feature. Since this is
probably a relatively infrequent activity (if you have blacklisting or
wbcaptcha comment spam protection enabled) I figured this was a
sufficient mechanism.&lt;/p&gt;
&lt;p&gt;You can &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/lastcommented"&gt;download the latest version of this plugin here&lt;/a&gt;.&lt;/p&gt;
&lt;/p&gt;</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Minor Additional Mods to Blosxom moreenties Plugin</title><link href="http://tech.agilitynerd.com/minor-additional-mods-to-blosxom-moreenties-p-1.html" rel="alternate" /><updated>2008-02-24T16:24:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-02-24:minor-additional-mods-to-blosxom-moreenties-p-1.html</id><summary type="html">&lt;p&gt;I had previously extended Jason Clark's &lt;a class="reference external" href="http://jclark.org/weblog/WebDev/Blosxom/plugins/moreentries"&gt;moreentries&lt;/a&gt; plugin for
&lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; to allow adding images to links to the previous and next
group of articles/entries in the head or foot of a Blosxom weblog. While
attempting to have valid HTML on my blogs I found that I had left an img
tag unclosed. I also found that it wasn't as easy to change the styling
of the links as I had originally planned.&lt;/p&gt;
&lt;p&gt;So I've closed the open img tags and added the configuration variable
&lt;tt class="docutils literal"&gt;$selfstyle&lt;/tt&gt; into which you can place any CSS style information you'd
like added to the &lt;tt class="docutils literal"&gt;td&lt;/tt&gt; containing each page link. For example:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
# Set to the CSS style for $moreentries::links
$selfstyle = 'style=&amp;quot;padding: 0 3px;&amp;quot;'; # set to '' for no style
&lt;/pre&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>New Scheme Using YouTube to Get "Google Juice"?</title><link href="http://tech.agilitynerd.com/new-scheme-using-youtube-to-get-google-juice-1.html" rel="alternate" /><updated>2008-02-15T04:32:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-02-15:new-scheme-using-youtube-to-get-google-juice-1.html</id><summary type="html">&lt;p&gt;In the last couple days my YouTube account has fallen victim to a new
scheme by porn websites attempting to garner Google Juice. It is pretty
straightforward. A generic YouTube account is created with a user name
like &amp;quot;babiegirl123xyz&amp;quot; and the &amp;quot;user&amp;quot; has a single line profile with the
line &amp;quot;all my pics are at the link below&amp;quot;. The link is to an adult
sex/dating site.&lt;/p&gt;
&lt;p&gt;All by itself creating a bogus YouTube account with a link to your site
won't get a site listed any higher in a Google search. But the clever
part of this approach is the &amp;quot;user&amp;quot; subscribes to many YouTube channels.
Since YouTube users often have channel pages with links to all of their
subscribers, the more subscriptions the bogus user creates; the more
incoming links they have to their profile and consequently to their
adult website. But I'd think the Google algorithm wouldn't weigh links
from YouTube too heavily anyway...&lt;/p&gt;
&lt;p&gt;I came across this scheme because I got several subscription emails from
variants of the babiegirl accounts all with the same template of a
profile page and all linking to the same website. It doesn't really
matter much to me that they go to this trouble, but it is strange since
I don't display a list of my subscribers on my channel so they don't get
any real benefit.&lt;/p&gt;
&lt;p&gt;I guess another way this scheme could help increase traffic would be
through the subscription emails. Heck I clicked on the links to see what
the site was about. It could be there are enough folks who do the same
in response to the subscription email. If only a few end up becoming
members of the adult sites it could make this a worthwhile means for
these sites to get more page hits/subscribers.&lt;/p&gt;
&lt;p&gt;Sigh...&lt;/p&gt;
&lt;/p&gt;</summary><category term="google" /><category term="youtube" /></entry><entry><title>Web Site Themes for Techies</title><link href="http://tech.agilitynerd.com/web-site-themes-for-techies-1.html" rel="alternate" /><updated>2008-02-04T05:34:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-02-04:web-site-themes-for-techies-1.html</id><summary type="html">&lt;p&gt;I'm no graphic artist so when it comes time to create or change the look
of my website I always have a difficult time. So I've discovered some
tools and tips that might help other non artists create reasonable
looking websites.&lt;/p&gt;
&lt;p&gt;Here's the general approach:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Select an existing template or layout to use as a framework&lt;/li&gt;
&lt;li&gt;Select a photo or logo that represents the theme/color scheme of the
website&lt;/li&gt;
&lt;li&gt;Create a background or background image using a sample of the
photo/logos color&lt;/li&gt;
&lt;li&gt;Sample colors from the background, photo or logo for highlighting
other elements of the website&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'll present how I applied this approach to my latest website makeover
and I'll discuss a some websites/tools I found helpful in these areas.&lt;/p&gt;
&lt;div class="section" id="selecting-a-layout"&gt;
&lt;h2&gt;Selecting a Layout&lt;/h2&gt;
&lt;p&gt;Unfortunately I don't have any suggestions for templates for specific
blog engines (apart from searching with Google). But I'll try to make up
for that with my next discovery.&lt;/p&gt;
&lt;p&gt;If you are familiar with using CSS for website customization Alessandro
Fulciniti has created a set of 40 one, two, and three column layouts on
&lt;a class="reference external" href="http://blog.html.it/layoutgala/"&gt;his website&lt;/a&gt;. They are straight forward to follow if you've done any
CSS coding. If you haven't please skip ahead to the next section for
some other tips that might be useful.&lt;/p&gt;
&lt;p&gt;I have to admit to having some problem retrofitting my old CSS design.
My issue was with the rendering of the right hand column. When the
negative offset for the column was larger than the right margin for the
containing div the right column would be rendered at the bottom of the
page. It would look OK in Firefox but not in IE.&lt;/p&gt;
&lt;p&gt;Of course it is important to test any new layout in at least Internet
Explorer and Firefox. Now that Safari is available for Windows it is
easy to test on that browser as well.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="select-a-photo-or-logo"&gt;
&lt;h2&gt;Select a Photo or Logo&lt;/h2&gt;
&lt;p&gt;If you already have a color logo that you use for your business then you
can help reinforce that logo by using those colors in your site design.
Since my logo is currently black and white that didn't help me much.&lt;/p&gt;
&lt;p&gt;I was looking to change my site to reflect the winter season so I went
to &lt;a class="reference external" href="http://www.sxc.hu/"&gt;StockXCHNG&lt;/a&gt; and found a nice royalty free &lt;a class="reference external" href="http://www.sxc.hu/photo/911036"&gt;snow scene&lt;/a&gt; by
photographer &lt;a class="reference external" href="http://www.sxc.hu/profile/ijsendoorn"&gt;ijsendoorn&lt;/a&gt;. There are thousands of interesting photos
suitable for use in blogs hosted on Stock.XCHNG, it is a great resource
and I heartily thank the photographers who post their work.&lt;/p&gt;
&lt;p&gt;I then used the free &lt;a class="reference external" href="http://www.gimp.org/"&gt;Gimp&lt;/a&gt;image editing software to select a section
of the appropriate size for the header of my site. The Gimp is easy to
use and there is a fair amount of online help available if you are doing
something tricky. Of course you can use Photoshop or a similar program
if you have it.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="create-a-background"&gt;
&lt;h2&gt;Create a Background&lt;/h2&gt;
&lt;p&gt;This is an area where I was really at a loss. I've never been good at
creating graphics, and a tileable image is even harder to create. There
are some tutorials you can find via Google but I just wasn't up for the
challenge. Of course you can use a complementary solid color background
by sampling your photo or logo (see the next section).&lt;/p&gt;
&lt;p&gt;Fortunately I came across a software program called &lt;a class="reference external" href="http://www.ransen.com/Gliftex/Default.htm"&gt;Gliftex - An
Infinity of Tileable Graphics Designs&lt;/a&gt;. This is an interesting program
that can create a multitude of different images suitable for tiling
across the background of a web page. Gliftex has a number of algorithms
for choosing the form, color scheme, and interpretation and generating
images. &lt;a class="reference external" href="http://graphicssoft.about.com/od/productreviews/l/aaglifticintro.htm"&gt;Here is a good overview&lt;/a&gt; of the product along with some
examples.&lt;/p&gt;
&lt;p&gt;Gliftex has a feature that makes it perfect for creating a tileable
background image for a website, it can sample an image and use the
colors in the image as its palette of colors. So I took my snow scene
section and had Gliftex sample it. I pressed the Form and Interpretation
buttons a number of times and after a while came up with these
understated and simple tileable images:&lt;/p&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="image0" src="http://data.agilitynerd.com/images/bluebg05.png" /&gt;
&lt;img alt="image1" src="http://data.agilitynerd.com/images/bluebg03.png" /&gt;
&lt;img alt="image2" src="http://data.agilitynerd.com/images/bluebg02.png" /&gt;
&lt;img alt="image3" src="http://data.agilitynerd.com/images/bluebg01.png" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;As far as I'm concerned the $ 50 US for the Gliftex Home download was a
great investment. They have a free demo version available for download
if you want to play around before you buy.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="sample-colors-for-highlights"&gt;
&lt;h2&gt;Sample Colors for Highlights&lt;/h2&gt;
&lt;p&gt;So now I had an image for the header and a repeating image for the
background. I just needed to add some matching highlights for some other
areas of my blog to tie the whole theme together. I decided to color the
headings of my right and left sidebars to match one of the darker colors
of the header image. But how to pick out colors from the image?&lt;/p&gt;
&lt;p&gt;Gimp to the rescue again. I loaded my image again and selected Tools &amp;gt;
Color Picker. You are then presented with an eye dropper pointer. I just
clicked on interesting colors in my image to obtain a color value that I
could type into my theme's style sheet for the background colors. In the
picture below you can see the color value is &amp;quot;bec7d0&amp;quot; which you would
write as #bec7d0 in the style sheet.&lt;/p&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="image4" src="http://data.agilitynerd.com/images/GimpColorPicker.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
&lt;div class="section" id="results"&gt;
&lt;h2&gt;Results&lt;/h2&gt;
&lt;p&gt;While there are an many additional changes to my theme I could make I am
pretty happy with the cool, blue-ish, wintry theme I've created:&lt;/p&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="image5" src="http://data.agilitynerd.com/images/blog_snow_theme_thumb.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;Take a look at &lt;a class="reference external" href="http://agilitynerd.com/blog/"&gt;AgilityNerd&lt;/a&gt;to see it in action or, depending on when
you read this, to see my next seasonal theme. I hope some of these tips
might be helpful for helping you create your next theme.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="sitedesign" /></entry><entry><title>Stock.XCHNG - Free Stock Photos</title><link href="http://tech.agilitynerd.com/stockxchng-free-stock-photos-1.html" rel="alternate" /><updated>2008-01-19T19:41:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-01-19:stockxchng-free-stock-photos-1.html</id><summary type="html">&lt;p&gt;I was looking at templates for &lt;a class="reference external" href="http://www.zen-cart.com/"&gt;ZenCart&lt;/a&gt; and came across some templates
that use images from &lt;a class="reference external" href="http://www.sxc.hu/"&gt;Stock.XCHNG&lt;/a&gt;. Over an hour ago I started browsing
the images and am really impressed with the quality and breadth of
photos that artists are making available for free use.&lt;/p&gt;
&lt;p&gt;I'll definitely use some of these images to spice up my sites.&lt;/p&gt;
</summary><category term="sitedesign" /></entry><entry><title>Feed2JS - Adding AgilityNerd Headlines to Your Website</title><link href="http://tech.agilitynerd.com/feed2js-adding-agilitynerd-headlines-to-your-1.html" rel="alternate" /><updated>2008-01-11T22:24:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-01-11:feed2js-adding-agilitynerd-headlines-to-your-1.html</id><summary type="html">&lt;p&gt;&lt;strong&gt;Updated 16-Aug-2006 to reflect Feed2JS's move to http://feed2js.org&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I never thought anyone would want me to &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Web_syndication"&gt;syndicate&lt;/a&gt; my blog and put
links to my latest articles on their website. But Marj Kibby in
Australia with her very nice dog raising and training blog &lt;a class="reference external" href="http://marjkibby.blogspot.com/"&gt;Choose and
Raise a Puppy&lt;/a&gt; did just that. So I started thinking about how I might
provide an HTML feed that could be included into blogs using client-side
JavaScript as not all web/blog writers are developers who can integrate
this data on the server side.&lt;/p&gt;
&lt;p&gt;Well Google to the rescue: the &lt;a class="reference external" href="http://feed2js.org/"&gt;Feed To JavaScript&lt;/a&gt; Open Source project
provides a really great service that exactly fits the bill. If you have
a website or blog and wish to list the latest articles from another
website you can just fill out a form on the Feed2JS site and get the
HTML to paste into your website. All you do is supply the RSS feed from
the source site to Feed2JS, select the options and click Preview or
Generate JavaScript to get the code you need. Feed2JS does all the
&amp;quot;heavy lifting&amp;quot;.&lt;/p&gt;
&lt;p&gt;Here is an example of the generated HTML as it would appear in your
website using the current data from this site:&lt;/p&gt;
&lt;p&gt;The code for the above is a single (long) line of code:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
&amp;lt;script src=&amp;quot;http://feed2js.org/feed2js.php?src=http%3A%2F%2Fagilitynerd.com%2Fblog%2Findex.rss10&amp;amp;chan=y&amp;amp;desc=0&amp;amp;date=n&amp;quot; type=&amp;quot;text/javascript&amp;quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/pre&gt;
&lt;p&gt;There are many other ways to configure/display the information from
AgilityNerd on your site. Just go to:
&lt;a class="reference external" href="http://feed2js.org/index.php?s=build"&gt;http://feed2js.org/index.php?s=build&lt;/a&gt; and enter:
&lt;a class="reference external" href="http://agilitynerd.com/blog/index.rss10"&gt;http://agilitynerd.com/blog/index.rss10&lt;/a&gt; in the URL text entry box. Then
answer the other questions.&lt;/p&gt;
&lt;p&gt;Feed2JS kindly caches the latest RSS feed from the source site you
specify and replies with the above HTML each time someone visits a page
on your site. This has the added advantage of not taxing the webserver
of the source site to feed your website; which is nice for sites like
this one with a small monthly bandwidth quota.&lt;/p&gt;
&lt;p&gt;Feed2JS also has a number of ways to style the HTML that is delivered so
you can change the look of the links to match your site. Of course, web
developers fluent in CSS can just style the HTML directly.&lt;/p&gt;
&lt;p&gt;For &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; bloggers I've been playing around developing a new plugin
based on the headlines plugin to provide this same functionality. I hope
to post this new plugin when/if I get it working. Either way I strongly
recommend Feed2JS, it is a great project developed in the true spirit of
the Open Source movement.&lt;/p&gt;
</summary><category term="syndication" /></entry><entry><title>New hitcounter, favorites Blosxom Plugin Features</title><link href="http://tech.agilitynerd.com/new-hitcounter-favorites-blosxom-plugin-featu-1.html" rel="alternate" /><updated>2008-01-09T13:25:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-01-09:new-hitcounter-favorites-blosxom-plugin-featu-1.html</id><summary type="html">&lt;p&gt;After fixing a bug in these plugins I decided to add some new features:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The links generated by the favorites plugin used to display the path
and filename of each popular article. It now displays the article
title and the filename is displayed when hovering over the link. This
makes for a much nicer looking and more useful display. Thanks to
&lt;a class="reference external" href="http://blosxom.ookee.com/blog/"&gt;Doug&lt;/a&gt; for the &lt;a class="reference external" href="http://blosxom.ookee.com/blog/plugins/hitcounter_and_favorites_plugins.html"&gt;suggestion&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The hitcounter plugin now tests for existence of articles before
adding them to its hash of counts.&lt;/li&gt;
&lt;li&gt;Requesting a page which no longer exists deletes the page and its
counts from the hash of counts. Since previous versions of this
plugin would record non-existent pages they could appear in the
favorites listing. So now you can delete them by clicking on the link
in the favorites page.&lt;/li&gt;
&lt;li&gt;As pages previously stored in the hitcounter hash of counts are
visited their titles will be added so they will appear in the
favorites' generated links.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both plugins must be upgraded together and will upgrade the existing
hitcounter database as pages are accessed. The new versions can be
downloaded here:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://data.agilitynerd.com/downloads/favorites"&gt;favorites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://data.agilitynerd.com/downloads/hitcounter"&gt;hitcounter&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Notes on Configuring Postfix on Ubuntu Gutsy to Send Email via Google Apps</title><link href="http://tech.agilitynerd.com/notes-on-configuring-postfix-on-ubuntu-gutsy-1.html" rel="alternate" /><updated>2008-01-05T23:53:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-01-05:notes-on-configuring-postfix-on-ubuntu-gutsy-1.html</id><summary type="html">&lt;p&gt;Here are some notes I took on configuring my &lt;a class="reference external" href="http://www.slicehost.com/"&gt;Slicehost&lt;/a&gt; Ubuntu Gutsy
installation to use Postfix to send emails via Google Apps. I am far
from an expert on postfix configuration but maybe these notes will be
helpful to others needing this configuration.&lt;/p&gt;
&lt;p&gt;These sites contain the key information:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://behindmyscreen.newsvine.com/_news/2006/12/31/501615-configuringubuntu-postfix-and-gmail-in-101-easy-steps"&gt;Behind My Screen - Configuring[Ubuntu] Postfix and Gmail in 10+1
Easy Steps&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://prantran.blogspot.com/2007/01/getting-postfix-to-work-on-ubuntu-with.html"&gt;The Prancing Tarantula - Getting Postfix to work on Ubuntu with
Gmail&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://souptonuts.sourceforge.net/postfix_tutorial.html"&gt;Mike Chirico - Gmail on Home Linux Box using Postfix and Fetchmail&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I basically followed the Behind My Screen tutorial (read the comments
too) with the updates from The Prancing Tarantula and the following
changes.&lt;/p&gt;
&lt;p&gt;My Ubuntu server install didn't have the Thawte certificates installed
by default so I installed them:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo aptitude install ca-certificates
&lt;/pre&gt;
&lt;p&gt;Then you can append that file to your /etc/postfix/cacert.pem If you
&amp;quot;sudo su&amp;quot; before doing the append to the file you won't get messed up by
the shell:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo su
# cat /etc/ssl/certs/Thawte\_Premium\_Server\_CA.pem &amp;gt;&amp;gt;
/etc/postfix/cacert.pem
&lt;/pre&gt;
&lt;p&gt;Since I have Google Apps setup for my domain I don't just want to relay
email as &amp;quot;&lt;a class="reference external" href="mailto:user&amp;#64;gmail.com"&gt;user&amp;#64;gmail.com&lt;/a&gt;&amp;quot;, I want the email to be sent as though it
came from my domain (&amp;quot;&lt;a class="reference external" href="mailto:me&amp;#64;agilitynerd.com"&gt;me&amp;#64;agilitynerd.com&lt;/a&gt;&amp;quot;). This requires some simple
changes to the config files.&lt;/p&gt;
&lt;p&gt;In my transport file I have:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
agilitynerd.com smtp:[smtp.gmail.com]:587
&lt;/pre&gt;
&lt;p&gt;In my generic file I have:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
demo&amp;#64;myservername.agilitynerd.com me&amp;#64;agilitynerd.com
&lt;/pre&gt;
&lt;p&gt;Where &amp;quot;demo&amp;quot; is the login name and &amp;quot;myservername&amp;quot; is my slicename. In my
sasl_passwd file I have:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
[smtp.gmail.com]:587 me&amp;#64;agilitynerd.com:me\_gmail\_account\_password
&lt;/pre&gt;
&lt;p&gt;After restarting postix you can test sending email from your server:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$ sudo aptitude install mailx
$ mailx -s &amp;quot;test email&amp;quot; someotheraccount&amp;#64;gmail.com &amp;lt;
~/sometestfile\_to\_send
&lt;/pre&gt;
&lt;p&gt;Check your logfiles for errors/warnings:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
sudo tail /var/log/mail.\*
&lt;/pre&gt;
&lt;p&gt;I hope these notes might help folks &amp;quot;get over the hump&amp;quot; if they are
setting up the same configuration.&lt;/p&gt;
</summary><category term="postfix" /><category term="slicehost" /><category term="ubuntu" /></entry><entry><title>hitcounter, favorites, lastcommented and lastread Blosxom Plugin Updates</title><link href="http://tech.agilitynerd.com/hitcounter-favorites-lastcommented-and-lastre-1.html" rel="alternate" /><updated>2008-01-01T18:44:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2008-01-01:hitcounter-favorites-lastcommented-and-lastre-1.html</id><summary type="html">&lt;p&gt;I found a bug in my hitcounter, favorites, lastread and lastcommented
plugins due to my not using the correct Perl Storable functions. For
some reason I didn't use the locking versions of retrieve() and
nstore(). So when my old webhost had some problem causing long page load
times (and many simultaneous requests) I ended up having my favorites
data file written as zero sized.&lt;/p&gt;
&lt;p&gt;So I changed to Storable::lock_retrieve() and Storable::lock_nstore()
for all four plugins. The new versions are here:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference external" href="http://data.agilitynerd.com/downloads/favorites"&gt;favorites&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://data.agilitynerd.com/downloads/hitcounter"&gt;hitcounter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://data.agilitynerd.com/downloads/lastcommented"&gt;lastcommented&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference external" href="http://data.agilitynerd.com/downloads/lastread"&gt;lastread&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I never heard any bug reports about these plugins, but I'm sorry if
anyone ran into any difficulties due to these bugs.&lt;/p&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Taking Better Photos - Building a Light Box</title><link href="http://tech.agilitynerd.com/taking-better-photos-building-a-light-box-1.html" rel="alternate" /><updated>2007-12-31T19:15:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-31:taking-better-photos-building-a-light-box-1.html</id><summary type="html">&lt;p&gt;I have often been disappointed with the quality of photos of objects
I've posted on my site. My biggest problems have been providing good
lighting and a nice background (&lt;a class="reference external" href="http://agilitynerd.com/blog/agility/equipment/PVCCutter.html"&gt;see this for example&lt;/a&gt;). So I was
Googling to find a photographer's light box to build when I found &lt;a class="reference external" href="http://www.pbase.com/wlhuber/light_box_light_tent"&gt;Bill
Huber's great directions for building your own Light Box&lt;/a&gt;. So I built
my own light box based on Bill's design.&lt;/p&gt;
&lt;p&gt;I got two 10 foot lengths of 1/2 PVC and the fittings Bill outlines.
Nancy picked up a couple yards of white cloth from the fabric store. I
went to a craft store and bought 10 different colored sheets of poster
board about 20&amp;quot; by 26&amp;quot;. I already had some goose neck desk type lamps
into which I put some compact fluorescent bulbs.&lt;/p&gt;
&lt;p&gt;I figured I'd size the box so that I could use the poster board oriented
in either direction. That made my pipes for the width and depth 28&amp;quot; by
22&amp;quot; (to leave room for the pipe going into the fittings). I made two
sets of legs at 12&amp;quot; and 16&amp;quot; long. I got to use my &lt;a class="reference external" href="http://agilitynerd.com/blog/agility/equipment/PVCCutter.html"&gt;PVC Cutter&lt;/a&gt; to
quickly cut the pipe. I didn't bother gluing any of the pipe together
since the friction fit is plenty tight enough. I also wanted to be able
to easily make the box smaller if I over estimated the size I would
need. It is a pretty sizeable box.&lt;/p&gt;
&lt;div class="section" id="light-box-with-backgrounds-and-cover-sheet-pulled-back"&gt;
&lt;h2&gt;Light Box with Backgrounds and Cover Sheet Pulled Back&lt;/h2&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="image0" src="http://data.agilitynerd.com/images/LightBox.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;So if you are looking to take photos for your blog or for selling items
on eBay this is a simple and inexpensive project. We'll see if it helps
my photos look nicer.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="project" /></entry><entry><title>Blog Spammers Using URL Encoding</title><link href="http://tech.agilitynerd.com/blog-spammers-using-url-encoding-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:blog-spammers-using-url-encoding-1.html</id><summary type="html">&lt;p&gt;I was getting hit by comment spammers in the last week who were using
&lt;a class="reference external" href="http://www.w3schools.com/tags/ref_urlencode.asp"&gt;URL-encoding&lt;/a&gt; of their addresses to get around the comment
blacklisting filter I use. By replacing regular characters with the
multi-character encoded representation of those characters within the
URL the spammers were able to post comment spam with links to casino and
porn websites to my blog.&lt;/p&gt;
&lt;p&gt;This type of spam is a reversal of the same method for hiding your own
email address in a web page so it won't be harvested by email spammers
(see for example &lt;a class="reference external" href="http://www.unicom.com/chrome/a/000388.html"&gt;Chip Rosenthal's Blog entry&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;I just added a regular expressions in my blacklist file to block the use
of URL encoded characters in all links. If your blog comment software
supports this approach you might want to do the same thing.&lt;/p&gt;
</summary><category term="spam" /></entry><entry><title>Blosxom - Default Flavour Plugin - Fixes Unknown Flavour Error</title><link href="http://tech.agilitynerd.com/blosxom-default-flavour-plugin-fixes-unknown-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:blosxom-default-flavour-plugin-fixes-unknown-1.html</id><summary type="html">&lt;p&gt;I previously &lt;a class="reference external" href="/blosxom-removing-the-unknown-flavour-error-1.html"&gt;blogged a solution&lt;/a&gt; for the Unknown Flavour error that
involved modifying the blosxom.cgi script itself. I recently found my
modification caused requests for the RSS 0.91 feed and atom feeds to
return the default flavour instead of the desired feed. I suspect any
plugin using a flavour that inserted itself into the Blosxom template
hash table without corresponding flavour file(s) probably didn't work
correctly after my modification.&lt;/p&gt;
&lt;p&gt;While investigating a fix for that bug I noticed that a better solution
would be to override the &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; template subroutine. So I created
the defaultflavour plugin. This plugin fixes the bug by first applying
any registered flavour templates if the requested template files are not
found. If that registered flavour templates don't exist the plugin
finally applies the default flavour template files.&lt;/p&gt;
&lt;p&gt;Download version 0.1 of the defaultflavour plugin &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/defaultflavour_0.1.tar"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;No configuration of the plugin is required. Just copy it into the plugin
directory.&lt;/p&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Blosxom - Hit Counter and Favorites Plugins</title><link href="http://tech.agilitynerd.com/blosxom-hit-counter-and-favorites-plugins-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:blosxom-hit-counter-and-favorites-plugins-1.html</id><summary type="html">&lt;p&gt;I added a &lt;a class="reference external" href="http://agilitynerd.com/static/Favorites.html"&gt;Favorites page&lt;/a&gt; to the side menu of my AgilityNerd site.
Since then I've been updating it manually about once a month based on
the AWStats reports from my web hosting provider (HostMagix). This task
is unnecessarily complicated because AWStats keeps page counts
independently for each URL, which includes the &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; flavour
(filename extension). For my purposes I don't want to distinguish
between say HitCounterFavorites.html and HitCounterFavorites.htm&lt;/p&gt;
&lt;p&gt;So I decided to write a plugin to track the hits per page and &amp;quot;lump&amp;quot;
together counts independently from the file extension. This plugin
allows me to display the hit count of visitors for each page on each
page. I wrote a second plugin to use the hit count data to automatically
generate my Favorites page.&lt;/p&gt;
&lt;div class="section" id="hitcounter-plugin"&gt;
&lt;h2&gt;Hitcounter Plugin&lt;/h2&gt;
&lt;p&gt;I based the hitcounter plugin on the categories plugin written by &lt;a class="reference external" href="http://molelog.molehill.org/"&gt;Todd
Larason&lt;/a&gt;. The plugin has a couple features of interest:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$reset\_count&lt;/tt&gt; flag within the plugin lets you provide a starting
count value for any page. For example to set a page's count to 10
append &amp;quot;?count=10&amp;quot; to the page's URL. Disable this flag once you've
set your counts to avoid mischevious count setting by outsiders.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$retrieve\_only\_flavour flag&lt;/tt&gt; within the plugin can be set to a
flavour you want to use for retrieving counts without incrementing
the count. Use this flavour to view the counts for URLs of interest.&lt;/li&gt;
&lt;li&gt;You can add filters to the &lt;cite&gt;start()&lt;/cite&gt; subroutine to exclude certain
requests from updating your counters. I exclude RSS and Atom feed
requests from my counts.&lt;/li&gt;
&lt;li&gt;As of version 0.5 you can filter out loading and incrementing page
counts for specific user agents via the &lt;tt class="docutils literal"&gt;ignore\_agents array&lt;/tt&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The hitcounter plugin stores the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$blosxom::path&lt;/span&gt;&lt;/tt&gt; as the key in a hash
whose value is the count of hits. The hash is stored in a file in the
data directory. The same hash is used for the entire site. As each page
is &amp;quot;hit&amp;quot; the value is incremented and stored in the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$hitcounter::count&lt;/span&gt;&lt;/tt&gt;
variable. I put this variable in the footer of my pages.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="favorites-plugin"&gt;
&lt;h2&gt;Favorites Plugin&lt;/h2&gt;
&lt;p&gt;My favorites plugin uses the data file containing the hash of paths and
counts as its input. This plugin generates an HTML unordered list of the
most visited URLs with the following configuration options:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$num\_entries&lt;/tt&gt; variable controls how many URLs are listed.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$include\_counts&lt;/tt&gt; variable controls if directories will be
included in the list of entries.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$include\_counts&lt;/tt&gt; variable controls whether or not the number of
counts is appended to each list entry.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$anchor\_format variable&lt;/tt&gt; controls if the full path or only the
filename is displayed in the list entry.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$anchor\_link&lt;/tt&gt; variable controls whether or not the entry is
wrapped in an anchor &amp;lt;a&amp;gt;.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$excludes&lt;/tt&gt; variable controls which pages are excluded from
consideration. I exclude the main URL for my site from showing up in
the list.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$groups&lt;/tt&gt; array holds regexps that will generate separate unordered
lists grouping together pages matching each regexp of &lt;tt class="docutils literal"&gt;$num\_entries&lt;/tt&gt;
list elements. See the configuration section for an example. I use
this feature for my Favorites page to group pages for agility and
tech categories into separate lists.&lt;/li&gt;
&lt;li&gt;The &lt;tt class="docutils literal"&gt;$url\_flavour&lt;/tt&gt; variable lets you specify the flavour to use for
anchors to non-category URLs. It defaults to to
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$$blosxom::default\_flavour&lt;/span&gt;&lt;/tt&gt;.&lt;/li&gt;
&lt;li&gt;You can add filters to the &lt;tt class="docutils literal"&gt;start()&lt;/tt&gt; subroutine to exclude certain
requests from doing the work to generate the HTML. For example, I
only run this plugin for my Favorites page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When this plugin runs the &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$favorites::count&lt;/span&gt;&lt;/tt&gt; variable is populated with
the HTML. I then put this variable in my Favorites.txt page.&lt;/p&gt;
&lt;p&gt;Download version 0.5 of the hitcounter plugin &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/hitcounter"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Download version 0.1 of the favorites plugin
&lt;a class="reference external" href="http://data.agilitynerd.com/downloads/favorites"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Comments/bug reports are welcome.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Blosxom Plugins: lastcommented and lastread</title><link href="http://tech.agilitynerd.com/blosxom-plugins-lastcommented-and-lastread-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:blosxom-plugins-lastcommented-and-lastread-1.html</id><summary type="html">&lt;p&gt;In preparation for an overhaul of my website I wrote two new &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt;
plugins. The lastread plugin allows me to create a list of links to the
last &lt;em&gt;n&lt;/em&gt; articles that were viewed by readers. The lastcommented plugin
allows me to create a list of links to the last &lt;em&gt;n&lt;/em&gt; articles that had
comments added by readers.&lt;/p&gt;
&lt;p&gt;I took pains with these plugins so they only write their state files to
disk when a single article is read (not when the index pages are hit) or
when a comment is actually posted. I've also made it optional to not
rewrite the state file if an already existing entry in the list is
visited/commented again. There are a lot of Blosxom plugins that do a
lot of work on every page access and I didn't want to add to the server
load.&lt;/p&gt;
&lt;p&gt;The configuration of each plugin is pretty straightforward. Both plugins
require the Storable plugin for storing the state files. Only the
lastcommented plugin requires any coding; whatever comment plugin you
use needs to set a variable in order for lastcommented to do any work.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://agilitynerd.com/downloads/lastread"&gt;Download the lastread plugin&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Updated 2007-06-23 to version 0.3 to fix unshift() error and ignore
unknown file requests and filter out user defined HTTP Agents.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;`Download the lastcommented plugin`_
Updated 2007-06-23 to version 0.2 to fix unshift() error.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Hopefully I'll have my new site design using these plugins available in
a few weeks.&lt;/p&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Blosxom - Removing the Unknown Flavour Error</title><link href="http://tech.agilitynerd.com/blosxom-removing-the-unknown-flavour-error-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:blosxom-removing-the-unknown-flavour-error-1.html</id><summary type="html">&lt;p&gt;After my &lt;a class="reference external" href="/blosxom-plugin-to-block-referer-spam-1.html"&gt;recent Referer Spam attacks&lt;/a&gt; I've been checking my logs and
&lt;a class="reference external" href="http://awstats.sourceforge.net"&gt;AWStats&lt;/a&gt; reports daily. I noticed that occasionally I'd get people
requesting pages with incorrect suffixes. i.e. foo.ht or bar.|id|
(some of these are probably spammers too). A &amp;quot;feature&amp;quot; of &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; is
that it will still serve the page with the default layout or flavour.
This feature causes a not too helpful and ugly error message to be
displayed on the top of the page:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
Error: I'm afraid this is the first I've heard of an &amp;quot;ht&amp;quot; flavoured
Blosxom. Try dropping the &amp;quot;/+ht&amp;quot; bit from the end of the URL.
&lt;/pre&gt;
&lt;p&gt;Of course removing the suffix from the URL doesn't usually work; the
client is served with an empty (but flavoured) page. So I though a
better solution would be to have Blosxom serve my default_flavour
whenever the flavour couldn't be identified. A little Googling later
turned up &lt;a class="reference external" href="http://brutalhugs.com/flavours/#mod_default_template"&gt;this Blosxom mod by James Vasille&lt;/a&gt;. James explicitly loads a
flavour file named &amp;quot;default&amp;quot; whenever the requested flavour can't be
found.&lt;/p&gt;
&lt;p&gt;For my site it is more appropriate to serve the $default_flavour when
the requested flavour can't be found. That way I didn't have copy/create
head.default, foot.default, etc files. So my code change is almost the
same as James':&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$template =   sub {
  my ($path, $chunk, $flavour) = &amp;#64;_;
  do {
    return join '', &amp;lt;$fh&amp;gt; if $fh-&amp;gt;open(&amp;quot;&amp;lt; $datadir/$path/$chunk.$flavour&amp;quot;);
  } while ($path =~ s/(/*[^/]*)$// and $1);
  # Begin added code
  do {
    return join '', &amp;lt;$fh&amp;gt; if $fh-&amp;gt;open(&amp;quot;&amp;lt; $datadir/$path/$chunk.$default_flavour&amp;quot;);
  } while ($path =~ s/(/*[^/]*)$// and $1);
  # End added code
  return join '', ($template{$flavour}{$chunk} || $template{error}{$chunk} || '');
};
&lt;/pre&gt;
&lt;p&gt;So now a less ugly page will be provided when an unknown suffix/flavour
is requested.&lt;/p&gt;
&lt;div class="section" id="update-19-apr-2005-this-algorithm-has-a-bug-that-has-been-fixed-by-the-defaultflavour-plugin"&gt;
&lt;h2&gt;Update 19-Apr-2005 - this algorithm has a bug that has been fixed by the &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/defaultflavour_0.1.tar"&gt;defaultflavour plugin&lt;/a&gt;.&lt;/h2&gt;
&lt;/div&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Comment Spam and wbcaptcha Plugin Enhancements</title><link href="http://tech.agilitynerd.com/comment-spam-and-wbcaptcha-plugin-enhancement-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:comment-spam-and-wbcaptcha-plugin-enhancement-1.html</id><summary type="html">&lt;p&gt;I have been fighting automated comment spam robots (see also
&lt;a class="reference external" href="http://agilitynerd.posterous.com/refererblock-version-02-1"&gt;refererblock update&lt;/a&gt;, and &lt;a class="reference external" href="http://agilitynerd.posterous.com/blosxom-hit-counter-and-favorites-plugins-1"&gt;hit counter changes&lt;/a&gt;) with my modified
writeback plugin that uses a blacklist of spam words and URLs. However,
I'm getting tired of updating the blacklist and removing spam comments
that get through the filter. So I decided to install the &lt;a class="reference external" href="http://varg.dyndns.org/psi/pub/code/misc/wbcaptcha.html"&gt;wbcaptcha
plugin&lt;/a&gt; written by &lt;a class="reference external" href="http://varg.dyndns.org/psi/pub/index.html"&gt;Pasi Savolainen&lt;/a&gt;. His plugin uses the &lt;a class="reference external" href="http://www.figlet.org"&gt;FIGlet&lt;/a&gt;
program to generate an ASCII &amp;quot;image&amp;quot; of random letters:&lt;/p&gt;
 &lt;div class="centered"&gt;&lt;pre&gt;
              _
  ___  _ __  | |__    __ _
 / _ \| '_ \ | '_ \  / _` |
|  __/| | | || | | || (_| |
 \___||_| |_||_| |_| \__,_|
 &lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A person leaving a comment then enters the letters (enha for the image
above) as a &amp;quot;key&amp;quot; along with their comments. The plugin then validates
that the key matches the generated image before it allows the comment to
be posted. This is similar to the approach used by other blog software
to distinguish humans from automated spam programs.&lt;/p&gt;
&lt;p&gt;Pasi's plugin worked by executing figlet in a subshell whenever someone
wanted to post a comment. I wanted to change this approach since I don't
have the ability to install figlet on my web host and I didn't want to
pay the performance cost of calling a program through the shell.&lt;/p&gt;
&lt;p&gt;So I found the &lt;a class="reference external" href="http://search.cpan.org/~jpierce/Text-FIGlet-1.06/FIGlet.pm"&gt;Text::FIGlet&lt;/a&gt; Perl package on &lt;a class="reference external" href="http://cpan.org/"&gt;CPAN&lt;/a&gt; and made a few
small edits to Pasi's plugin to allow it to invoke the FIGlet Perl code
without the shell. This simplified the plugin and sped up it's
performance. I've sent my changes to Pasi in case he wants to
incorporate them in a future release of his plugin.&lt;/p&gt;
&lt;p&gt;I've decided to leave my blacklist in place, since I'm sure I'll still
be facing human comment spammers looking to promote their websites. The
last change I can think to make to further discourage spammers will be
to change all &amp;lt;a&amp;gt; elements in comments to include the &lt;a class="reference external" href="http://googleblog.blogspot.com/2005/01/preventing-comment-spam.html"&gt;rel=&amp;quot;nofollow&amp;quot;&lt;/a&gt;
attribute supported by most portal/search websites.&lt;/p&gt;
&lt;p&gt;I also took this opportunity to add some name anchors to my foot.htm and
foot.html files so clicking on the &amp;quot;Comments&amp;quot; link on my index pages now
takes you directly to the comments section of the individual article.
Similarly, selecting &amp;quot;Add Your Comment&amp;quot; and Posting a comment also show
the writeback for and the last added comment respectively. This makes
for less scrolling on long articles and multiple comments.&lt;/p&gt;
&lt;p&gt;Unfortunately, I don't think this will actually reduce the spammer hits
on my site since these are due to automated &amp;quot;bots&amp;quot;. I doubt the spammers
actually check if their comments get posted, they are just hoping for a
comment to &amp;quot;get through&amp;quot; occaisionally so their sites can can get some
&lt;a class="reference external" href="http://search.cpan.org/~jpierce/Text-FIGlet-1.06/FIGlet.pm"&gt;&amp;quot;Google Juice&amp;quot;&lt;/a&gt;. So they'll keep pounding on my site although
hopefully fewer spam comments will get through.&lt;/p&gt;
&lt;p&gt;Please email me at the address shown in the right hand menu bar if you
have any problems posting comments.&lt;/p&gt;
</summary></entry><entry><title>Directing Search Engines to Blosxom Content Pages</title><link href="http://tech.agilitynerd.com/directing-search-engines-to-blosxom-content-p-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:directing-search-engines-to-blosxom-content-p-1.html</id><summary type="html">&lt;p&gt;I've noticed that folks who come to my site by querying through a search
engine often end up on my main page and the article they are searching
for has already rolled off the front page. This is because the search
engine robots tend to choose the index pages as more relevant than the
content pages linked to by the index pages. This is especially
frustrating to me since I want my blog to serve as a reference for other
Agility enthusiasts - so making my articles easily retrievable from
search engines is important to me. It turns out this is a pretty common
problem faced by many blogs.&lt;/p&gt;
&lt;p&gt;I found that &lt;a class="reference external" href="http://jclark.org"&gt;Jason Clark&lt;/a&gt; had run into this problem with his
&lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; blog and resolved it by using the following meta element on
his index pages: &amp;lt;meta name=&amp;quot;robots&amp;quot; content=&amp;quot;noindex,follow&amp;quot; &amp;gt;. &lt;a class="reference external" href="http://jclark.org/weblog/WebDev/Blosxom/robottweak2.html"&gt;His
post describes&lt;/a&gt; how he conditionally includes these robot meta tags
through his head flavour files (using the interpolate_fancy plugin and
his storystate plugin) only on his index pages and not on other pages.&lt;/p&gt;
&lt;p&gt;For my site it is even easier. I use separate head.index pages to style
my index pages differently from my htm/html pages. So I just directly
inserted the &amp;lt;meta name=&amp;quot;robots&amp;quot; content=&amp;quot;noindex,follow&amp;quot; &amp;gt; into my
head.index pages.&lt;/p&gt;
&lt;p&gt;There are a number of other robots meta tags that I might consider
adding in the future. &lt;a class="reference external" href="http://www.bauser.com/websnob/"&gt;Websnob&lt;/a&gt; has a &lt;a class="reference external" href="http://www.bauser.com/websnob/meta/robots.html"&gt;thorough explanation&lt;/a&gt; of the
tags and the robots that use them.&lt;/p&gt;
&lt;p&gt;Hopefully over the next few months these changes will cause search
engines to give their users links directly to the articles in my site.&lt;/p&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Image Theft Ranting And Blocking</title><link href="http://tech.agilitynerd.com/image-theft-ranting-and-blocking-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:image-theft-ranting-and-blocking-1.html</id><summary type="html">&lt;p&gt;I was really disappointed yesterday when I checked my blog's statistics
and found that someone from a Hungarian Agility discussion board had
directly linked course images from my website. Direct linking of an
image is placing the URL of the image directly in a webpage hosted on
another server. Each time a browser loads that page the direct link
causes the other server to send the image to the client's browser. So
each time the forum is viewed my server has to send the images to the
forum's viewer.&lt;/p&gt;
&lt;p&gt;I purposely try to make my image sizes small to make it possible for
dial up visitors to view my site without too much delay. So direct
linking doesn't impact my bandwidth costs too much; so far it is about
1Mb a day. The additional burden it puts on the webserver should be
small too, but since I don't own the shared server I don't really know.&lt;/p&gt;
&lt;div class="section" id="ranting"&gt;
&lt;h2&gt;Ranting&lt;/h2&gt;
&lt;p&gt;The aspect of this that most irritates me is that someone would copy my
work without attribution. That is the only thing I ask of visitors to my
site who wish to reuse my content. The &lt;a class="reference external" href="http://creativecommons.org/licenses/by-nc-sa/2.0/"&gt;license link&lt;/a&gt; at the bottom of
each page should make this clear. I really don't think this is it too
much to ask.&lt;/p&gt;
&lt;p&gt;I take copyright infringement very seriously. To me it isn't just the
legal requirements of using other's material in accordance with their
wishes that is important, taking credit, explicitly or implicitly, for
another's work is just wrong. This is one of those &lt;a class="reference external" href="http://www.robertfulghum.com/books.php#book1"&gt;All I Really Need To
Know I Learned In Kindergarten&lt;/a&gt; concepts: &lt;a class="reference external" href="http://www.peace.ca/kindergarten.htm"&gt;Don't take things that
aren't yours&lt;/a&gt;. As a friend of Nancy's says &amp;quot;Some people don't have good
home training&amp;quot;.&lt;/p&gt;
&lt;p&gt;Lastly, the Agility community is still a small community and the online
Agility community even more so. I guess I am naive, but I hoped that the
members of our community wouldn't do things like this.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="blocking"&gt;
&lt;h2&gt;Blocking&lt;/h2&gt;
&lt;p&gt;My first step was emailing the webmaster of the site. The site was
entirely in Hungarian so it is possible the webmaster may not have
understood my English request. In any event, after 24 hours they hadn't
removed the links.&lt;/p&gt;
&lt;p&gt;So I went to look for a technical solution. There are a few well known
technical solutions for this problem. A search of Google for &lt;a class="reference external" href="http://www.google.com/search?q=blocking+direct+linking"&gt;blocking
direct linking&lt;/a&gt; or &lt;a class="reference external" href="http://www.google.com/search?q=blocking+hotlinking"&gt;blocking hotlinking&lt;/a&gt; will turn them all up. The
most useful solutions include:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Rename the direct linked images&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
This mean updating all posts one your site to match the new name.
But if you only have a few images and/or posts to them you can do this on a per direct link basis.&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Randomly generate image file names that change over time&lt;/p&gt;
&lt;p&gt;This is usually used for photo galleries where there is no text referring to each image.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Serve images through a script&lt;/p&gt;
&lt;p&gt;This script would reject requests for images based on information in
the request. This can be a compute intensive approach since it causes
requests even from your own site to go through the script. For sites
where you don't have control over the webserver this may be required.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Use a Rewrite rule to serve a different image to non-local referers&lt;/p&gt;
&lt;p&gt;This is the technique I used.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Based on this &lt;a class="reference external" href="http://altlab.com/htaccess_tutorial.html"&gt;altlab.com article&lt;/a&gt; I originally added these rules to my
.htaccess file on my server:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
RewriteEngine on
RewriteCond %{HTTP\_REFERER} !^$
RewriteCond %{HTTP\_REFERER} !^http://(www.)?agilitynerd.com/.\* [NC]
RewriteRule .\*.(jpg\|jpeg\|gif\|png\|bmp)$ /images/nodirectlink.g [L,NC]
&lt;/pre&gt;
&lt;p&gt;I then created an image called nodirectlink.g shown below.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="redirected-image"&gt;
&lt;h2&gt;Redirected Image&lt;/h2&gt;
&lt;div class="p_embed p_image_embed"&gt;&lt;p&gt;&lt;img alt="image0" src="http://data.agilitynerd.com/images/nodirectlink.g" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;Don't use the same filename suffix as one of the real image filenames
you use or you'll loop the rewrite engine.&lt;/p&gt;
&lt;p&gt;I had forgotten about images direct linked by RSS feed readers that
access the root. Rather than rewrite those requests I moved my Rewrite
rules into the .htaccess file in the images directory. I also decided to
not send the image after all, I'll just fail the request. There is no
sense in even wasting the bandwidth, the clients will now get the broken
image icon from their browser. The official &lt;a class="reference external" href="http://httpd.apache.org/docs/1.3/misc/rewriteguide.html"&gt;Apache URL Rewriting
Guide&lt;/a&gt; describes this in the Blocked Inline-Images section. So here is
my final solution:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
RewriteEngine on
RewriteCond %{HTTP\_REFERER} !^$
RewriteCond %{HTTP\_REFERER} !^http://(www.)?agilitynerd.com/.\*$ [NC]
RewriteRule .\*.(jpg\|jpeg\|gif\|png\|bmp)$ - [F]
&lt;/pre&gt;
&lt;p&gt;I took this opportunity to modify some of my very first articles and
move their images into the /images directory. Those images were being
served by the Blosxom binary plugin back when I thought it was a
good idea. Now I won't pay any extra processing cost for those images.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="conclusion"&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So now I have a technical solution to a human problem. There is a chance
that this change may cause some viewers of this site to not see images
on this site. But hopefully that should be a very, very small number of
people. But if you think you are getting my blocking image incorrectly
Please email me: steve at agilitynerd.com.&lt;/p&gt;
&lt;p&gt;I'm still disappointed I was forced to resort to this change.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="apache" /><category term="sysadmin" /></entry><entry><title>Modifications to Blosxom entriescache PluginHandles Articles With Future meta-creation_date</title><link href="http://tech.agilitynerd.com/modifications-to-blosxom-entriescache-pluginh-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:modifications-to-blosxom-entriescache-pluginh-1.html</id><summary type="html">&lt;p&gt;&lt;a class="reference external" href="http://fletcher.freeshell.org"&gt;Fletcher Penney&lt;/a&gt; created the original &lt;a class="reference external" href="http://fletcher.freeshell.org/computers/web/blosxom/entries_cache/"&gt;entriescache&lt;/a&gt; &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt;
plugin. It is designed to cache all the articles and their creation
dates in a Blosxom blog to avoid the overhead of scanning the file
system for new/modified files. He merged the meta plugin functionality
into the plugin to allow authors to date their entries without relying
on the file creation date (this makes it easy to move from one web
hosting provider to another without having your article dates get
reset).&lt;/p&gt;
&lt;p&gt;Unfortunately, articles posted to a Blosxom blog with
meta-creation_date's in the future would be displayed immediately. I
had wanted to modify Blosxom to allow me post articles with future
creation dates and have them hidden until that date. After a &lt;a class="reference external" href="http://groups.yahoo.com/group/blosxom/message/10664"&gt;request
for this functionality on the Blosxom email list&lt;/a&gt;, I looked into the
required modifications and found it wasn't too difficult to make them.&lt;/p&gt;
&lt;p&gt;You can download the modified plugin &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/entriescache"&gt;here&lt;/a&gt;. Let me know if you have
any questions or comments.&lt;/p&gt;
</summary><category term="blosxom" /><category term="plugin" /></entry><entry><title>Refererblock Version 0.2</title><link href="http://tech.agilitynerd.com/refererblock-version-02-1.html" rel="alternate" /><updated>2007-12-29T23:16:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2007-12-29:refererblock-version-02-1.html</id><summary type="html">&lt;p&gt;I came up with two improvements to the first release of my &lt;a class="reference external" href="http://agilitynerd.posterous.com/blosxom-plugin-to-block-referer-spam-1"&gt;refererblock
plugin&lt;/a&gt;:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;If the referer string matches the site's URL it passed immediately
and isn't checked against the blacklist.&lt;/li&gt;
&lt;li&gt;The blacklist.txt file was being read even if the referer string was
empty. Now it is only read if the referer string is not empty and it
isn't for the site's URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These optimizations do improve the performance of the plugin. My testing
on a PIII 800MHz running Fedora Core 3 Linux with Apache 2.0 showed the
following average latencies:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;1.5 ms - Empty referer string or current domain.&lt;/li&gt;
&lt;li&gt;2.0 ms - Referer string matching the first regex of the example
blacklist file.&lt;/li&gt;
&lt;li&gt;3.0 ms - Referer string matching the final regex of the example
blacklist file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I was kind of surprised at how little additional time was required to
load the blacklist file and process the regular expressions. This is
probably due to the file remaining in the disk cache for subsequent
requests. Of course your mileage may vary.&lt;/p&gt;
&lt;p&gt;Download version 0.2 of the plugin &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/refererblock_0.2.tar"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;See my &lt;a class="reference external" href="/blosxom-plugin-to-block-referer-spam-1"&gt;original plugin description&lt;/a&gt; for installation, configuration,
and testing information. Please let me know if you use this plugin or if
you have comments or suggestions for improving it.&lt;/p&gt;
</summary><category term="blosxom" /><category term="plugin" /><category term="referrer" /><category term="spam" /></entry><entry><title>Those Darn Spammers Blosxom - Hit Counter and Writeback Changes</title><link href="http://tech.agilitynerd.com/those-darn-spammersblosxom-hit-counter-and-wr-1.html" rel="alternate" /><updated>2006-08-11T19:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2006-08-11:those-darn-spammersblosxom-hit-counter-and-wr-1.html</id><summary type="html">&lt;p&gt;Over the past year I'd noticed that comment and trackback spammers had
been hitting the same dozen or so pages of my blog multiple times daily.
(It is probably the same person/group who took a snapshot of the
articles on my front page at that time and just reuses those URLs for
all of their different domains). Last I calculated, about 50% of my
overall website traffic is due to spammers. This constant barrage of
hits skews my &lt;a class="reference external" href="http://awstats.sourceforge.net"&gt;AWStats&lt;/a&gt; statistics and, more importantly, skews the
results on my &lt;a class="reference external" href="http://agilitynerd.com/blog/static/Favorites.html"&gt;Favorites page&lt;/a&gt;. So I took a little time to work on this
problem.&lt;/p&gt;
&lt;p&gt;FWIW I'm also starting to see a lot of spam coming from &amp;quot;blogs&amp;quot; being
setup on &lt;a class="reference external" href="http://blogspot.com"&gt;BlogSpot&lt;/a&gt; that are a single page all of whose links point to
the real site. Some of these bogus blogs use the BlogSpot temmplate
which contains a flag used to alert BlogSpot admins to content in
violation of their Terms of Service. This allows anyone to report the
bogus blog by just clicking on the flag. Other &amp;quot;blogs&amp;quot; just use their
own HTML and BlogSpot support would have to be sent an email with the
offending site's URL.&lt;/p&gt;
&lt;p&gt;So anyway, I've taken the following steps:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Disabled trackback comments entirely using the configuration variable
in the writeback plugin. I've never received a legitimate trackback
ping.&lt;/li&gt;
&lt;li&gt;Modified my modified version of the writeback plugin to set a
variable $rejected with a 1 if the comment was rejected or if
trackback was attempted.&lt;/li&gt;
&lt;li&gt;Modified my HitCounter plugin to read the $writeback::rejected
variable and then not increase the counter for the spammed page.&lt;/li&gt;
&lt;li&gt;I had to change the ordering of the hitcounter plugin to run after
writeback so the variable would be set correctly when hitcounter ran.&lt;/li&gt;
&lt;li&gt;Set the $hitcounter::reset_count variable and reset the counts of
the spammed pages back to &amp;quot;reasonable&amp;quot; counts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Another couple hours wasted messing around against spammers.&lt;/p&gt;
&lt;p&gt;I've &lt;a class="reference external" href="/blosxom-plugin-to-block-referer-spam-1.html"&gt;previously written&lt;/a&gt; how I've been using comment content
blacklisting to reject comment, trackback, and referer (sic) spam. My
current blacklist file has over 40 regular expressions containing over
250 words and patterns. I update it whenever a spam comment slips
through. So that is an ongoing almost daily effort. I might just have to
go to the trouble of getting a &lt;a class="reference external" href="http://varg.dyndns.org/psi/pub/code/misc/wbcaptcha.html"&gt;CAPTCHA&lt;/a&gt; plugin to work.&lt;/p&gt;
</summary><category term="blosxom" /><category term="plugin" /><category term="spam" /></entry><entry><title>How Safe is Your Personal Information in the Hands of Website Developers?</title><link href="http://tech.agilitynerd.com/how-safe-is-your-personal-information-in-the-1.html" rel="alternate" /><updated>2005-06-23T19:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2005-06-23:how-safe-is-your-personal-information-in-the-1.html</id><summary type="html">&lt;p&gt;I was going through the webserver statistics for this site to see if any
new sites had linked to any of my articles (it is always nice to see
that what I have to say is useful to someone). Anyway, I ran across
someone who had come to my site through a Google query (I won't mention
what the query was for reasons you'll soon see). I ran the same query on
Google to see what else came up since it was a rather unique query.
Another Google link was for a site that looked like it had raw data -
not your usual HTML pages.&lt;/p&gt;
&lt;p&gt;When I went to the site I found what looked like a website developer's
development directory wide open to the internet. There were at least
three company's websites sitting in subdirectories. The file referred to
in the Google result page was a backup of an SQL database dump file. Not
just any database file - a backup of all the customer information for
running one site's shopping cart database. It included names, addresses,
email addresses, and phone numbers! (I didn't poke around to see if it
had any more sensitive data).&lt;/p&gt;
&lt;p&gt;I was able to figure out the original data owner's domain name from some
info in the header of the file. So I just sent them an email letting
them know that their customer information is posted for all to see on
someone else's website. It will be interesting to see if they respond. I
hope it is just their website developer who has a test server running
and accidentally left this SQL dump in a publicly accessible area of
their webserver. I'd hate to think this data was stolen from the real
website and being used for spamming purposes.&lt;/p&gt;
&lt;p&gt;As a software developer I've read numerous cautionary tales of
accidental (and malicious) data theft occurring when real customer data
is used in test systems. I just never imagined I'd stumble across such
an egregious privacy violation. So this experience makes me wonder about
all the online systems into which we type our personal information. All
it takes is one careless developer (not even a malicious one) to expose
our private information to a much wider audience...&lt;/p&gt;
</summary><category term="development" /><category term="privacy" /></entry><entry><title>Blosxom Plugin to Block Referer Spam</title><link href="http://tech.agilitynerd.com/blosxom-plugin-to-block-referer-spam-1.html" rel="alternate" /><updated>2005-02-12T20:00:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2005-02-12:blosxom-plugin-to-block-referer-spam-1.html</id><summary type="html">&lt;p&gt;Like so many other bloggers who allow comments on their websites and
blog articles, I was facing increasing &lt;a class="reference external" href="http://en.wikipedia.org/wiki/Blog_spam"&gt;comment spam&lt;/a&gt; as my blog got
noticed by more spammers. The size of this problem is illustrated by
&lt;a class="reference external" href="http://www.google.com/search?q=%22comment+spam%22"&gt;this Google query for &amp;quot;comment spam&amp;quot;&lt;/a&gt; that returned 1.5 million hits.
For the uninitiated comment spam is like email spam for blogs; the
spammer inserts fake comments in a blog where either the comment text
contains from one to dozens of links to the spammer's websites. When web
search sites &amp;quot;spider&amp;quot; the blog the links to the spammer's site are
treated as &amp;quot;endorsements&amp;quot; of the spammer's sites and the spammer's sites
are raised to the top of the search site's result lists.&lt;/p&gt;
&lt;p&gt;There is another growing type of blog spam called &lt;a class="reference external" href="http://www.spywareinfo.com/articles/referer_spam/"&gt;referer spam&lt;/a&gt; (yes
it is &lt;a class="reference external" href="http://dictionary.reference.com/search?q=referer"&gt;officially misspelled&lt;/a&gt;). When a web surfer clicks on a link in a
web page that sends them to another web page most web browsers fill in
the URL of the referring page into the request called the HTTP_REFERER.
Some websites and blogs capture that page link information when they are
on the receiving end of a web request. These sites might have a section
on each page indicating the sites that link to that page. These links
are referer links.&lt;/p&gt;
&lt;p&gt;Referer spam uses the same mechanism as comment spam to raise the search
sites ranking of the spammer's websites. But referer spammer's don't
post comments; they post fake referrals to a website. The are hoping
that the website or blog displays links of the sites that refer to them.
So when the website is spidered the search ranking is raised.&lt;/p&gt;
&lt;div class="section" id="blosxom-plugins-addressing-spam"&gt;
&lt;h2&gt;Blosxom Plugins Addressing Spam&lt;/h2&gt;
&lt;p&gt;Like so many bloggers once I started getting comment spam I was able to
manually delete them as they occurred. But that got old fast. After some
Googling I discovered &lt;a class="reference external" href="http://www.lathi.net"&gt;Doug Alcorn's&lt;/a&gt; &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; &lt;a class="reference external" href="http://www.lathi.net/twiki-bin/view/Main/BlogSpam"&gt;writeback blacklist
plugin&lt;/a&gt;. I had been using the original &lt;a class="reference external" href="http://www.blosxom.com/plugins/input/writeback.htm"&gt;writeback&lt;/a&gt; plugin. Doug's
improvements provided enough protection (so far) with less than a dozen
regular expressions removing all my comment spam.&lt;/p&gt;
&lt;p&gt;Referer spam started hitting me three weeks ago. What was most
infuriating was that I don't display any links of referring sites on my
site at all. So all these spammers were succeeding in doing was skewing
my site statistics and using my bandwidth with their fake referer
attacks several times a day.&lt;/p&gt;
&lt;p&gt;Of course all the referer spam site's addresses contained one or more of
the same dozen blacklisted words I had already configured for comment
spam. About the same time I saw &lt;a class="reference external" href="http://jclark.org"&gt;Jason Clark&lt;/a&gt; post his &lt;a class="reference external" href="http://jclark.org/weblog/WebDev/Blosxom/plugins/deferer/deferer-0-1i.html"&gt;deferer
plugin&lt;/a&gt; to return a 301 permanent redirect for the IP address of one
particular referer spammer who was attacking his site. I thought that by
combining Doug's blacklisting plugin with Jason's immediate redirect
plugin I could reduce the referer spam from my log files.&lt;/p&gt;
&lt;p&gt;This plugin hasn't removed the entries entirely from my logs, since the
initial request is still logged with a 301 status. But it has stopped
the subsequent downloading of images for the pages whose content is now
not served. Now that the log contains 301 status messages for these
requests they are ignored by my host's statistics program (&lt;a class="reference external" href="http://awstats.sourceforge.net"&gt;Advanced Web
Statistics - AWStats&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;On a re-reading of Jason's blog entry for deferer he also mentions the
idea of white and black lists for referer filtering. So I might have
subliminally remembered his idea and implemented my refererblock plugin
based on his idea. In any event, I fully credit Jason and Doug for
giving me the ideas and code with which to put together my plugin.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="referer-block-plugin"&gt;
&lt;h2&gt;Referer Block Plugin&lt;/h2&gt;
&lt;p&gt;The refererblock plugin's tar file can be downloaded &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/refererblock_0.1.tar"&gt;here&lt;/a&gt;. It
contains the refererblock plugin already named 000refererblock so that
it runs before all other plugins (you want to discard the blacklisted
requests before all legitimate requests). A sample blacklist.txt file is
provided and contains some example regexs. It uses the same blacklist
file format and file name as Doug's writeback modification (I took the
code from his plugin with only cosmetic changes). See Doug's website for
links to the Movable Type and other blacklists.&lt;/p&gt;
&lt;p&gt;The only configuration variable you can set is $log_blacklisted. If set
to a full path file name the script logs the UTC date/time, referer
string, and the page to which they were referring. You could use the
frequency of words in the rejected referer strings to fine tune the
content and ordering of your blacklist.txt file to match the spammers
hitting your site. Be aware that this file isn't trimmed so you might
want to keep an eye on its size.&lt;/p&gt;
&lt;p&gt;Lastly, the zip file contains a simple Perl script you can use to test
the plugin. Execute it as:&lt;/p&gt;
&lt;pre class="code literal-block"&gt;
referer_test.pl http://example.com http://referer-spam.com
where the first URL is your website and the second URL is the referer
&lt;/pre&gt;
&lt;p&gt;to be sent in the request. This script uses &lt;a class="reference external" href="HTTP::Request"&gt;HTTP::Request&lt;/a&gt; to send the
request. The script returns the status &amp;quot;200 OK&amp;quot; for the requested page
if it isn't blacklisted and &amp;quot;301 Moved Permanently&amp;quot; if the referer is
blacklisted.&lt;/p&gt;
&lt;p&gt;Please let me know if you use this plugin or if you have comments or
suggestions for improving it.&lt;/p&gt;
&lt;/div&gt;
</summary><category term="blosxom" /><category term="plugin" /><category term="spam" /></entry><entry><title>Online Button Creation Tool</title><link href="http://tech.agilitynerd.com/online-button-creation-tool-1.html" rel="alternate" /><updated>2004-12-08T20:00:00-06:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2004-12-08:online-button-creation-tool-1.html</id><summary type="html">&lt;p&gt;I was reading the &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; email list tonight and came across an
interesting &lt;a class="reference external" href="http://rbpark.ath.cx"&gt;photoblog by Rob Park&lt;/a&gt;. After enjoying some of his photos,
I noticed his &lt;a class="reference external" href="http://rpbark.ath.cx/news/buttons.html"&gt;post&lt;/a&gt; about an &lt;a class="reference external" href="http://kalsey.com/tools/buttonmaker"&gt;online form for creating the XML, RSS,
etc. buttons&lt;/a&gt; popular on so many websites. &lt;a class="reference external" href="http://kalsey.com"&gt;Adam Kalsey&lt;/a&gt; has created a
simple (dare I say elegant?) form that gives you just enough inputs to
completely control the look of the buttons. The image is generated in
PNG format.&lt;/p&gt;
&lt;p&gt;Had I known of this site before it would have saved me a couple hours
messing around with Fireworks creating the buttons on my AgilityNerd
&lt;a class="reference external" href="http://agilitynerd.com/blog/static/subscribe.html"&gt;Feeds&lt;/a&gt; page...&lt;/p&gt;
</summary><category term="sitedesign" /><category term="tools" /></entry><entry><title>AgilityNerd Site Enhancements</title><link href="http://tech.agilitynerd.com/agilitynerd-site-enhancements-1.html" rel="alternate" /><updated>2004-10-29T23:16:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2004-10-29:agilitynerd-site-enhancements-1.html</id><summary type="html">&lt;p&gt;I've spent some time over the past couple nights trying to improve
the access to information on my site. As a software developer it is
often more fun to play around with the technology than it is to do
&amp;quot;real&amp;quot; work.&lt;/p&gt;
&lt;p&gt;I've made the following enhancements:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Added Quick Links to the right hand navigation to allow folks to get
to the three main Agility sections of the website without going to the
&lt;a class="reference external" href="http://agilitynerd.com/blog/navigate"&gt;Navigate&lt;/a&gt; page.
Used the [[Blosxom]] &lt;a class="reference external" href="http://www.blosxom.com/plugins/include/file.htm"&gt;file plugin&lt;/a&gt;to include the new HTML in all
pages.&lt;/li&gt;
&lt;li&gt;Changed my &lt;a class="reference external" href="http://agilitynerd.com/blog/agility/handling/glossary"&gt;AgilityGlossary&lt;/a&gt; page to have a preface with an
alphabetical listing ofdefinitions. I ended up using three new
Blosxom plugins to make thiswork: &lt;a class="reference external" href="http://www.blosxom.com/plugins/general/config.htm"&gt;config&lt;/a&gt;,&lt;a class="reference external" href="http://www.blosxom.com/plugins/display/postheadprefoot.htm"&gt;postheadprefoot&lt;/a&gt;and
&lt;a class="reference external" href="http://blosxom.ookee.com/blosxom/plugins/v2/sort_order-v0i85"&gt;sort_order&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For other Blosxom users I've updated my &lt;a class="reference external" href="http://tech.agilitynerd.com/site_plugins.html"&gt;site pluginspage&lt;/a&gt; with the
details.&lt;/p&gt;
&lt;p&gt;Hopefully these changes along with the page links and previous/nextpage
arrows at the bottom of the page will give everyone enough ways to move
easily around the sight.&lt;/p&gt;
</summary></entry><entry><title>Modifications to Blosxom moreenties PluginLinks to a Configurable Number of Index Pages</title><link href="http://tech.agilitynerd.com/modifications-to-blosxom-moreenties-pluginlin-1.html" rel="alternate" /><updated>2004-10-21T19:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2004-10-21:modifications-to-blosxom-moreenties-pluginlin-1.html</id><summary type="html">&lt;p&gt;Jason Clark created the &lt;a class="reference external" href="http://jclark.org/weblog/WebDev/Blosxom/plugins/moreentries"&gt;moreentries&lt;/a&gt; plugin for &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; to allow
adding links to the previous and next group of articles/entries in the
head or foot of a Blosxom weblog. He solved a problem that most weblogs
have; it can be difficult for visitors to browse older articles.
Although he calls the solution a hack, he nicely solved an important
problem.&lt;/p&gt;
&lt;p&gt;I had been using the morentries plugin but found the text links didn't
make it obvious to some visitors that there were more pages of entries.
So I added images to the links to make them stand out. But I decided it
might be more intuitive to use the same model used by search sites;
create a graphical and textual link for each page of articles along with
previous and next links.&lt;/p&gt;
&lt;p&gt;So I decided to modify Jason's plugin to provide this feature. If you
take a look at the bottom of my &lt;a class="reference external" href="http://agilitynerd.com/blog/"&gt;AgilityNerd&lt;/a&gt; home page page with more
than $blosxom::num_entries entries and scroll to the bottom) you'll see
grey dots, arrows, and page numbers each linking to a page of articles.&lt;/p&gt;
&lt;p&gt;You can download the modified plugin &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/moreentries.zip"&gt;here&lt;/a&gt;. The prolog of the file
describes the configuration options. I've included the public domain
icons I use on my site in the zip file for your experimentation. Since
there are a few configuration options I've put some examples below so
you can see how the new parameters control the html stored in
$moreentries::pagelinks.&lt;/p&gt;
&lt;p&gt;With these settings:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$numpagelinks = 10;$textlinks = 1;                # set to 1 to enable text links 0 to disable
$imagelinks = 0;        # set to 1 to enable image links 0 to disable
&lt;/pre&gt;
&lt;p&gt;A screenshot of the HTML generated when the fifth page is selected:&lt;/p&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="image0" src="http://data.agilitynerd.com/images/moreentries_pagelink_1.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;With these settings:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$numpagelinks = 10;
$textlinks = 0;                # set to 1 to enable text links 0 to disable
$imagelinks = 1;        # set to 1 to enable image links 0 to disable
$prevlinkimage = &amp;quot;/images/left.gif&amp;quot;;
$nextlinkimage = &amp;quot;/images/right.gif&amp;quot;;
$currentpageimage = &amp;quot;/images/ball.red.gif&amp;quot;;&amp;#64;pageimages = qw( /images/ball.gray.gif );
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;A screenshot of the HTML generated when the fifth page is selected:&lt;/p&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="image1" src="http://data.agilitynerd.com//images/moreentries_pagelink_6.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;With these settings:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
$numpagelinks = 10;
$textlinks = 1;                # set to 1 to enable text links 0 to disable
$imagelinks = 1;        # set to 1 to enable image links 0 to disable
$prevlinkimage = &amp;quot;/images/left.gif&amp;quot;;
$nextlinkimage = &amp;quot;/images/right.gif&amp;quot;;
$currentpageimage = &amp;quot;/images/text.gif&amp;quot;;&amp;#64;pageimages = qw( /images/ball.gray.gif /images/ball.red.gif );
&lt;/pre&gt;
&lt;p&gt;A screenshot of the HTML generated when the fifth page is selected:&lt;/p&gt;
&lt;div class="thumbnail"&gt;&lt;p&gt;&lt;img alt="image2" src="http://data.agilitynerd.com//images/moreentries_pagelink_4.jpg" /&gt;&lt;/p&gt;
&lt;/div&gt;&lt;p&gt;Thanks again to Jason for creating this plugin. It was fun to modify and
I hope it is useful for other Blosxom webloggers.&lt;/p&gt;
</summary></entry><entry><title>See More... Added to Article Display on Index Pages</title><link href="http://tech.agilitynerd.com/see-more-added-to-article-display-on-index-pa-1.html" rel="alternate" /><updated>2004-09-26T19:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2004-09-26:see-more-added-to-article-display-on-index-pa-1.html</id><summary type="html">&lt;p&gt;I know my Agility readers will give me a hard time about this
non-Agility post, but hey I'm &amp;quot;thinking about technology&amp;quot; too. I was
limiting to five the number of articles/posts on my main and index pages
to minimize the download time for non cable/DSL visitors. But I've
noticed some searches hit my site for which an article was moved off my
main page and I'm sure they didn't hunt around for the missing post. So
I wanted to show more articles without increasing the download time.&lt;/p&gt;
&lt;p&gt;The &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; plugin &lt;a class="reference external" href="http://www.blosxom.com/plugins/display/seemore.htm"&gt;seemore&lt;/a&gt; automatically adds a link labeled &amp;quot;See
More...&amp;quot; in place of a special label in each article. Selecting the link
takes you to the full text of the article. This useful plugin was
developed by &lt;a class="reference external" href="http://molelog.molehill.org/blox/Computers/Internet/Web/Blosxom/SeeMore/"&gt;Todd Larason&lt;/a&gt;, a blogger who's developed a number of
powerful Blosxom plugins.&lt;/p&gt;
&lt;p&gt;I made a minor modification to seemore, which I've forwarded to Todd, to
allow showing the entire article for a configurable number of articles
on a page. This lets you see the full text and diagrams for the latest
articles and only seeing the first couple paragraphs of the older
articles.&lt;/p&gt;
&lt;p&gt;If you are a Blosxom blogger and would like my modifications you can
download my modified version &lt;a class="reference external" href="http://data.agilitynerd.com/downloads/seemore.zip"&gt;here&lt;/a&gt;. Unzip it and put it in your plugin
directory. By default it should work the same as Todd's original
version. A new configuration variable $show_all_of_stories_until can
be set to the number of stories on each index page that you'd like to
show in full.&lt;/p&gt;
&lt;p&gt;So I've modified most of my posts to include the seemore label and
increased the number of articles shown on each index page to
$blosxom::num_entries. I like this new site configuration; let me know
if you do too.&lt;/p&gt;
</summary></entry><entry><title>Backup Your Data Lately?</title><link href="http://tech.agilitynerd.com/backup-your-data-lately-1.html" rel="alternate" /><updated>2004-08-20T19:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2004-08-20:backup-your-data-lately-1.html</id><summary type="html">&lt;p&gt;A few weeks ago I bought a &lt;a class="reference external" href="http://www.ximeta.com/products/network_drives/netdisk/index.php"&gt;Ximeta NetDisk&lt;/a&gt; 120 GB external hard drive
to back up the data on my home computers. There are a number of vendors
making hard drives that support USB connections; this model is unique in
that it also supports direct ethernet connections. Unlike more expensive
Network Attached Storage devices (more than $ 1000) this unit only cost
a little more than the hard drive itself; about $ 150. The only downside
is that it uses it's own proprietary network protocol which requires
installation of a driver on any computers using the drive.&lt;/p&gt;
&lt;p&gt;My goal was to put this drive on my network switch and back up data from
my XP, WinME, RedHat 8.0, and Win95 machines. It turns out this was a
little trickier than I expected. So I spent the better part of the
morning on a cool, grey Chicago day making this all work.&lt;/p&gt;
&lt;p&gt;The disk comes formatted for NTFS but in order for it to be shared on
the older Windows platforms and my Linux machine it needs to be
reformatted to use a FAT32 file system. Windows XP doesn't support
formatting drives for FAT32 so I had to install the Ximeta driver
software on my WinME machine and install the NetDisk on my network.&lt;/p&gt;
&lt;p&gt;Running fdisk and reformatting the hard drive is the only &amp;quot;scary&amp;quot; aspect
of the installation. Choosing the wrong drive or partition would be a
&lt;em&gt;bad&lt;/em&gt; thing. This &lt;a class="reference external" href="http://www.ximeta.com/support/guides/netdisk/ndas/98seme/05.php"&gt;document&lt;/a&gt; gives a good step by step description.&lt;/p&gt;
&lt;p&gt;I was skeptical that running fdisk over the network would work
correctly, but it did. At this point I was able to view the drive on
both my WinME and XP systems and copy data to the drive as if it was
locally connected.&lt;/p&gt;
&lt;p&gt;I was pretty sure making the drive work for Linux would be difficult.
Unfortunately, the PDF documents from the Ximeta website are unreadable
as they require installing the Korean Acrobat extensions... thankfully
Google has its &amp;quot;View has HTML&amp;quot; facility which let me read the RedHat
instructions. The docs on the install CDROM are viewable (but don't
include the RedHat docs).&lt;/p&gt;
&lt;p&gt;I'll spare you all the trial and error but after downloading the &lt;a class="reference external" href="http://www.ximeta.com/support/downloads/red_hat_8/index.php"&gt;driver
RPM&lt;/a&gt; from the website and installing it I couldn't configure and
connect to the drive on the network. It could be that ports required for
their protocol aren't opened on my Linux machine, but Ximeta doesn't
give any information on what ports are used by their driver. The admin
tool gives some cryptic error messages that Googling and the docs didn't
explain (the docs recommend reinstalling the drivers for any errors...).
I ended up connecting the drive directly via USB and was able to mount
the drive and backup my user and system accounts to the disk.&lt;/p&gt;
&lt;p&gt;So it looks like my goal of leaving the drive on the network and copying
to it from any computer will only work for Windows machines. But at
least I have a mechanism for backing up all my machines, that is easy
enough that I'll use it all the time. My next step is looking into
configuring &lt;a class="reference external" href="http://rsync.samba.org"&gt;rsync&lt;/a&gt; or a similar mechanism to only backup the changed
files to the NetDisk.&lt;/p&gt;
&lt;p&gt;In summary, I'd recommend this drive for anyone who is using Windows XP;
it is plug and play for that operating system. If you are computer savvy
you can make this hard drive play with other systems too.&lt;/p&gt;
&lt;p&gt;However you do it, &lt;strong&gt;backup your computer!&lt;/strong&gt;&lt;/p&gt;
</summary><category term="sysadmin" /></entry><entry><title>Centering DIVs in CSS</title><link href="http://tech.agilitynerd.com/centering-divs-in-css-1.html" rel="alternate" /><updated>2004-08-15T00:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2004-08-15:centering-divs-in-css-1.html</id><summary type="html">&lt;p&gt;I took a look at this site on an 800x600 resolution monitor and didn't
like how the original layout looked. Having the menu box on the left hand
side with a &lt;tt class="docutils literal"&gt;20px&lt;/tt&gt; margin to the left used up too much screen space. So I
decided to move the menu box to the right of the main body of the page.&lt;/p&gt;
&lt;p&gt;I could have hard-coded the box position; but decided that I wanted
to have the main body remain centered regardless of browser size.
That would complicate the positioning of the menu box (which is it's
own div not contained with the main page). Once again &lt;a class="reference external" href="http://www.alistapart.com"&gt;A List Apart&lt;/a&gt; had
a good &lt;a class="reference external" href="http://www.alistapart.com/articles/journey/"&gt;article&lt;/a&gt;describing how they got their CSS centering to work.&lt;/p&gt;
&lt;p&gt;The ALA article had links to these two pages describing the methods
I used: &lt;a class="reference external" href="http://bluerobot.com/web/css/center1.html"&gt;Auto Width Margins&lt;/a&gt;and &lt;a class="reference external" href="http://bluerobot.com/web/css/center2.html"&gt;NegativeMargins&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The AgilityNerd site has two main divs: #page for the main body
and#sidebar for the box containing the menu. I used the Auto Width
Margins method to center the main body:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
#page { font-size: 13px; background: white; border: 1px solid black;
top: 0px; width: 640px; margin: 0px auto; margin-bottom: 50px;
text-align: left;}
&lt;/pre&gt;
&lt;p&gt;The two lines of interest are &lt;tt class="docutils literal"&gt;margin: 0px auto;` and &lt;span class="pre"&gt;``text-align:&lt;/span&gt; left;&lt;/tt&gt;.
Thelater is required because I used the IE 5.x workaround of
specifying &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;text-align:&lt;/span&gt; center;&lt;/tt&gt; in the body element.&lt;/p&gt;
&lt;p&gt;So now that the main body of the site was centered, I wanted the menu to
be floating off to the right of the main body. The Negative
Margins method let me horizontally center the divand also offset it
horizontally by any amount. I offset it by half the(fixed) width of the
main body plus a little space. The additionalspace keeps IE 5.x
browser's miscalculation of sizes from causing theborders to not meet
correctly:&lt;/p&gt;
&lt;pre class="literal-block"&gt;
#sidebar { background: white; font-size: 12px; position: fixed;
width: 140px; height: auto; top: 70px; left: 50%; margin-left:
330px; text-align: center; border: 1px solid black; }
&lt;/pre&gt;
&lt;p&gt;Unlike the article, I used &lt;tt class="docutils literal"&gt;position: fixed&lt;/tt&gt; instead of absolute. This
allows the menubox to float in place during scrolling (but only for CSS
compliant browsers like: Mozilla/Netscape).&lt;/p&gt;
</summary><category term="css" /><category term="webdevelopment" /></entry><entry><title>CSS for Print Media</title><link href="http://tech.agilitynerd.com/css-for-print-media-1.html" rel="alternate" /><updated>2004-08-14T23:16:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2004-08-14:css-for-print-media-1.html</id><summary type="html">&lt;p&gt;In keeping with my desire to have this site's layout be easy to read,I
wanted it to look good in print too. The layout uses &lt;a class="reference external" href="http://www.w3.org/Style/CSS/"&gt;CSS&lt;/a&gt; instead
of HTML tables. This lets me position the divscontaining various text for
the screen and it allows me to positionand hide text differently for the
printed media.&lt;/p&gt;
&lt;p&gt;Any CSS book covers the basics of changing the display,
position, margins, and padding along with the use of separate style
sheets forprint and screen media. But I ran into problems with printing
mysite's pages with the Mozilla/Netscape browsers. Only
the first page of my main div would print. Alittle Googling turned up
this excellent &lt;a class="reference external" href="http://www.alistapart.com/articles/goingtoprint/"&gt;article&lt;/a&gt; on &lt;a class="reference external" href="http://www.alistapart.com"&gt;A ListApart&lt;/a&gt;. ALA is a great resource for
web developers.&lt;/p&gt;
&lt;p&gt;The only clarification I have is that since both the screen and the print
CSS files are included, the print CSS file's elements may needto set
additional properties as well as reseting properties that existin the
screen media file. Otherwise the definitions for the screen will be used.
I had to set some div's position properties to static to get them back
into the document flow.&lt;/p&gt;
</summary></entry><entry><title>AgilityNerd Blosxom Plugins</title><link href="http://tech.agilitynerd.com/agilitynerd-blosxom-plugins-1.html" rel="alternate" /><updated>2004-08-10T19:00:00-05:00</updated><author><name>Steve Schwarz</name></author><id>tag:tech.agilitynerd.com,2004-08-10:agilitynerd-blosxom-plugins-1.html</id><summary type="html">&lt;p&gt;&lt;strong&gt;Updated 20-Jun-08 for addition of storytitle&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I'm really impressed with the &lt;a class="reference external" href="http://blosxom.sourceforge.net/"&gt;Blosxom&lt;/a&gt; &lt;a class="reference external" href="http://blosxom.com/documentation/users/plugins.html"&gt;plugin&lt;/a&gt; mechanism. &lt;a class="reference external" href="http://www.raelity.org/"&gt;Rael
Dornfest&lt;/a&gt;, author of Blosxom, put hooks at strategic points in the page
generation process. At each hook point the main script iterates all the
plugins present in the plugin directory. Plugins that implement the hook
function can then modify the state or add additional data for use in the
page presentation.&lt;/p&gt;
&lt;p&gt;Consequently, a plugin only implements the hook functions it needs
invoked to provide its features. Some powerful plugins need only
implement one or two hook functions and are implemented with a couple
dozen lines of Perl code.&lt;/p&gt;
&lt;p&gt;When I started building this site I found it a little confusing to sort
through all the plugins and understand their functionality. The ease of
creating and modifying plugins has allowed a lot of developers to
contribute to the Blosxom plugin &lt;a class="reference external" href="http://blosxom.com/plugins"&gt;registry&lt;/a&gt;. I found it helpful when
Blosxom sites listed the plugins that implemented their site's features.
So here is the list of plugins I use.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/syndication/atomfeed.htm"&gt;atomfeed&lt;/a&gt; - Provides the AgilityNerd Atom feed. Point
your browser to &lt;a class="reference external" href="http://agilitynerd.com/blog/index.atom"&gt;http://agilitynerd.com/blog/index.atom&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/display/breadcrumbs.htm"&gt;breadcrumbs&lt;/a&gt; - Creates the click-able trail to the current position in
the weblog's path shown at the top of all .index pages and at the bottom
of each article.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/display/binary.htm"&gt;binary&lt;/a&gt; - Used to supply images from the directory in which an article
exists. Modified to use LWP::MediaTypes since MIME::Types isn't
installed on this web server. I also commented out the interpolate()
function and added it to the one in interpolate_fancy to make this
plugin coexist with interpolate fancy.&lt;/p&gt;
&lt;p&gt;I no longer use binary because it is faster to just have Apache serve
binary files directly from one or more directories outside of the
Blosxom src tree.&lt;/p&gt;
&lt;p&gt;I've stopped using this plugin since it requires Blosxom to look at each
file to determine whether or not it should be skipped. So I'm starting
to put all binary files in my images directory outside of Blosxom. This
has the added advantage that I can just put &amp;quot;/images&amp;quot; and &amp;quot;/video&amp;quot; in by
robots.txt file so my images and videos don't get indexed by search
engine robots.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/category/categories.htm"&gt;categories&lt;/a&gt; - Displays the tree of paths/categories of articles on the
&lt;a class="reference external" href="http://agilitynerd.com/blog/navigate/"&gt;navigate&lt;/a&gt; page. Slightly modified to remove the root directory from
the display. Added a hide feature to hide specified directories entirely
- used this to prune subdirectories for my side bar menu. Removed
flavour ending from all generated links since all paths are valid links,
are shorter for people emailing links around, and would always use the
default flavour. Modified to not calculate breadcrumbs since I use the
breadcrumbs plugin for that feature.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/general/config.htm"&gt;config&lt;/a&gt; - Allows any Perl variable to be set differently in any
category/directory. I use this to change the sort order for my
&lt;a class="reference external" href="http://agilitynerd.com/blog/agility/glossary/"&gt;glossary&lt;/a&gt; page to be alphabetical order by changing the sort_order
plugin's sorder variable.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/indexing/entries_cache.htm"&gt;entries_cache&lt;/a&gt; - Speeds up processing of the site by caching the
article index to avoid scanning directories until a configured amount of
time has elapsed. Also caches article creation time to allow for editing
of articles without changing their timestamps.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/blosxom-hit-counter-and-favorites-plugins-1.html"&gt;favorites&lt;/a&gt; - Used to automatically generate my &lt;a class="reference external" href="http://agilitynerd.com/blog/static/Favorites.html"&gt;Favorites page&lt;/a&gt; based on the visitor hit counts recorded by my hitcounter plugin.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/include/file.htm"&gt;file&lt;/a&gt; - Used to include the navigational side div into all pages
without copy/paste of the same HTML.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/search/find.htm"&gt;find&lt;/a&gt; - Provides search capabilities for all articles, comments, and
linkbacks on the web site. HTML slightly modified to remove the advanced
search link. Also set the hidden &amp;quot;path&amp;quot; input to the empty string so the
entire site is searched regardless of where in the hierarchy the search
was initiated.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/text/foreshortened.htm"&gt;foreshortened&lt;/a&gt; - Ends articles at the end of the first sentence. Used
to provide the shortened version of articles for use in RSS and Atom
feeds.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/display/headlines.htm"&gt;headlines&lt;/a&gt; - Used to determine all the entries in my &lt;a class="reference external" href="http://agilitynerd.com/blog/agility/glossary/"&gt;glossary&lt;/a&gt;
category and create links in alphabetical order. Requires
interpolate_fancy.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/files/hide.htm"&gt;hide&lt;/a&gt; - Excludes directories of articles from the index views (like
the agilitynerd.com main page) but still allows them to be searched by
find.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/blosxom-hit-counter-and-favorites-plugins-1.html"&gt;hitcounter&lt;/a&gt; - Used to automatically generate the
number of visitors displayed in the footer for each page on my site.
These hit counts per page are used by my favorites plugin to generate my
&lt;a class="reference external" href="http://agilitynerd.com/blog/static/Favorites.html"&gt;Favorites page&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/interpolate/interpolate_fancy.htm"&gt;interpolate_fancy&lt;/a&gt; - Used to remove the space allocated for the
results of find and the breadcrumbs plugin when their variables aren't
defined. I think I'll use this more in the future to dynamically change
the page format.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/blosxom-plugins-lastcommented-and-lastread-1.html"&gt;lastcommented&lt;/a&gt; - Used to automatically
generate the list of the last 10 articles that were commented shown in
the side bar on each page of the site.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/blosxom-plugins-lastcommented-and-lastread-1.html"&gt;lastread&lt;/a&gt; - Used to automatically generate
the list of the last 10 articles that were read shown in the side bar on
each page of the site.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/display/moreentries.htm"&gt;moreentries&lt;/a&gt; - Creates the &amp;quot;Previous&amp;quot; and &amp;quot;Next&amp;quot; links at the bottom
of each index or search page when there are more than
&lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;$blosxom::num\_entries&lt;/span&gt;&lt;/tt&gt; entries. I've modified this plugin as I described
&lt;a class="reference external" href="/minor-additional-mods-to-blosxom-moreenties-p-1.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/display/postheadprefoot.htm"&gt;postheadprefoot&lt;/a&gt; - Allows each category/directory to have unique text
inserted after the header and/or before the footer. I use this on my
&lt;a class="reference external" href="http://agilitynerd.com/blog/agility/glossary/"&gt;glossary&lt;/a&gt; page to add a category specific subsection. I had to &lt;a class="reference external" href="http://groups.yahoo.com/group/blosxom/message/9364"&gt;modify
the script&lt;/a&gt; to have paths resolve correctly.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/general/redirect.htm"&gt;redirect&lt;/a&gt; - Unmodified version of this plugin. I had to use it when I
moved a directory within my tree so search engine and email list links
would redirect correctly.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/refererblock-version-02-1.html"&gt;refererblock&lt;/a&gt; - A blacklist driven plugin to permanently
redirect referer spam. The redirect probably has little effect on the
receipt of referer spam but the 301 redirect code keeps the spammer's
sites out of my site statistics.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/syndication/rss10.htm"&gt;rss10&lt;/a&gt; - Provides the extra information to provide a valid RSS 1.0
feed for syndication. Slightly modified to work with the writeback
plugin.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/display/seemore.htm"&gt;seemore&lt;/a&gt; - Adds &amp;quot;See more...&amp;quot; link within the text of a post when the
post is shown in an index page. The location of the link is controlled
by placing a special tag in the posting source file. I have &lt;a class="reference external" href="/see-more-added-to-article-display-on-index-pa-1.html"&gt;modified&lt;/a&gt;
this plugin to show the first &amp;quot;n&amp;quot; entries of an index page in full.
Furthe&lt;/p&gt;
&lt;p&gt;r modified to always create a link to my html flavoured pages.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://blosxom.ookee.com/blosxom/plugins/v2/sort_order-v0i85"&gt;sort_order&lt;/a&gt; - Allows sorting by date, directory names, or file name.
I modified it to not read the URI parameters and just use a
configuration variable. Then I used the config plugin to change the sort
order for my &lt;a class="reference external" href="http://agilitynerd.com/blog/agility/glossary/"&gt;glossary&lt;/a&gt; page to sort by file name instead of in reverse
chronological order.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.leverton.org/blosxom/Software/Projects/Blosxom/storytitle.html"&gt;storytitle&lt;/a&gt; - sets the &amp;lt;title&amp;gt; element in the HTML &amp;lt;head&amp;gt; with the
title of the article or the name of the category. I use it
to aid in navigation and site statistics]].&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/date/timezone2.htm"&gt;timezone&lt;/a&gt; - Adjusts timestamps automatically created or entered using
meta-creation_date: to be offset correctly to my timezone; which is
different from the web server's timezone.&lt;/p&gt;
&lt;p&gt;I no longer use timezone since I can set the timezone on my
Slicehost server.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="/blosxom-default-flavour-plugin-fixes-unknown-1.html"&gt;defaultflavour&lt;/a&gt; - Provides the user with the
default Blosxom flavoured feed when an unknown flavour is requested.
This keeps the user from getting the Unknown Flavour error across the
top of the page.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://varg.dyndns.org/psi/pub/code/misc/wbcaptcha.html"&gt;wbcaptcha&lt;/a&gt; - Provides an ASCII image via &lt;a class="reference external" href="http://www.figlet.org"&gt;FIGlet&lt;/a&gt; when visitors enter
comments to stop spam bots from saturating my blog comments. See this article for &lt;a class="reference external" href="/comment-spam-and-wbcaptcha-plugin-enhancement-1.html"&gt;my modifications&lt;/a&gt; to this plugin.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.blosxom.com/plugins/text/wikiwordish.htm"&gt;wikiwordish&lt;/a&gt; - Provides WikiWord-like linking to Wiki articles, local
article file names and modified to replace WikiWords with HTML &amp;lt;a&amp;gt;
links. This plugin saves me from having to enter links to common sites
to which I always refer. I had to name this file 00wikiwordish so that
WikiWords would be correctly replaced in RSS and Atom feeds.&lt;/p&gt;
&lt;p&gt;&lt;a class="reference external" href="http://www.lathi.net/twiki-bin/view/Main/BlogSpam"&gt;writeback blacklist plugin&lt;/a&gt; - Provides comment and TrackBack
capability. This is Doug Alcorn's blacklist modified version to help
fight comment spam. I've slightly modified mine to try to protect
comment poster's from spam by obfuscating their email addresses.&lt;/p&gt;
&lt;p&gt;In case anyone is trying to get these plugins to &amp;quot;play&amp;quot; together I have
them named as follows:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;000refererblock&lt;/li&gt;
&lt;li&gt;001redirect&lt;/li&gt;
&lt;li&gt;002defaultflavour&lt;/li&gt;
&lt;li&gt;005wikiwordish&lt;/li&gt;
&lt;li&gt;007google_highlight&lt;/li&gt;
&lt;li&gt;008wbcaptcha&lt;/li&gt;
&lt;li&gt;01atomfeed&lt;/li&gt;
&lt;li&gt;01breadcrumbs&lt;/li&gt;
&lt;li&gt;01categories&lt;/li&gt;
&lt;li&gt;01config&lt;/li&gt;
&lt;li&gt;01entriescache&lt;/li&gt;
&lt;li&gt;01favorites&lt;/li&gt;
&lt;li&gt;01file&lt;/li&gt;
&lt;li&gt;01find&lt;/li&gt;
&lt;li&gt;01foreshortened&lt;/li&gt;
&lt;li&gt;01fullcategories&lt;/li&gt;
&lt;li&gt;01headlines&lt;/li&gt;
&lt;li&gt;01hide&lt;/li&gt;
&lt;li&gt;01moreentries&lt;/li&gt;
&lt;li&gt;01postheadprefoot&lt;/li&gt;
&lt;li&gt;01rss10&lt;/li&gt;
&lt;li&gt;01seemore&lt;/li&gt;
&lt;li&gt;01sort_order&lt;/li&gt;
&lt;li&gt;01storytitle&lt;/li&gt;
&lt;li&gt;01writeback&lt;/li&gt;
&lt;li&gt;02hitcounter&lt;/li&gt;
&lt;li&gt;02lastcommented&lt;/li&gt;
&lt;li&gt;02lastread&lt;/li&gt;
&lt;li&gt;02recentwritebacks&lt;/li&gt;
&lt;li&gt;50interpolate_fancy&lt;/li&gt;
&lt;/ul&gt;
</summary><category term="blosxom" /><category term="perl" /><category term="plugin" /></entry></feed>
