<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Mikkel Høgh]]></title>
  <link href="http://mikkel.hoegh.org/atom.xml" rel="self"/>
  <link href="http://mikkel.hoegh.org/"/>
  <updated>2012-10-07T14:11:45+02:00</updated>
  <id>http://mikkel.hoegh.org/</id>
  <author>
    <name>Mikkel Høgh</name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html">How to configure Octopress for Drupal Planet syndication</title>
    <link href="http://mikkel.hoegh.org/blog/2012/09/17/how-to-configure-octopress-for-drupal-planet-syndication/"/>
    <updated>2012-09-17T14:32:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2012/09/17/how-to-configure-octopress-for-drupal-planet-syndication</id>
    <content type="html"><![CDATA[<p>Since I moved my blog to <a href="http://octopress.org/">Octopress</a>, I&#8217;ve been struggling with my
blog posts not getting picked up by <a href="http://drupal.org/planet/">Planet Drupal</a>.</p>

<p>When I started using Octopress, it only had a site-wide <a href="http://en.wikipedia.org/wiki/Atom_%28standard%29">Atom feed</a>,
and despite my little experience with Ruby or Liquid, I managed to hack
together <a href="https://github.com/imathis/octopress/pull/201">category-specific feeds for Octopress</a>. These were
merged into Octopress core, so if you get the latest version from the
<code>master</code> branch, you should have these.</p>

<p>Sadly, despite <a href="http://mikkel.hoegh.org/blog/categories/drupal/atom.xml">my feed</a> being <a href="http://feedvalidator.org/check.cgi?url=http%3A%2F%2Fmikkel.hoegh.org%2Fblog%2Fcategories%2Fdrupal%2Fatom.xml">valid Atom 1.0</a>, Planet Drupal does
not parse it properly, and my blog posts were still not included in its
feed. This might not be all that surprising to someone more familiar
with the history of the <a href="https://github.com/drupal/drupal/blob/7.x/modules/aggregator/aggregator.module">aggregator.module</a>, which is the feed
aggregation module that ships with Drupal, that Planet Drupal uses for
its syndication.</p>

<p>Politely said, this module is not one of the parts of Drupal core that
gets the most attention, and its <a href="https://github.com/drupal/drupal/blob/7.x/modules/aggregator/aggregator.parser.inc#L67">feed parsing code</a> is not exactly
state-of-the-art. In fact, it only supports a subset of the Atom spec
and a particular version of the older and inferior <a href="http://en.wikipedia.org/wiki/RSS">RSS</a>. Now, I won&#8217;t
get into the nasty <a href="http://en.wikipedia.org/wiki/History_of_web_syndication_technology">history of web syndication technology</a>, but
suffice to say that this is one of the instances where I find myself
wishing that the Drupal community wasn&#8217;t so <a href="http://en.wikipedia.org/wiki/Not_Invented_Here">NIH</a>-prone. (that goes
for you, too, project.module).</p>

<p>So, long story short, if you want to be on Drupal planet, you have to
implement a feed format it understands, so I made an old-school RSS
format, that should correspond almost exactly to what Drupal itself
outputs when it generates feeds.</p>

<h2>The template</h2>

<figure class='code'><figcaption><span>&#8220;Drupal Planet feed template&#8221;  (planet_drupal.xml)</span> <a href='http://mikkel.hoegh.org/downloads/code/planet_drupal.xml'>download</a></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'>---
</span><span class='line'>layout: nil
</span><span class='line'>---
</span><span class='line'><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;</span>
</span><span class='line'><span class="nt">&lt;rss</span> <span class="na">version=</span><span class="s">&quot;2.0&quot;</span> <span class="na">xml:base=</span><span class="s">&quot;{{ site.url }}&quot;</span> <span class="na">xmlns:atom=</span><span class="s">&quot;http://www.w3.org/2005/Atom&quot;</span> <span class="na">xmlns:dc=</span><span class="s">&quot;http://purl.org/dc/elements/1.1/&quot;</span><span class="nt">&gt;</span>
</span><span class='line'><span class="nt">&lt;channel&gt;</span>
</span><span class='line'>  <span class="nt">&lt;title&gt;</span>Planet Drupal | {{ site.title | xml_escape }}<span class="nt">&lt;/title&gt;</span>
</span><span class='line'>  <span class="nt">&lt;link&gt;</span>{{ site.url }}/planet_drupal.xml<span class="nt">&lt;/link&gt;</span>
</span><span class='line'>  <span class="nt">&lt;atom:link</span> <span class="na">href=</span><span class="s">&quot;{{ site.url }}/planet_drupal.xml&quot;</span> <span class="na">rel=</span><span class="s">&quot;self&quot;</span> <span class="na">type=</span><span class="s">&quot;application/rss+xml&quot;</span> <span class="nt">/&gt;</span>
</span><span class='line'>  <span class="nt">&lt;language&gt;</span>en<span class="nt">&lt;/language&gt;</span>
</span><span class='line'>  <span class="nt">&lt;generator&gt;</span>Octopress<span class="nt">&lt;/generator&gt;</span>
</span><span class='line'>  <span class="nt">&lt;description&gt;</span>Planet Drupal RSS feed for {{ site.url }}<span class="nt">&lt;/description&gt;</span>
</span><span class='line'>
</span><span class='line'>{% for post in site.categories[&#39;Drupal&#39;] limit: 10 %}
</span><span class='line'>  <span class="nt">&lt;item&gt;</span>
</span><span class='line'>    <span class="nt">&lt;title&gt;</span>{{ post.title | xml_escape }}<span class="nt">&lt;/title&gt;</span>
</span><span class='line'>    <span class="nt">&lt;link&gt;</span>{{ site.url }}{{ post.url }}<span class="nt">&lt;/link&gt;</span>
</span><span class='line'>    <span class="nt">&lt;dc:creator&gt;</span>{{ site.author | xml_escape }}<span class="nt">&lt;/dc:creator&gt;</span>
</span><span class='line'>    <span class="nt">&lt;guid</span> <span class="na">isPermaLink=</span><span class="s">&quot;true&quot;</span><span class="nt">&gt;</span>{{ site.url }}{{ post.url }}<span class="nt">&lt;/guid&gt;</span>
</span><span class='line'>    <span class="nt">&lt;pubDate&gt;</span>{{ post.date | date: &quot;%a, %d %b %Y %H:%M:%S %z&quot; }}<span class="nt">&lt;/pubDate&gt;</span>
</span><span class='line'>    <span class="nt">&lt;description&gt;</span><span class="cp">&lt;![CDATA[{{ post.content | expand_urls: site.url | cdata_escape }}]]&gt;</span><span class="nt">&lt;/description&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/item&gt;</span>
</span><span class='line'>{% endfor %}
</span><span class='line'>
</span><span class='line'><span class="nt">&lt;/channel&gt;</span>
</span><span class='line'><span class="nt">&lt;/rss&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<h2>How to do it</h2>

<ol>
<li>Download the feed template attached above.</li>
<li><p>Put it somewhere in the <code>source</code> folder of your Octopress site.</p>

<p> I have it at <code>source/planet_drupal.xml</code>, but the name and location
 should make no difference at all. Since I have it at the route of
 the source tree, my feed is available at http://mikkel.hoegh.org/planet_drupal.xml</p></li>
<li>Change the <code>{% for post in site.categories['Drupal'] limit: 10 %}</code>
 line to match the name of the category you want to make a feed of.
 In my case, the category name is <code>Drupal</code>. This is probably case
 sensitive, so be sure to be consistent when categorizing your posts.</li>
<li><p>If you haven&#8217;t done so already, tag your posts with the right
 category to have them included in the feed.</p>

<p> This is how this post is tagged, for reference. This is standard
 Octopress post metadata.</p>

<pre><code> ---
 layout: post
 title: "How to configure Octopress for Drupal Planet syndication"
 date: 2012-08-12 22:32
 comments: true
 categories: [ Drupal, Octopress ]
 ---
 [post body here]
</code></pre></li>
<li>Redirect your current feed address or report your new feed address to
 the Drupal.org webmasters.</li>
</ol>

]]></content>
  </entry>
  
  <entry>
    <title type="html">Varnish as reverse proxy with nginx as web server and SSL terminator</title>
    <link href="http://mikkel.hoegh.org/blog/2012/07/24/varnish-as-reverse-proxy-with-nginx-as-web-server-and-ssl-terminator/"/>
    <updated>2012-07-24T23:37:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2012/07/24/varnish-as-reverse-proxy-with-nginx-as-web-server-and-ssl-terminator</id>
    <content type="html"><![CDATA[<p>Or, if you like, the nginx-Varnish-nginx sandwich.</p>

<h2>Why?</h2>

<p>This is, admittedly, a bit unorthodox. But here’s my rationale:</p>

<p>Since time immemorial (ie. more than a couple of years of Internet time),
we at <a href="http://revealit.dk/">Reveal IT</a> have been deploying <a href="https://www.varnish-cache.org/">Varnish</a> in front of the web
sites we build for our customers, with the dual purpose of a faster (and
thus better) user experience and conservation of server resources.</p>

<p>In our previous Varnish setups, only standard
HTTP would be passed through Varnish, and HTTPS traffic gets paased
directly to <a href="http://nginx.org/">nginx</a>.
However, we&#8217;re seeing increasing demands for <a href="http://en.wikipedia.org/wiki/Transport_Layer_Security">TLS/SSL</a>, and more sites
are going HTTPS-only.</p>

<p>Varnish itself does not support (and with <a href="https://www.varnish-cache.org/docs/trunk/phk/ssl.html">good reason</a>), so
we need another program to provide the secure connection. I&#8217;ve tried a
couple of commonly recommended TLS/SSL terminators, namely <a href="http://www.apsis.ch/pound/">Pound</a> and
<a href="https://github.com/bumptech/stud">stud</a>, but I&#8217;ve yet to succeed in getting either to work on my server
setup. And since I didn&#8217;t feel like spending an entire workday getting
to know either tool enough to deploy it with confidence, I was reminded
of the old quote</p>

<blockquote><p>I suppose it is tempting, if the only tool you have is a hammer, to
treat everything as if it were a nail.</p></blockquote>

<p>In this case, I have an excellent hammer, nginx, so I decided to treat
this problem like a nail. Here&#8217;s my current setup:</p>

<h2>How?</h2>

<p>This is all running on a single machine, &#8220;rajka&#8221;, Varnish is listening
on port 80, passing uncached (or uncacheable) requests on port 8080.</p>

<p>Now to the interesting parts. I&#8217;ve set up an nginx virtual host for the
TLS terminator work:</p>

<figure class='code'><figcaption><span>&#8220;nginx TLS terminator&#8221;  (nginx-tls-proxy.conf)</span> <a href='http://mikkel.hoegh.org/downloads/code/nginx-tls-proxy.conf'>download</a></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
</pre></td><td class='code'><pre><code class='nginx'><span class='line'><span class="k">server</span> <span class="p">{</span>
</span><span class='line'>  <span class="kn">listen</span> <span class="mi">443</span> <span class="s">ssl</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="kn">server_name</span> <span class="s">revealit.dk</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="kn">ssl_certificate</span> <span class="s">/etc/ssl/revealit.dk/cert_chain.pem</span><span class="p">;</span>
</span><span class='line'>  <span class="kn">ssl_certificate_key</span> <span class="s">/etc/ssl/revealit.dk/key.pem</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>  <span class="kn">location</span> <span class="s">/</span> <span class="p">{</span>
</span><span class='line'>    <span class="c1"># Pass the request on to Varnish.</span>
</span><span class='line'>    <span class="kn">proxy_pass</span>  <span class="s">http://127.0.0.1</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1"># Pass a bunch of headers to the downstream server, so they&#39;ll know what&#39;s going on.</span>
</span><span class='line'>    <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$host</span><span class="p">;</span>
</span><span class='line'>    <span class="kn">proxy_set_header</span> <span class="s">X-Real-IP</span> <span class="nv">$remote_addr</span><span class="p">;</span>
</span><span class='line'>    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1"># Most web apps can be configured to read this header and understand that the current session is actually HTTPS.</span>
</span><span class='line'>    <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-Proto</span> <span class="s">https</span><span class="p">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1"># We expect the downsteam servers to redirect to the right hostname, so don&#39;t do any rewrites here.</span>
</span><span class='line'>    <span class="kn">proxy_redirect</span>     <span class="no">off</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>While nginx virtual host should be pretty self-explanatory, the Varnish
configuration is a bit more tricky. I suggest you take the time to
review our entire <a href="https://github.com/revealit/varnishconf/blob/master/revealit.vcl">varnishconf</a>, but I have extracted the most
relevant parts here:</p>

<figure class='code'><figcaption><span>&#8220;Varnish VCL&#8221;  (varnish-tls-proxy.vcl)</span> <a href='http://mikkel.hoegh.org/downloads/code/varnish-tls-proxy.vcl'>download</a></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
</pre></td><td class='code'><pre><code class='c'><span class='line'><span class="cp"># List of upstream proxies we trust to set X-Forwarded-For correctly.</span>
</span><span class='line'><span class="n">acl</span> <span class="n">upstream_proxy</span> <span class="p">{</span>
</span><span class='line'>  <span class="s">&quot;127.0.0.1&quot;</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">backend</span> <span class="k">default</span> <span class="p">{</span>
</span><span class='line'>  <span class="p">.</span><span class="n">host</span> <span class="o">=</span> <span class="s">&quot;127.0.0.1&quot;</span><span class="p">;</span>
</span><span class='line'>  <span class="p">.</span><span class="n">port</span> <span class="o">=</span> <span class="s">&quot;8080&quot;</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">sub</span> <span class="n">vcl_recv</span> <span class="p">{</span>
</span><span class='line'>  <span class="cp"># Set the X-Forwarded-For header so the backend can see the original</span>
</span><span class='line'>  <span class="cp"># IP address. If one is already set by an upstream proxy, we&#39;ll just re-use that.</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">client</span><span class="p">.</span><span class="n">ip</span> <span class="o">~</span> <span class="n">upstream_proxy</span> <span class="o">&amp;&amp;</span> <span class="n">req</span><span class="p">.</span><span class="n">http</span><span class="p">.</span><span class="n">X</span><span class="o">-</span><span class="n">Forwarded</span><span class="o">-</span><span class="n">For</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">set</span> <span class="n">req</span><span class="p">.</span><span class="n">http</span><span class="p">.</span><span class="n">X</span><span class="o">-</span><span class="n">Forwarded</span><span class="o">-</span><span class="n">For</span> <span class="o">=</span> <span class="n">req</span><span class="p">.</span><span class="n">http</span><span class="p">.</span><span class="n">X</span><span class="o">-</span><span class="n">Forwarded</span><span class="o">-</span><span class="n">For</span><span class="p">;</span>
</span><span class='line'>  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">set</span> <span class="n">req</span><span class="p">.</span><span class="n">http</span><span class="p">.</span><span class="n">X</span><span class="o">-</span><span class="n">Forwarded</span><span class="o">-</span><span class="n">For</span> <span class="o">=</span> <span class="n">regsub</span><span class="p">(</span><span class="n">client</span><span class="p">.</span><span class="n">ip</span><span class="p">,</span> <span class="s">&quot;:.*&quot;</span><span class="p">,</span> <span class="s">&quot;&quot;</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="n">sub</span> <span class="n">vcl_hash</span> <span class="p">{</span>
</span><span class='line'>  <span class="cp"># URL and hostname/IP are the default components of the vcl_hash</span>
</span><span class='line'>  <span class="cp"># implementation. We add more below.</span>
</span><span class='line'>  <span class="n">hash_data</span><span class="p">(</span><span class="n">req</span><span class="p">.</span><span class="n">url</span><span class="p">);</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">req</span><span class="p">.</span><span class="n">http</span><span class="p">.</span><span class="n">host</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>      <span class="n">hash_data</span><span class="p">(</span><span class="n">req</span><span class="p">.</span><span class="n">http</span><span class="p">.</span><span class="n">host</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span><span class='line'>      <span class="n">hash_data</span><span class="p">(</span><span class="n">server</span><span class="p">.</span><span class="n">ip</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="cp"># Include the X-Forward-Proto header, since we want to treat HTTPS</span>
</span><span class='line'>  <span class="cp"># requests differently, and make sure this header is always passed</span>
</span><span class='line'>  <span class="cp"># properly to the backend server.</span>
</span><span class='line'>  <span class="k">if</span> <span class="p">(</span><span class="n">req</span><span class="p">.</span><span class="n">http</span><span class="p">.</span><span class="n">X</span><span class="o">-</span><span class="n">Forwarded</span><span class="o">-</span><span class="n">Proto</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'>    <span class="n">hash_data</span><span class="p">(</span><span class="n">req</span><span class="p">.</span><span class="n">http</span><span class="p">.</span><span class="n">X</span><span class="o">-</span><span class="n">Forwarded</span><span class="o">-</span><span class="n">Proto</span><span class="p">);</span>
</span><span class='line'>  <span class="p">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="p">(</span><span class="n">hash</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>The main issue here is the <a href="http://en.wikipedia.org/wiki/X-Forwarded-For"><code>X-Forwarded-For</code></a> header
which is used by the web application to determine the IP address of the
actual client, not any intermediary proxies. Since the <code>X-Forwarded-For</code>
can be used for <a href="http://en.wikipedia.org/wiki/IP_spoofing">IP address spoofing</a>, it is important to configure
this securely.</p>

<p>For your web application to get the correct IP address for the client
user, it needs to be configured to trush the <code>X-Forwarded-For</code> header
for requests coming from the Varnish server. Most web applications have
built-in support for this.</p>

<p>However, this means that we need to be completely sure that we do not
pass on malicious X-Forwarded-For headers from the client. The easiest
way to accomplish this is to simply set the header yourself. But in this
case, we have two levels of proxying. So nginx always sets the X-Forwarded-For
header to the client&#8217;s IP address. Varnish normally does the same, but
if, and only if, the request is coming from <code>127.0.0.1</code> (the IP of the
TLS terminator), we use the value it provided instead.</p>

<p>Now, this is not strictly compliant to how X-Forwarded-For is supposed
to be used (we should actually append the IP address of the TLS
terminator to its value), but since <a href="http://api.drupal.org/api/drupal/includes%21bootstrap.inc/function/ip_address/7">Drupal uses the right-most (ie. last
added) IP address</a>, that would not actually work in our case.</p>

<p>Lastly, by including <code>X-Forwarded-Proto</code> in the <code>vcl_hash</code> function, we
ensure that HTTP and HTTPS requests are cached separately, so a user
visiting the site via HTTPS will get pages where the links are also
HTTPS. This does reduce the efficiency of the cache (since it&#8217;ll leave
two copies of everything in the cache, including images and other static
files that are not protocol-sensitive.</p>

<p>Fixing that issue is left as an exercise to the reader.</p>

<h2>Caveat lector</h2>

<p>Though I&#8217;ve had this idea for a while, I&#8217;ve only had a working, in
production, implementation of this for ~8 hours. It may yet turn out
that this was a horrible idea, but so far it&#8217;s working great.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Deploying LedgerSMB with nginx and Plack on FreeBSD</title>
    <link href="http://mikkel.hoegh.org/blog/2012/04/05/deploying-ledgersmb-with-nginx-and-plack-on-freebsd/"/>
    <updated>2012-04-05T21:59:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2012/04/05/deploying-ledgersmb-with-nginx-and-plack-on-freebsd</id>
    <content type="html"><![CDATA[<p>I have recently been looking at <a href="http://ledgersmb.org/">LedgerSMB</a> for <a href="http://drupaldanmark.dk/">Drupal Danmark</a>’s
accounting needs, but I’ve struggled getting it set up, since it is a
classic <a href="http://en.wikipedia.org/wiki/Common_Gateway_Interface">CGI</a> app, which is not something we usually run on our
servers, especiallty since we use <a href="http://en.wikipedia.org/wiki/Nginx">nginx</a> on our servers, at it does
not support classic CGI. It does support <a href="http://en.wikipedia.org/wiki/FastCGI">FastCGI</a>, so we need some sort
of wrapper around LedgerSMB to get it running.</p>

<p>Fortunately, the wonderful Perl commnuty has a solution for that, namely
<a href="http://plackperl.org/">Plack</a>.</p>

<p>After digging around a bit and getting helpful tips from the LedgerSMB
mailing list, I managed to get a working solution together:</p>

<ol>
<li><p>Preamble</p>

<p> Since we use <a href="http://www.freebsd.org/">FreeBSD</a> in this example, I’m going to use the ports
 collection to install the required CPAN packages for this to work.
 Here we assumbe that you have LedgerSMB and its dependencies
 installed already.</p></li>
<li><p>Installation</p>

<p> The installation is as simple as running this command:</p>

<pre><code> # portmaster www/p5-Plack www/p5-CGI-Emulate-PSGI www/p5-CGI-Compile
</code></pre>

<p> This will install Plack and its dependencies.</p></li>
<li><p>Starting the Plack server</p>

<p> This is fairly simple, in fact. Just proceed to the folder where you
 have LedgerSMB installed and run the following command:</p>

<pre><code> % plackup -MPlack::App::CGIBin -e 'Plack::App::CGIBin-&gt;new(root =&gt; "/usr/local/ledgersmb", exec_cb =&gt; sub { 1 })-&gt;to_app'
</code></pre>

<p> Make sure you adjust the root path in the command to point to your
 LedgerSMB folder.</p>

<p> Unless you have added the LedgerSMB folder to your Perl library
 path, you need to run this command from that folder.</p></li>
<li><p>Configuring nginx</p>

<p> This is very similar to what we do for our PHP-FPM apps, and it
 seems to work as it should:</p>

<pre><code> server {
   listen       443;
   server_name  ledger.example.com;

   keepalive_timeout 300;

   ssl                  on;
   ssl_certificate      /etc/ssl/ledger.example.com.crt;
   ssl_certificate_key  /etc/ssl/ledger.example.com.key;

   root   /usr/local/ledgersmb;

   # Serve the login page at the root.
   location = / {
     rewrite ^ /login.pl redirect;
   }

   location ~ \.pl$ {
     proxy_set_header Host $http_host;
     proxy_set_header X-Forwarded-Host $http_host;
     proxy_set_header X-Real-IP $remote_addr;
     proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     proxy_set_header X-Forwarded-Port 443; #this is important for Catalyst Apps!
     proxy_pass http://localhost:5000; #changed from http://localhost:5000/ which was causing double forward slash problems in the url
   }

   # Deny access to configuration and other nasty places.
   location ~ \.conf$ { deny all; }
   location /users { deny all; }
   location /bin { deny all; }
   location /utils { deny all; }
   location /spool { deny all; }
   location /templates { deny all; }
   location /LedgerSMB { deny all; }
 }
</code></pre></li>
<li><p>Profit</p>

<p> This setup has worked very well for me so far. Hit me up in the
 comments below if you have any improvements :)</p></li>
</ol>

]]></content>
  </entry>
  
  <entry>
    <title type="html">Trouble in Node.js paradise: The mess that is npm</title>
    <link href="http://mikkel.hoegh.org/blog/2011/12/20/trouble-in-node-dot-js-paradise-the-mess-that-is-npm/"/>
    <updated>2011-12-20T02:41:00+01:00</updated>
    <id>http://mikkel.hoegh.org/blog/2011/12/20/trouble-in-node-dot-js-paradise-the-mess-that-is-npm</id>
    <content type="html"><![CDATA[<h3>The preface</h3>

<p>Let me begin by stating that I love building web apps with <a href="http://nodejs.org/">Node.js</a>,
and I think it&#8217;s one of the greatest things that&#8217;s happened in the web
app space this decade. I have been using Node.js for various small
projects the last nine months, so I think I have a reasonable grasp of
the subject matter.</p>

<h3>The mess</h3>

<p>One of the most exiting things about Node.js is the intense vibrance of
the community of developers around it and the strength of tools like npm
and Github that makes it almost effortless to share your Node.js
creations with the world.</p>

<p>However, this has also created a huge problem for developers like me –
that of picking the right module to use. The number of modules on
<a href="http://npmjs.org/">npm</a> is growing at a staggering speed, and it is becoming
increasingly difficult to shift the wheat from the chaff.</p>

<h3>The example</h3>

<p>A real-world example. I&#8217;ve been looking for a tool to help me serve up
my app&#8217;s CSS and JavaScript in as small a package as possible to reduce
load times. That means concatenation and minification, and preferably
compression as well.</p>

<p>In my old <a href="http://djangoproject.com/">Django</a>-days, I&#8217;d have used <a href="https://github.com/jezdez/django_compressor">django_compressor</a>, and if I
was new to the community, I&#8217;d have to look no further than this
<a href="http://djangopackages.com/grids/g/asset-managers/">asset manager comparison page</a> to see that it would be the right
choice for me.</p>

<p>With Node.js, there are no such comforts. I&#8217;ve spent a couple of hours
digging around on Github and npm (mainly by looking at modules that
depend on <a href="https://github.com/mishoo/UglifyJS">UglifyJS</a>), and after eliminating modules that were
obviously unmaintaned/outdated/undocumented, I&#8217;ve managed to shorten my
list to these options:</p>

<ul>
<li>ams</li>
<li>app.js</li>
<li>asereje</li>
<li>assets-packager</li>
<li>auton</li>
<li>bastard</li>
<li>beans</li>
<li>browserify</li>
<li>buddy</li>
<li>buildr</li>
<li>codesurgeon</li>
<li>dryice</li>
<li>express-asset</li>
<li>folio</li>
<li>hem</li>
<li>inliner</li>
<li>masher</li>
<li>nap</li>
<li>piler</li>
<li>polymorph</li>
<li>resmin</li>
<li>smoosh</li>
<li>snockets</li>
<li>stitchup</li>
<li>vivid-builder</li>
<li>wepp</li>
</ul>


<p>Yes, twenty-six possible modules to evaluate. I don&#8217;t have the entire
list, but I&#8217;d expect it to be around a hundred modules.</p>

<h3>The choice</h3>

<p>This is an almost impossible choice. I&#8217;d have to spend a couple of days
trying out each of these modules to figure out which one is the right
fit for me. It&#8217;ll probably be faster for me to write my own little tool
for it thand to educate myself of all these options.</p>

<p>And it&#8217;s not just this space that suffers from this problem. It&#8217;s the
same for routing, templating, testing and similar things that are
commonly used in Node apps.</p>

<h3>The waste</h3>

<p>Each of these modules have been crafted, built. Someone took the time to
explore the problem space, figure out what the his requirements, build a
reusable tool, document it (somewhat), make releases, publish them omn
npm, fix bugs, etc, etc.</p>

<p>The above list probably represents more than two thousand man-hours of
work, most of it wasted.</p>

<p>Of course, some duplication is necessary for the best ideas to float to
the top, but contrasting this with my experiences from the <a href="http://drupal.org/">Drupal</a>
community, this is absurd.</p>

<h3>The solution</h3>

<p>If you have been listening to <a href="http://nodeup.com/">NodeUp</a> (which you probably should if
you&#8217;re at all interested in Node.js), you would know that the npm
developers are working to come up with metrics for packages, that will
make it easier to gauge the quality of a module. This will definitely
help with sorting out the mess, but I think we should also consider what
we can do about the waste and duplication of effort.</p>

<p>I think this is mainly a question of culture. What makes the Drupal
community so strong is how much collaboration is valued. Duplication of
effort is frowned upon, <a href="http://www.palantir.net/blog/responsible-module-maintainership">responsible maintainership</a> <a href="http://buytaert.net/responsible-maintainers">is encouraged</a>.</p>

<p>There&#8217;s no easy way to accomplish this. I won&#8217;t suggest we restrict
access to posting modules on npm or have a formal application system,
like Drupal does, as I do not believe either of those would work with
the Node.js community. We would need more formal structure for that and
a much more cohesive community, and I do not think either of those are
feasible (nor desirable) for the Node.js project.</p>

<h3>The plea</h3>

<p>Instead, I&#8217;d just like to plead with my fellow developers. Before you
decide to roll your own, please see if there is an existing project you
could help improve instead. Consider it a way of repaying for all the
open source work you have benefited from. Paving the road for others,
like others have paved it for you.</p>

<p>I know it&#8217;s fun to do it lone ranger style, to take on the beast and
slay it all by yourself – but ask yourself this: Do you care enough
about this problem space to maintain a module for the next year? Would
you prefer your efforts being 100% of the experience for a dozen people
– or 10% of the experience for thousands of people.</p>

<p>That is why I love open source. Knowing that each of the half a million
Drupal installations out there have a small piece of my work in them.
That the President of the United States relies on my work, in an
infitessimally tiny way.</p>

<p>That is what I&#8217;d like to see more of in the Node.js community.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Shave a couple of stubborn of DIV-wrappers off your Drupal site</title>
    <link href="http://mikkel.hoegh.org/blog/2011/10/20/shave-a-couple-stubborn-of-div-wrappers-off-your-drupal-site/"/>
    <updated>2011-10-20T19:27:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2011/10/20/shave-a-couple-stubborn-of-div-wrappers-off-your-drupal-site</id>
    <content type="html"><![CDATA[<p>One of the more annoying things about theming <a href="http://drupal.org/">Drupal</a> sites is having
to wade through the staggering amounts of wrapping <code>&lt;div&gt;</code> elements and
containers. Some of these are are fairly easy to get rid of. Others
require you to override core templates.</p>

<p>I recently found a clean way to get rid of a couple of those. These two
were introduced in Drupal 7, and you will probably find them on almost
all Drupal 7 sites – they look like this:</p>

<p><img src="http://mikkel.hoegh.org/images/static/2011-10-20-revealit-wrappers.png" width="777" height="125" title="Triple wrapped content" ></p>

<p>Or in markup:</p>

<figure class='code'><figcaption><span>The culprits  </span></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='html'><span class='line'><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">&quot;region region-content&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>  <span class="nt">&lt;div</span> <span class="na">id=</span><span class="s">&quot;block-system-main&quot;</span> <span class="na">class=</span><span class="s">&quot;block block-system&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">&quot;content&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>    <span class="c">&lt;!-- Actual page content here --&gt;</span>
</span><span class='line'>    <span class="nt">&lt;/div&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/div&gt;</span>
</span><span class='line'><span class="nt">&lt;/div&gt;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Now, the last of these wrappers are actually useful, the rest stems from
one of the changes in Drupal 7, namely that the main page content is now
a block, that can be positioned on the page via Drupal’s block system.</p>

<p>Now, that&#8217;s a nice concept, but all the site I&#8217;ve seen do business as
usual, and get around this inconvenience by creating a block region
called “content” and sticking the content-block in there as the only
thing, leaving the region and block wrappers as more DIV-spam in your
site’s markup.</p>

<p>So unless you&#8217;re actually doing something different with the content
block and/or region, you can just get rid of these extra wrappers by
sticking the two following templates in your theme’s template folder:</p>

<figure class='code'><figcaption><span> (region&#8211;content.tpl.php)</span> <a href='http://mikkel.hoegh.org/downloads/code/region--content.tpl.php'>download</a></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="cp">&lt;?php</span>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * @file</span>
</span><span class='line'><span class="sd"> * Render the main content block region.</span>
</span><span class='line'><span class="sd"> *</span>
</span><span class='line'><span class="sd"> * We don&#39;t print all kinds of wrapper divs and titles, just the content.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">print</span> <span class="nv">$content</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<figure class='code'><figcaption><span> (block&#8211;system&#8211;main.tpl.php)</span> <a href='http://mikkel.hoegh.org/downloads/code/block--system--main.tpl.php'>download</a></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="cp">&lt;?php</span>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * @file</span>
</span><span class='line'><span class="sd"> * Render the main content block.</span>
</span><span class='line'><span class="sd"> *</span>
</span><span class='line'><span class="sd"> * We don&#39;t print all kinds of wrapper divs and titles, just the content.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">print</span> <span class="nv">$content</span><span class="p">;</span>
</span></code></pre></td></tr></table></div></figure>


<p>Short and sweet :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Using Database-level Foreign Keys in Drupal 7</title>
    <link href="http://mikkel.hoegh.org/blog/2011/10/06/using-database-level-foreign-keys-in-drupal-7/"/>
    <updated>2011-10-06T18:05:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2011/10/06/using-database-level-foreign-keys-in-drupal-7</id>
    <content type="html"><![CDATA[<p>If you use a <a href="http://www.postgresql.org/">good database system</a>, foreign keys is an
actual concept on the server that is used to enforce data integrity.</p>

<p>With database-level foreign keys, it becomes impossible to break your
data by deleting data referenced by other data, without also dealing
with the referenced data.</p>

<p>In practice this means that deleting a node from the database could be
as simple as <code>DELETE FROM node WHERE nid = 53 CASCADE</code>, and conversely
it would not be allowed to delete the row from the <code>node</code> table, as long
as it is referenced in <code>node_revision</code>, <code>field_something_something</code> and
the umpteen other tables a <a href="http://drupal.org/">Drupal</a> site is likely to have that depends on
the node table.</p>

<p>Currently, it&#8217;s neigh-impossible to delete a node or another entity from
the database without using Drupal&#8217;s API. This is a sticky problem if you
ever need to share the database between systems.</p>

<p>In Drupal 7, the syntax to define foreign keys were introduced to
Drupal’s schema API. The schema API is used to explain the database
structure to Drupal, so it can be understood and utilised by modules
like Views. This understanding is also translated into SQL code, when
tables are created by Drupal’s install scripts.</p>

<p>However, the foreign key syntax introduced in Drupal 7 does not affect
the database structure at all, it is only used inside Drupal for
relating one table to another. Perhaps in time, Drupal will also create
the foreign keys at the database level, but until that happens, you will
need to create them manually. Here&#8217;s how to do it.</p>

<h2>An example</h2>

<p>In the following example, I define two tables, and then use hook_install
and hook_uninstall to set up and dismantle the foreign keys.</p>

<figure class='code'><figcaption><span> (2011-10-05-foreign-key-example.php)</span> <a href='http://mikkel.hoegh.org/downloads/code/2011-10-05-foreign-key-example.php'>download</a></figcaption>
 <div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
</pre></td><td class='code'><pre><code class='php'><span class='line'><span class="cp">&lt;?php</span>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * @file</span>
</span><span class='line'><span class="sd"> * Installation and upgrade code for Zavod supplier.</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Implements hook_schema().</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">zavod_supplier_schema</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>  <span class="nv">$schema</span> <span class="o">=</span> <span class="k">array</span><span class="p">();</span>
</span><span class='line'>
</span><span class='line'>  <span class="nv">$schema</span><span class="p">[</span><span class="s1">&#39;zavod_suppliers&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>    <span class="s1">&#39;description&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;Stock suppliers for Zavod.&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;fields&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>      <span class="s1">&#39;supplier_id&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>        <span class="s1">&#39;description&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;The primary identifier for a supplier.&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;serial&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;unsigned&#39;</span> <span class="o">=&gt;</span> <span class="k">TRUE</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;not null&#39;</span> <span class="o">=&gt;</span> <span class="k">TRUE</span><span class="p">,</span>
</span><span class='line'>      <span class="p">),</span>
</span><span class='line'>      <span class="s1">&#39;title&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>        <span class="s1">&#39;description&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;The title of this supplier, always treated as non-markup plain text.&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;text&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;not null&#39;</span> <span class="o">=&gt;</span> <span class="k">TRUE</span><span class="p">,</span>
</span><span class='line'>      <span class="p">),</span>
</span><span class='line'>    <span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;primary key&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;supplier_id&#39;</span><span class="p">),</span>
</span><span class='line'>  <span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="nv">$schema</span><span class="p">[</span><span class="s1">&#39;zavod_supply_orders&#39;</span><span class="p">]</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>    <span class="s1">&#39;description&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;Supply orders for Zavod.&#39;</span><span class="p">,</span>
</span><span class='line'>    <span class="s1">&#39;fields&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>      <span class="s1">&#39;order_id&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>        <span class="s1">&#39;description&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;The primary identifier for a supply order.&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;serial&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;unsigned&#39;</span> <span class="o">=&gt;</span> <span class="k">TRUE</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;not null&#39;</span> <span class="o">=&gt;</span> <span class="k">TRUE</span><span class="p">,</span>
</span><span class='line'>      <span class="p">),</span>
</span><span class='line'>      <span class="s1">&#39;supplier_id&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>        <span class="s1">&#39;description&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;{zavod_suppliers}.supplier_id of the supplier that the order is made to.&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;int&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;not null&#39;</span> <span class="o">=&gt;</span> <span class="k">TRUE</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;unsigned&#39;</span> <span class="o">=&gt;</span> <span class="k">TRUE</span><span class="p">,</span>
</span><span class='line'>      <span class="p">),</span>
</span><span class='line'>      <span class="s1">&#39;title&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>        <span class="s1">&#39;description&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;The title of this order, always treated as non-markup plain text.&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;type&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;text&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;not null&#39;</span> <span class="o">=&gt;</span> <span class="k">TRUE</span><span class="p">,</span>
</span><span class='line'>      <span class="p">),</span>
</span><span class='line'>    <span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;foreign keys&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>      <span class="s1">&#39;zavod_suppliers&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span>
</span><span class='line'>        <span class="s1">&#39;table&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;zavod_suppliers&#39;</span><span class="p">,</span>
</span><span class='line'>        <span class="s1">&#39;columns&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;supplier_id&#39;</span> <span class="o">=&gt;</span> <span class="s1">&#39;supplier_id&#39;</span><span class="p">),</span>
</span><span class='line'>      <span class="p">),</span>
</span><span class='line'>    <span class="p">),</span>
</span><span class='line'>    <span class="s1">&#39;primary key&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span><span class="p">(</span><span class="s1">&#39;order_id&#39;</span><span class="p">),</span>
</span><span class='line'>  <span class="p">);</span>
</span><span class='line'>
</span><span class='line'>  <span class="k">return</span> <span class="nv">$schema</span><span class="p">;</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Implements hook_install().</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">zavod_supplier_install</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// Make real foreign keys.</span>
</span><span class='line'>  <span class="nx">db_query</span><span class="p">(</span><span class="s1">&#39;</span>
</span><span class='line'><span class="s1">    ALTER TABLE {zavod_supply_orders}</span>
</span><span class='line'><span class="s1">    ADD CONSTRAINT {zavod_suppliers}</span>
</span><span class='line'><span class="s1">    FOREIGN KEY (supplier_id) REFERENCES {zavod_suppliers} (supplier_id)</span>
</span><span class='line'><span class="s1">  &#39;</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="sd">/**</span>
</span><span class='line'><span class="sd"> * Implements hook_uninstall().</span>
</span><span class='line'><span class="sd"> */</span>
</span><span class='line'><span class="k">function</span> <span class="nf">zavod_supplier_uninstall</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'>  <span class="c1">// Make real foreign keys.</span>
</span><span class='line'>  <span class="nx">db_query</span><span class="p">(</span><span class="s1">&#39;</span>
</span><span class='line'><span class="s1">    ALTER TABLE {zavod_supply_orders}</span>
</span><span class='line'><span class="s1">    DROP CONSTRAINT IF EXISTS {zavod_suppliers}</span>
</span><span class='line'><span class="s1">  &#39;</span><span class="p">);</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>It&#8217;s pretty self-explanatory if you&#8217;re used to Drupal&#8217;s schema API. If
you&#8217;re not, you should definitely learn.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">How to install multicore Apache Solr on FreeBSD with Jetty</title>
    <link href="http://mikkel.hoegh.org/blog/2011/07/24/install-multicore-apache-solr-freebsd-jetty/"/>
    <updated>2011-07-24T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2011/07/24/install-multicore-apache-solr-freebsd-jetty</id>
    <content type="html"><![CDATA[<p>If you use <a href="http://lucene.apache.org/solr/">Apache Solr</a> with your <a href="http://drupal.org/">Drupal</a> site, you have probably
come across the need to have more than one Solr instance. You may have
multiple sites, or just multiple copies of the same site, production and
staging perhaps?</p>

<p>There are two widely published ways to accomplish that. One is to set up
completely separate Solr instances with whatever Java-server you’re
using. That is somewhat inefficient, and I was unable to get such a
setup working properly anyways. So here’s the alternative, “<a href="http://wiki.apache.org/solr/CoreAdmin">multi core</a>”.</p>

<p>The benefit of using multi core is that you avoid most of the
configuration overhead associated with figuring out how to get multiple
<code>WebAppContainerDeploymentContextFactoryGeneratorWidgetClass</code> instances
(or whatever they’re named in your brand of Java-server) to coexist by
using the same WAR-file but not the same configuration. I spent a lot of
hours trying to accomplish just that.</p>

<p>Instead, all the interesting stuff happens in the Solr configuration,
which is a lot less confusing for a Java novice like me.</p>

<h2>Instructions</h2>

<ol>
<li><p>Install a Java JRE.</p>

<p>You may want to see <a href="http://serverfault.com/questions/1077/how-to-install-java-jre-on-freebsd-with-as-few-dependencies-as-possible">my explanation on how to do this</a>.</p></li>
<li><p>Install <a href="http://jetty.codehaus.org/jetty/">Jetty</a> and Solr.</p>

<p>If you use <code>portmaster</code>, this can be as simple as running <code>portmaster www/jetty textproc/apache-solr</code></p></li>
<li><p>Create a folder for your Solr multi core instance’s configuration and
data files. This could be anywhere, but in this example, I&#8217;m going to
use <code>/srv/solr</code>.</p></li>
<li><p>Create a <code>/srv/solr/solr.xml</code> file for the configuration, setting up
the different cores into folders.</p>

<p>Mine looks like this:</p>

<p><figure class='code'><figcaption><span> (solr.xml)</span> <a href='http://mikkel.hoegh.org/downloads/code/solr.xml'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;</span>
</span><span class='line'><span class="c">&lt;!--</span>
</span><span class='line'><span class="c">All (relative) paths are relative to the installation path</span>
</span><span class='line'><span class="c">  </span>
</span><span class='line'><span class="c">  persistent: Save changes made via the API to this file</span>
</span><span class='line'><span class="c">  sharedLib: path to a lib directory that will be shared across all cores</span>
</span><span class='line'><span class="c">--&gt;</span>
</span><span class='line'><span class="nt">&lt;solr</span> <span class="na">persistent=</span><span class="s">&quot;false&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>
</span><span class='line'>  <span class="c">&lt;!--</span>
</span><span class='line'><span class="c">  adminPath: RequestHandler path to manage cores.  </span>
</span><span class='line'><span class="c">    If &#39;null&#39; (or absent), cores will not be manageable via request handler</span>
</span><span class='line'><span class="c">  --&gt;</span>
</span><span class='line'>  <span class="nt">&lt;cores</span> <span class="na">adminPath=</span><span class="s">&quot;/admin/cores&quot;</span> <span class="na">sharedLib=</span><span class="s">&quot;lib&quot;</span><span class="nt">&gt;</span>
</span><span class='line'>    <span class="nt">&lt;core</span> <span class="na">name=</span><span class="s">&quot;dev&quot;</span> <span class="na">instanceDir=</span><span class="s">&quot;dev&quot;</span> <span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;core</span> <span class="na">name=</span><span class="s">&quot;prod&quot;</span> <span class="na">instanceDir=</span><span class="s">&quot;prod&quot;</span> <span class="nt">/&gt;</span>
</span><span class='line'>    <span class="nt">&lt;core</span> <span class="na">name=</span><span class="s">&quot;stg&quot;</span> <span class="na">instanceDir=</span><span class="s">&quot;stg&quot;</span> <span class="nt">/&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/cores&gt;</span>
</span><span class='line'><span class="nt">&lt;/solr&gt;</span>
</span></code></pre></td></tr></table></div></figure></p></li>
<li><p>Create all the folders referenced in the config file.</p>

<p>We specified four folders, so lets create them via a simple
<code>mkdir dev lib prod stg</code> (while standing in the <code>/srv/solr</code> folder).</p></li>
<li><p>Make these folders owned by the user that will run our Solr
instances. In this example, I’ll use the <code>www</code> account, but it would
be more secure to set up a separate account for running Solr if you
have other web servers running on the same machine.</p>

<p><code>chown www:www dev lib prod stg</code></p></li>
<li><p>For each core you want to set up, copy or symlink the schema and
other configuration files you need into the conf folder in each core
folder. In this example, I&#8217;m copying the example configuration from
<code>/usr/local/share/examples/apache-solr/solr/conf/</code>. If you&#8217;re working
with Drupal, be sure to copy the solr configuration it ships with
into each core.</p>

<p><code>mkdir prod/conf</code><br/>
<code>cd prod/conf</code><br/>
<code>cp -r /usr/local/share/examples/apache-solr/solr/conf/* ./</code><br/>
<code>cd ../..</code>
<code>cp -r prod/conf dev/</code>
<code>cp -r prod/conf stg/</code></p></li>
<li><p>Enable Jetty</p>

<p>In this example, we’re going to use <a href="http://jetty.codehaus.org/jetty/">Jetty</a> to run the Solr
service. I am not well versed in the Java lingo for this, but Jetty
is a servlet container, so I guess that means Solr is being run as a
servlet. I also tried this with Tomcat, but that was a lot harder to
configure properly.</p>

<p>Add <code>jetty_enable="YES"</code> on a new line in <code>/etc/rc.conf</code>.</p></li>
<li><p>Copy <code>/usr/local/jetty/etc/jetty.xml</code> to <code>/usr/local/etc</code>.</p></li>
<li><p>Symlink <code>solr.war</code> into <code>/usr/local/jetty/webapps</code></p></li>
</ol>


<p>   <code>cd /usr/local/jetty/webapps</code><br/>
   <code>ln -s /usr/local/share/java/classes/apache-solr-3.2.0.war solr.war</code></p>

<ol>
<li>Symlink <code>/srv/solr</code> into <code>/usr/local/jetty</code></li>
</ol>


<p>   <code>cd /usr/local/jetty</code>
   <code>ln -s /srv/solr</code></p>

<ol>
<li>Start Jetty by running <code>service jetty start</code></li>
</ol>


<p>Hopefully, after all this work, Solr should be ready once its done
booting up.</p>

<p>You can check that it&#8217;s working by running
<code>curl -iL localhost:8080/solr/prod/admin/</code>. This should output the HTML
for the admin interface.</p>

<p>If you have problems, try running <code>tail -f /usr/local/jetty/jetty.log</code>
in one terminal, and then <code>service jetty restart</code> in another, and look
what goes on as Jetty restart (there&#8217;ll be a lot of messages flying by,
but the error should be in there somewhere).</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Run VirtualBox virtual machines on boot in Mac OS X</title>
    <link href="http://mikkel.hoegh.org/blog/2010/12/23/run-virtualbox-boot-mac-os-x/"/>
    <updated>2010-12-23T00:00:00+01:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/12/23/run-virtualbox-boot-mac-os-x</id>
    <content type="html"><![CDATA[<p>To celebrate the launch of <a href="http://www.virtualbox.org/">VirtualBox</a> 4.0, I’d like to share a
simple trick for making your virtual machines start automatically when
your computer boots.</p>

<p>I have a <a href="http://www.apple.com/macmini/server/">Mac mini server</a> that has a couple of virtual machines running.
Until recently, I used <a href="http://www.vmware.com/products/fusion/">VMWare Fusion</a> with a wonky setup with a user that
autologins and has VMWare Fusion as a login item, but that has proven to
be cumbersome and error-prone, and since both of those virtual machines
are servers, there’s really no reason they should be run graphically.</p>

<p>Thus, I switched to VirtualBox and set it up to run automatically on
boot. In this example, <code>revealit.dk</code> is my company, <code>virtual</code> is the
user account on the server that owns/runs the virtual machines and
Chestnut is the name of the virtual machine in question.<br/>
The virtual server config resides in <code>/Users/virtual/VirtualBox VMs/chestnut</code></p>

<ol>
<li>Set up your virtual machine as you please using the VirtualBox
Manager GUI app (or whatever tool you prefer).</li>
<li>Shut down the virtual machine (ie. close the window with its screen
content). To be completely safe, turn it off completely.</li>
<li><p>Create and edit <code>/Library/LaunchDaemons/dk.revealit.ChestnutVirtualbox.plist</code>
setting it up something like this:</p>

<p><figure class='code'><figcaption><span> (dk.revealit.ChestnutVirtualbox.plist)</span> <a href='http://mikkel.hoegh.org/downloads/code/dk.revealit.ChestnutVirtualbox.plist'>download</a></figcaption>
<div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class='xml'><span class='line'><span class="cp">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span>
</span><span class='line'><span class="cp">&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;</span>
</span><span class='line'><span class="nt">&lt;plist</span> <span class="na">version=</span><span class="s">&quot;1.0&quot;</span><span class="nt">&gt;</span>
</span><span class='line'><span class="nt">&lt;dict&gt;</span>
</span><span class='line'>  <span class="nt">&lt;key&gt;</span>Label<span class="nt">&lt;/key&gt;</span>
</span><span class='line'>  <span class="nt">&lt;string&gt;</span>dk.revealit.ChestnutVirtualbox<span class="nt">&lt;/string&gt;</span>
</span><span class='line'>  <span class="nt">&lt;key&gt;</span>ProgramArguments<span class="nt">&lt;/key&gt;</span>
</span><span class='line'>  <span class="nt">&lt;array&gt;</span>
</span><span class='line'>    <span class="nt">&lt;string&gt;</span>/usr/bin/VBoxHeadless<span class="nt">&lt;/string&gt;</span>
</span><span class='line'>    <span class="nt">&lt;string&gt;</span>-s<span class="nt">&lt;/string&gt;</span>
</span><span class='line'>    <span class="nt">&lt;string&gt;</span>chestnut<span class="nt">&lt;/string&gt;</span>
</span><span class='line'>  <span class="nt">&lt;/array&gt;</span>
</span><span class='line'>  <span class="nt">&lt;key&gt;</span>UserName<span class="nt">&lt;/key&gt;</span>
</span><span class='line'>  <span class="nt">&lt;string&gt;</span>virtual<span class="nt">&lt;/string&gt;</span>
</span><span class='line'>  <span class="nt">&lt;key&gt;</span>WorkingDirectory<span class="nt">&lt;/key&gt;</span>
</span><span class='line'>  <span class="nt">&lt;string&gt;</span>/Users/virtual<span class="nt">&lt;/string&gt;</span>
</span><span class='line'>  <span class="nt">&lt;key&gt;</span>RunAtLoad<span class="nt">&lt;/key&gt;</span>
</span><span class='line'>  <span class="nt">&lt;true/&gt;</span>
</span><span class='line'><span class="nt">&lt;/dict&gt;</span>
</span><span class='line'><span class="nt">&lt;/plist&gt;</span>
</span></code></pre></td></tr></table></div></figure></p></li>
<li><p>Edit the details to match your system. Make sure that the label
matches the filename.</p></li>
<li>Run <code>sudo launchctl load -w /Library/LaunchDaemons/dk.revealit.ChestnutVirtualbox.plist</code>
to load the launchd service. This will cause launchd to keep the
virtual machine running indefinitely.</li>
</ol>


<p>And that’s it. Be aware that you must now use
<code>launchctl stop dk.revealit.ChestnutVirtualbox</code> should you want to stop
the virtual machine. Stopping it in any other way will just cause
launchd to restart it.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">del.icio.us – can the Drupal community do better?</title>
    <link href="http://mikkel.hoegh.org/blog/2010/12/20/delicious-drupal-community-better/"/>
    <updated>2010-12-20T00:00:00+01:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/12/20/delicious-drupal-community-better</id>
    <content type="html"><![CDATA[<p>As you may know, Yahoo! is in trouble, and has decided to <a href="http://blog.delicious.com/blog/2010/12/whats-next-for-delicious.html">jettison the
social bookmarking service del.icio.us</a> (Delicious).</p>

<p>I am not a big delicious user anymore (actually, I deleted my account
when Microsoft was trying to purchase Yahoo!), but this recent closing
made me wonder if the Drupal community couldn’t do better…</p>

<p>One of the great strengths of Drupal is self-hosting, and a bookmarking
service is not a complicated thing.</p>

<p>Thus, I am planning to put in a bit of effort into having my own
bookmarks section here on this very site. A bookmark content type should
not take long to configure with the <a href="http://drupal.org/project/links">Links</a> module, <a href="http://drupal.org/project/auto_nodetitle">Automatic Nodetitles</a>
to make sure the nodes have the same title as the link
itself, and a bit of <a href="http://drupal.org/project/views">Views</a> work should make it presentable.</p>

<h4>Workflow</h4>

<p>The main issue here is workflow. Nowadays, I use Google Reader’s sharing
interface for “bookmarking” links, since it has a nice interface for
tagging, putting notes on there, etc.</p>

<p>So what’s really missing in my workflow is some way of getting that data
into Drupal, and since Google provides a <a href="https://www.google.com/reader/public/atom/user%2F15265426901976190807%2Fstate%2Fcom.google%2Fbroadcast">very tasty atom feed with lots
of metadata</a> for all my shared entries, it should be doable with
a bit of add-on code for the <a href="http://drupal.org/project/feeds">Feeds</a> module.</p>

<h4>What to do?</h4>

<p>The main thing I would like to know here is if other Drupallers would be
interestered in such a thing, and if anyone has input as to how it best
could be built? I suppose a <a href="http://drupal.org/project/features">Features</a> module could be in order, but I
have not seen a whole lot of those published on Drupal.org. Is it wise?</p>

<p>And where to put my Google Reader for Feeds module integration? I
suppose I could make a separate project for that on d.o, but that seems
a bit overkill-like. Any takers?</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">35% response time improvement from switching to uWSGI/nginx</title>
    <link href="http://mikkel.hoegh.org/blog/2010/10/22/response-time-improvement-switching-uwsgi-nginx/"/>
    <updated>2010-10-22T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/10/22/response-time-improvement-switching-uwsgi-nginx</id>
    <content type="html"><![CDATA[<p>As part of refreshing <a href="http://revealit.dk/">the Reveal IT website</a>, I have moved
it from <a href="http://code.google.com/p/modwsgi/">mod_wsgi</a> running on <a href="http://httpd.apache.org/">Apache HTTPD</a> to <a href="http://projects.unbit.it/uwsgi/">uWSGI</a>
running on <a href="http://wiki.nginx.org/">nginx</a>, mainly because my previous setup had both <a href="http://www.djangoproject.com/">Django</a> and
<a href="http://drupal.org/">Drupal</a> sites running on the same Apache server.</p>

<p>Due to some of the shortcomings of <a href="http://php.net/">PHP</a>, the only recommended way to
run <code>mod_php</code> on Apache is via the prefork MPM, which carries a high
memory usage penalty per process. My Apache processes hover around 100MB
of RAM each after serving a few requests. Thus, it is a bit wasteful to
use those fat PHP-enabled processes for serving Django requests.</p>

<p>Out of curiosity, I decided to move the site to nginx before the
upgrade, just to see how that would affect performance. I expected a
modest improvement, but in my case, it yielded a ~35% boost in page
loading times – here’s the chart from Pingdom:</p>

<p><img src="http://mikkel.hoegh.org/images/static/2010-10-22-pingdom-response-time.png" width="617" height="274" title="Page loading time statistics" ></p>

<p>I am not entirely certain what goes on here, but not only is the
uWSGI/nginx combo providing better RAM utilisation, but it is also
providing much better response times.</p>

<p>Take note, this is the same hardware, the same OS, the same database,
the same memcache instance.<br/>
Only thing thats changed in the period of the graphs is the replacement
of Apache. I have upgraded the website yesterday, but that did not
change the picture much. If anything, it increased the load time ever so
slightly.</p>

<p>In fact, the picture becomes even wilder if I use Pingdoms filters to
get load times from their Europe (where my site is hosted):</p>

<p><img src="http://mikkel.hoegh.org/images/static/2010-10-22-pingdom-response-time-europe.png" width="618" height="277" title="Page loading time statistics, Europe only" ></p>

<p>The average load time is essentially cut in half, from around 500 ms to
250 ms.</p>

<p>Спасибо большое, nginx.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Postbox botches upgrade policy, censors customer complaints</title>
    <link href="http://mikkel.hoegh.org/blog/2010/10/08/postbox-upgrade-policy-censor-customer-complaints/"/>
    <updated>2010-10-08T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/10/08/postbox-upgrade-policy-censor-customer-complaints</id>
    <content type="html"><![CDATA[<p><a href="http://www.postbox-inc.com/">Postbox</a> is a commercial e-mail client, based on <a href="http://www.mozillamessaging.com/thunderbird/">Mozilla Thunderbird</a>. Is is mostly a layer of polish and OS integration on top of a popular open source project.</p>

<p>Postbox 2.0 was <a href="http://postbox-inc.com/?/blog/entry/announcing_postbox_2/">recently released</a>, and the company behind it, Postbox Inc., decided to offer a very short upgrade window for customers that recently purchased a license, just 31 days. The 2.0 version had been announced much earlier, and many, myself included decided o purchase a license on the promise of the features coming in 2.0.</p>

<p>Additionally, Postbox participated in the <a href="http://www.mupromo.com/">MacUpdate Promo</a> just two weeks before the upgrade cutoff. That is, in my eyes, downright devious. Offer a discount, and then make a paid upgrade a few weeks later to get the full payment from all the people who fell for your ruse.</p>

<p>As I was one of those, I was a bit surprised, and decided to post a <a href="http://support.postbox-inc.com/entries/281146">question about it on their forums</a>.</p>

<p>As it turns out, I was not the only one disappointed by this, and a bunch of other customers chimed in with their comments.</p>

<p>And this is where Postbox, Inc. employee Sherman Dickman steps in, reiterates their policy, gives the usual “<em>we can’t give it all away for free</em>”-statement and finally states that several comments were deleted for “<em>violation of our posting guidelines</em>”</p>

<p>I have preserved those comments here, and as you can see, it was all just paying customers expressing their disappointment:</p>

<h4>Exhibit #1</h4>

<blockquote><p><strong>Joshua Miller said</strong>:
Yeah, that&#8217;s pretty lame - especially since I participated in the Postbox BETA just after buying a 1.1 license. I bought this in July and now, less than 3 months later, have to spend another $20 on the application. If I&#8217;d bought it a year ago I could see maybe paying full price, but less than 3 months? Come on, that&#8217;s just absurd.</p></blockquote>

<h4>Exhibit #2</h4>

<blockquote><p><strong>Evelyn Creelman said</strong>:
i too automatically updated - to 2.0- thinking this what what i had actually paid for- i paoid for this project because i felt it a viable alternative to mac mail-something ive been looking for forever- the fact that it was not free was fine- i didnt mind paying - but i am trully blown away by this policy -</p>

<p>When i purchased postbox- i believe i  should have been  clearly informed before purchase that they would be releasing an upgrade in three weeks-</p>

<p>that i would have to pay for- therefore giving me the choice to wait  and , essentially, not feel as though i paid to beta test their software for three weeks.</p>

<p>what a shame.</p>

<p>i so perturbed about this - really disappointing - as it seems very shady - especially when a response form postbox basically said- egged me on over</p></blockquote>

<h4>Exhibit #3</h4>

<blockquote><p><strong>Evelyn Creelman said</strong>:
sorry the last post got cut off- i was saying i received an email from postbox support over this issue-basically saying i paid the price of a pizza for their software- and basically seemed to be very condescending- regarding my perturbed-ness..over being asked to pay again three weeks later-</p></blockquote>

<h4>Exhibit #4</h4>

<blockquote><p><strong>Evelyn Creelman said</strong>:
and to just to back myself up ..( or beat a dead horse)  found this comment from postboxs&#8217; Sherman Dickman, that contradicts my experience:( found  macupdate.com/users/Sherman_Dickman )</p>

<p>&#8220;we provided one with as much transparency as possible so people can make informed choices.&#8221;</p>

<p>nope, you actually didnt.</p></blockquote>

<h4>Parting words</h4>

<p>Summing up, Postbox has now managed to make a number of customers feel exploited by their attempt to nickle-and-dime us, several of these customers active beta testers. They have demonstrated their contempt for customer opinions and open debate. All while riding the coattails of a popular open source project.</p>

<p>I hope that they will improve their behaviour, because otherwise I think their otherwise good and useful product will be ridden to ruin by a greedy and short-sighted business policy.</p>

<h4>Reply from Postbox</h4>

<p>I got this e-mail from Sherman Dickman, co-founder of Postbox:</p>

<blockquote><p>Mikkel,</p>

<p>We&#8217;re fine with criticism of our product or our policies, and anyone is free disagree with them and/or challenge thme.  But we draw the line when the integrity of our company is called into question, particularly when it is done so without all the facts.</p>

<p>We also draw the line on forum abuse, and as such, we&#8217;ve disabled your forum account.</p>

<p>We&#8217;re issuing you a refund via PayPal at this email address, and we wish you the best of luck in finding a suitable alternative.</p>

<p>Sherman Dickman
Postbox, Inc.l</p></blockquote>

<p>To which I have replied:</p>

<blockquote><p>Hi Sherman,</p>

<p>I am sorry that it turned out this way. I think this would have been an excellent opportunity for you to eat some humble pie and perhaps reconsider your policies.</p>

<p>You should have known that you would get unfavourable reactions when you decided to make a paid upgrade a mere six weeks after the MUPromo.</p>

<p>And when the criticism came, you decided to be condescending to some of your most loyal users.</p>

<p>It is your company to run, but I think you would be much better off trying to build a good community for Postbox instead of trying to monetise existing users.</p>

<p>I still enjoy Postbox as a product, and as such, I intend to spend the refund you sent me on an upgrade to 2.0.</p>

<p>So please, keep building awesome software, and loose up on the policies. None of the now eight comments you have deleted on that thread could have been as damaging to your reputation and customer relationship as deleting them have.</p>

<p>&#8211;
Kind regards,</p>

<p>Mikkel Høgh <a href="&#109;&#x61;&#x69;&#108;&#x74;&#x6f;&#x3a;&#x6d;&#105;&#x6b;&#107;&#x65;&#x6c;&#x40;&#x68;&#111;&#101;&#x67;&#x68;&#x2e;&#x6f;&#x72;&#x67;">&#x6d;&#105;&#107;&#107;&#x65;&#108;&#x40;&#x68;&#111;&#101;&#103;&#x68;&#x2e;&#111;&#114;&#103;</a></p></blockquote>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Relauching my blog on Drupal 7</title>
    <link href="http://mikkel.hoegh.org/blog/2010/10/07/relaunching-blog-drupal-7/"/>
    <updated>2010-10-07T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/10/07/relaunching-blog-drupal-7</id>
    <content type="html"><![CDATA[<p>More than a year ago, <a href="http://mikkel.hoegh.org/blog/2009/08/27/going-edge-drupal-7">I was agitating for a move to Drupal 7</a>
for all the blogging developers. As is rather obvious now, Drupal 7 was
not in a state then for public websites. There was outstanding security
issues, no upgrade path, lots of API changes to be made, etc.</p>

<p>However, since that has all been resolved now, I figured it was about
time I moved my blog over. I have obviously been preparing for this for
some time, as I will get back to in later blog posts.</p>

<p>However, I would once again like to encourage fellow developers to
experiment with Drupal 7. The process of building a real, public-facing
website with Drupal 7 so soon in the process have taught me a lot of
Drupals innards, and helped me uncover a fair number of bugs in Drupal
core and contributed modules that have since been fixed.</p>

<p>As you can also see in the <a href="http://drupal.org/drupal-7.0-beta1">7.0 beta 1 accouncement</a>, there is a
lot to be exited about, and as we have entered the beta-phase, upgrades
will be supported from now on, so the site you create with beta 1 should
be upgradable all the way to the final release.</p>

<p>So if you can spare the time to fiddle with it, I highly recommend it.
It is an educative challenge – enjoy :)</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Protecting your users from phishing with Apache rules and HSTS</title>
    <link href="http://mikkel.hoegh.org/blog/2010/09/09/protecting-your-users-phishing-apache-rules-hsts/"/>
    <updated>2010-09-09T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/09/09/protecting-your-users-phishing-apache-rules-hsts</id>
    <content type="html"><![CDATA[<p>HTTP Strict Transport Security or <a href="http://en.wikipedia.org/wiki/Strict_Transport_Security">HSTS</a> is a new security feature in
browsers that enables you tell the browser “<em>always use SSL when
accessing this site</em>”.</p>

<p>Mozilla has a <a href="http://hacks.mozilla.org/2010/08/firefox-4-http-strict-transport-security-force-https/">good blog post explaining HSTS</a>, so I won’t try
to replicate that here, but I’d just like to make it clear that if you
have a site that should always use SSL, be it <a href="http://drupal.org/">Drupal</a> or <a href="http://www.djangoproject.com/">Django</a>
or any other system, this is definitely something you should get set up.</p>

<p>Good examples of these are webmail, server administration and monitoring
tools and general admin backends. If you are running a large
Drupal-site, you should perhaps consider restricting admin-access to a
SSL-protected subdomain.</p>

<p>Currently, it is only supported in Chrome 4 and above, and Firefox 4
beta 5 and beyond, but hopefully the other browser makers will catch up
soon. Its fully backwards compatible, in that it will have no effect if
the browser does not support HSTS.</p>

<h4>How to use it</h4>

<p>Setting it up is very simple. In your <a href="http://httpd.apache.org/">Apache</a> VHost, where you do your
SSL config, just add this line:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Header add Strict-Transport-Security "max-age=15768000"</span></code></pre></td></tr></table></div></figure>


<p>This will tell the browser to remember that this site is SSL/HTTPS only
for the next 6 months. During that time it will simply rewrite any and
all requests to that site to use HTTPS instead of HTTP without ever
communicating insecurely with the server.</p>

<p>If you use <a href="http://wiki.nginx.org/">nginx</a>, the syntax is subtly different. Adding this to
the server section does the trick:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>add_header Strict-Transport-Security max-age=15768000;</span></code></pre></td></tr></table></div></figure>


<h4>Keep your redirects</h4>

<p>An important point is that HSTS only works <em>after</em> the user has received
the header via HTTPS. So you will still need to have a redirect from
your HTTP-site to HTTPS, also for supporting browsers that still do not
understand HSTS.</p>

<p>This is easily accomplished using Apache’s <code>mod_rewrite</code>:</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>&lt;IfModule mod_rewrite.c&gt;
</span><span class='line'>  RewriteEngine On
</span><span class='line'>  RewriteCond %{HTTPS} off
</span><span class='line'>  RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}
</span><span class='line'>&lt;/IfModule&gt;</span></code></pre></td></tr></table></div></figure>


<p>Thus, with a few lines of configuration, you can make the web a safer place
to be for your users. So, what are you waiting for?</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">A tip for using PostgreSQL with Drupal 6</title>
    <link href="http://mikkel.hoegh.org/blog/2010/08/09/tip-using-postgresql-drupal-6/"/>
    <updated>2010-08-09T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/08/09/tip-using-postgresql-drupal-6</id>
    <content type="html"><![CDATA[<p>If you are using <a href="http://www.postgresql.org/">PostgreSQL</a> for hosting
your <a href="http://drupal.org/">Drupal</a> sites, you might have noticed a lot of
warnings in your logs like these:</p>

<pre><code>Aug  8 18:41:05 s002 postgres[90076]: [5-1] WARNING:  nonstandard use of \\ in a string literal at character 32
Aug  8 18:41:05 s002 postgres[90076]: [5-2] HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
Aug  8 18:41:05 s002 postgres[90076]: [6-1] WARNING:  nonstandard use of \\ in a string literal at character 122
Aug  8 18:41:05 s002 postgres[90076]: [6-2] HINT:  Use the escape string syntax for backslashes, e.g., E'\\'.
</code></pre>

<p>The immediate cause for this is
<a href="http://drupal.org/node/426008">bug #426008 in Drupal core</a>, but the
issue stems from the fact that PostgreSQL does not conform exactly to
the SQL standard with regards to backslashes in strings. The reasoning
behind this and why its going away (as a default setting) in PostgreSQL
9.1 can be read in <a href="http://momjian.us/main/blogs/pgblog/2010.html#August_9_2010">this excellent blog post by Bruce Momjian</a>.</p>

<h4>But how do I fix it?</h4>

<p>The good news is that this behavior is configurable. You can set
<code>standard_conforming_strings = on</code> in your <code>postgresql.conf</code> and be done
with it. This will be the default setting from PostgreSQL 9.1, and
hopefully the other applications using your database do not depend on
the legacy behavior (if they do, they need fixing).</p>

<p>If that’s not suitable for your setup, there is a few other suggestions
<a href="http://dbaspot.com/forums/postgresql/251026-many-warning-pg-logs-nonstandard-use-string-literalat-character.html#post1004812">in this forum thread</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Presenting Django Password Required</title>
    <link href="http://mikkel.hoegh.org/blog/2010/06/07/presenting-django-password-required/"/>
    <updated>2010-06-07T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/06/07/presenting-django-password-required</id>
    <content type="html"><![CDATA[<p>Have you ever wanted to password-protect your <a href="http://www.djangoproject.com/">Django</a>-site, without
requiring user registration, do you find HTTP Basic Auth to be a very
blunt instrument for protecting sites or do you want to do
<a href="http://stackoverflow.com/">StackOverflow</a> style beta-testing?</p>

<p>Then <a href="http://github.com/mikl/django-password-required">Django Password Required</a> is for you. It provides a simple
<code>@password_required</code> decorator for your views, and lets you configure a
password in your settings.py file. The authentication is stored in the
user’s session data, using Django’s own session system. This means that
<a href="http://github.com/mikl/django-password-required">Django Password Required</a> can co-exist with <code>django.contrib.auth</code>,
so you can allow users to log in after they’ve provided the password to
access the site.</p>

<p>I use it for a little skunkworks project that does not have user logins
per se, but since it is not open to the public yet, I need to protect
it, at least from webspiders and random visitors. I don’t mind if the
password is spread by word-of-mouth, since the site contains nothing
sensitive or private.</p>

<p>Initially I used HTTP Basic Auth, but setting that up with Apache is an
all-or-nothing deal, requires you to enter the password quite often on
iPhone/iPad, and interferes with AJAX requests/API calls.
So I created this lightweight app, so as to require a password, store
that the user is logged in via a cookie bound to a server-side session,
with a long lifetime so you won’t get nagged for the password very often.</p>

<p>Bug reports/suggestions, documentation, source code, etc.
<a href="http://github.com/mikl/django-password-required">It all happens on Github</a>. Enjoy.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Introducing Herd Fire</title>
    <link href="http://mikkel.hoegh.org/blog/2010/01/21/introducing-herdfire/"/>
    <updated>2010-01-21T00:00:00+01:00</updated>
    <id>http://mikkel.hoegh.org/blog/2010/01/21/introducing-herdfire</id>
    <content type="html"><![CDATA[<p>If you, like me, are an avid Firefox user, you will likely have felt the
burden of using the same Firefox profile for a variety of tasks. Having
NoScript or ImgLikeOpera installed is handy when surfing, but just
annoying when working on developing websites. Having FireBug installed
will slow down JavaScript execution on all pages, unless you disable
some of its features, regardless of whether you&#8217;re using it or not.
Every extension you install slows down Firefox ever so slightly.</p>

<p>And that just extensions. Same goes for many aspects of Firefox
configuration, language, about:config, etc. Would it not be better to
have several Firefox profiles, one for each task? If you ask me, it
would.</p>

<p>One problem though – even if you find the hidden Firefox profile
manager, Firefox will not let you launch multiple instances of it
without a bit of coercion. Previously, I resorted to all kinds of
commandline trickery to manage my profiles until I found a script
somewhere on the web (I&#8217;ve been unable to find it again. If you know it,
please post a comment – I&#8217;d like to give proper attribution) that helped
me set up copies of Firefox.app for each profile, but it had its limits.
It did not work for Firefox.app itself, only for its named copies. It
also renamed the Firefox binary, causing trouble for other scripts. So
I&#8217;ve rewritten it in Python, improving a few key things:</p>

<ol>
<li>It modifies <code>Info.plist</code> to use a launching script instead of renaming <code>firefox-bin</code>.</li>
<li>It sets the normal Firefox.app to use the profile named “default”.</li>
</ol>


<h4>Instructions for use</h4>

<ol>
<li><a href="http://github.com/mikl/herdfire/tarball/master">Download Herd Fire</a>.</li>
<li>Copy your Firefox.app to create a named copy (I&#8217;m using the name “<em>example</em>” here):<br/>
<img src="http://reveal.dk/misc/2010-01-21-1-firefox-copy.png" alt="Copying Firefox.app" /></li>
<li>Run Herd Fire ( run it from the folder its located in, or stick it in a folder on your path):<br/>
<img src="http://reveal.dk/misc/2010-01-21-2-run-herdfire.png" alt="Running herdfire" /></li>
<li>Launch your new Firefox copy.<br/>
<img src="http://reveal.dk/misc/2010-01-21-3-launch-firefox.png" alt="Launching Firefox" /></li>
<li>If there is not a Firefox profile with the extra name you gave your
Firefox.app copy (in this case “example”), the profile manager will appear.
In that case, use it to create a new profile with the correct name.<br/>
<img src="http://reveal.dk/misc/2010-01-21-4-profile-manager.png" alt="Profile manager" /><br/>
<img src="http://reveal.dk/misc/2010-01-21-5-choose-name.png" alt="Pick a name" /></li>
<li>Firefox-example.app will now always start with the “example” profile
activated. Firefox auto updater might break this. In that case, all
you need to do is to run <code>herdfile</code> again.<br/>
<img src="http://reveal.dk/misc/2010-01-21-6-example-firefox.png" alt="New Firefox profile" /></li>
</ol>


<p>The code is in a <a href="http://github.com/mikl/herdfire">GitHub repository</a>,
so please don&#8217;t hesitate to fork, file bugs, etc.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Attention all Drupal Git-mirror users</title>
    <link href="http://mikkel.hoegh.org/blog/2009/11/09/attention-all-drupal-git-mirror-users/"/>
    <updated>2009-11-09T00:00:00+01:00</updated>
    <id>http://mikkel.hoegh.org/blog/2009/11/09/attention-all-drupal-git-mirror-users</id>
    <content type="html"><![CDATA[<p>A long-standing issue with the Git mirrors of Drupal&#8217;s CVS has been
fixed thanks to <a href="http://drupal.org/user/22211">Damien Tournoud</a>.</p>

<p>The problem is that CVS outputs dates in RCS tags in the somewhat
nonstandard format <em>2009/10/19</em> (<a href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>
specifies dashes, not slashes as separator). The git-cvsimport tool used
for creating the mirrors, however, uses
<a href="http://www.cobite.com/cvsps/">cvsps</a>, that updates the RCS tags to use
the correct format (<em>2009-10-19</em>). Adhering to standards is generally a
good thing, but in this case it was causing merge conflicts when trying
to merge patches created with Git into Drupal (or vice versa).</p>

<p>Damien found a way to resolve the issue, however:</p>

<blockquote><p>Adding DateFormat=old to the CVSROOT/config file fixes the problem.</p></blockquote>

<p>Changing this, however, required a reimport of the entire repository.
Due to the way Git works with commit-ids being a cryptographic hash of
their contents, changing the contents (even if just the RCS tags) means
a rewrite of Git history.</p>

<p>So while the new repository contains the same code, you will not be able
to merge new changes from it into your current checkouts. Damien will
continue both imports for a while, but updates for the old repository
with the incompatible date format will be discontinued at a future date.</p>

<p>What is the bottom line then?</p>

<h4>The executive summary</h4>

<ol>
<li>The Git mirror at git://github.com/drupal/drupal.git has been
rewritten with it&#8217;s RCS tag date format compatible with CVS defaults.
Please use this mirror for all your future projects.</li>
<li>The Git mirror at git://github.com/mikl/drupal.git will continue to
have the CVS-incompatible format, and will, for a time, continue to be
updated, so you will be able to use it for a little while longer.</li>
<li>There is now no excuse for not using Git for your Drupal core
development work. Enjoy.</li>
</ol>


<p>Finally, I&#8217;d like to thank Damien for doing all the hard work. I was maintaining the git-cvsimport process myself for a while, and I do not miss it.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">New blog, same as the old one…</title>
    <link href="http://mikkel.hoegh.org/blog/2009/10/15/new-blog-same-old-one/"/>
    <updated>2009-10-15T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2009/10/15/new-blog-same-old-one</id>
    <content type="html"><![CDATA[<p>So, I finally did it. I&#8217;ve long wanted to do something about this blog,
to try and push a better design on it and generally trim everything.</p>

<p>I wanted to try something new and challenging, so now I&#8217;ve rebuilt my
blog with <a href="http://github.com/montylounge/django-mingus">Django Mingus</a>.</p>

<p>Building stuff with <a href="http://www.djangoproject.com/">Django</a> tends to be
a lot of fun. I have quite a few ideas that I&#8217;d like to try out, so you
may see some of my work moving into Mingus.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">Rotating Apache httpd logfiles on FreeBSD</title>
    <link href="http://mikkel.hoegh.org/blog/2009/10/08/rotating-apache-httpd-logfiles-freebsd/"/>
    <updated>2009-10-08T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2009/10/08/rotating-apache-httpd-logfiles-freebsd</id>
    <content type="html"><![CDATA[<p>With the disk space available on modern servers, you tend to notice some
things a lot less. Like the boring fact that without log rotation, an
Apache access log can grow to gigabyte size in no time.</p>

<p>FreeBSD&#8217;s Apache HTTPD port does not ship with configuration for the
FreeBSD log rotation utility, <code>newsyslog</code>, so your logs won&#8217;t be rotated
by default.</p>

<p>That, however, is fairly easy to fix by tweaking /etc/
<a href="http://www.freebsd.org/cgi/man.cgi?query=newsyslog.conf&amp;sektion=5">newsyslog.conf</a> a bit.</p>

<p>Here&#8217;s how I did it:</p>

<pre><code>/var/log/httpd-access.log www:www 440 9 * $W1D4 J /var/run/httpd.pid 30
/var/log/httpd-error.log www:www 440 9 * $W1D4 J /var/run/httpd.pid 30
</code></pre>

<p>Broken up, this means:</p>

<ol>
<li><code>/var/log/httpd-access.log</code>: Name of the log file we&#8217;re rotating</li>
<li><code>www:www</code> set user and group ownership of the archived logs to www, so sysadmins can read them.</li>
<li><code>440</code> set the archive files to be read-only for the www user and group and no access for anyone else.</li>
<li><code>9</code> keep nine archived log files excluding the current one. This way, we should always have the latest 10 weeks of log data available.</li>
<li><code>*</code> don&#8217;t rotate based on log file size.</li>
<li><code>$W1D4</code> rotate logs every Monday at 4 in the morning.</li>
<li><code>J</code> compress the archived logs with bzip2.</li>
<li><code>/var/run/httpd.pid</code> – get the process ID for the httpd server here.</li>
<li><code>30</code> send <code>SIGUSR1</code> to cause a <a href="http://httpd.apache.org/docs/2.2/stopping.html#graceful">graceful restart of httpd</a>.</li>
</ol>


<p>I set this up last week, and it has since done its work, turning my 1GiB
/var/log/httpd-access.log into a 46MiB httpd-access.log.0.bz2. Log files
are some of the best use cases for compression. Enjoy.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html">How to get your Disqus API keys</title>
    <link href="http://mikkel.hoegh.org/blog/2009/09/15/how-get-your-disqus-api-keys/"/>
    <updated>2009-09-15T00:00:00+02:00</updated>
    <id>http://mikkel.hoegh.org/blog/2009/09/15/how-get-your-disqus-api-keys</id>
    <content type="html"><![CDATA[<p>I&#8217;m working on importing my comments into the otherwise excellent
<a href="http://disqus.com/">Disqus</a> commenting system, but getting ahold of
your API keys can be rather difficult, so I&#8217;ll just document the process
here for later reference.</p>

<p>To call the API functions, I&#8217;m using the Java-based
<a href="http://code.google.com/p/rest-client/">REST Client</a> – which is free and
very handy for this kind of thing.</p>

<ol>
<li>Log in to Disqus.com with a user that has access to the forum you want API keys for.</li>
<li>Visit <code>http://disqus.com/api/get_my_key/</code> <em>with your browser</em> to get the user API key (since it uses the active session to give you the API key).</li>
<li>Call <code>http://disqus.com/api/get_forum_list/?user_api_key=_USER_API_KEY_</code> to get the list of available forums, since you&#8217;ll need the numeric identifier for the forum. Look through the JSON response and find the id number for the forum you want an API key for (for my blog, it&#8217;s <code>"id": "180233"</code>).</li>
<li>Call <code>http://disqus.com/api/get_forum_api_key/?user_api_key=_USER_API_KEY_&amp;forum_id=180233</code> where <em>180233</em> is your forum id. The message field in the JSON response should contain your API key.</li>
</ol>


<p>That&#8217;s quite a bit of manual work, but it does not seem like there&#8217;s
currently any better method. If you happen to find one, please let me
know.</p>
]]></content>
  </entry>
  
</feed>
