{"id":318,"date":"2006-08-21T23:13:50","date_gmt":"2006-08-22T03:13:50","guid":{"rendered":"http:\/\/jclark.org\/weblog\/2006\/08\/21\/the-permalink-problem\/"},"modified":"2006-08-21T23:13:50","modified_gmt":"2006-08-22T03:13:50","slug":"the-permalink-problem","status":"publish","type":"post","link":"https:\/\/jclark.org\/weblog\/2006\/08\/21\/the-permalink-problem\/","title":{"rendered":"The Permalink Problem"},"content":{"rendered":"<p>The conversion from <a href=\"http:\/\/blosxom.com\">Blosxom<\/a> to <a href=\"http:\/\/wordpress.org\">Wordpress<\/a> began (in my head, at least), over a year ago, when I <a href=\"http:\/\/jclark.org\/weblog\/2005\/06\/15\/friction-and-tags\/\">began to consider switching from categories to tags<\/a> as a way to reduce the &#8220;<a href=\"http:\/\/jclark.org\/weblog\/2005\/06\/13\/friction\/\">friction<\/a>&#8221; of writing.  Blosxom is good at many things, but its filesystem based storage isn&#8217;t well suited for tagging.  After exploring several possiblilites, I decided I&#8217;d move away from Blosxom; eventually I settled on Wordpress.<\/p>\n<p>The biggest hurdle I faced was dealing with permalinks.  Blosxom supports two styles of permalinks: date-based and category based.  I decided to go with category style permalinks (e.g., <code>\/weblog\/Apple\/macbook.html<\/code>) when I first started using Blosxom because the URIs are &#8220;hackable&#8221;; you can whack the end (filename) off the URI and you&#8217;ve got the URI for the category.  This worked fine when the site was category based, but fails with tags.  For this reason, I&#8217;ve configured Wordpress to use date based URIs (e.g., <code>\/weblog\/2006\/01\/01\/macbook\/<\/code>); I also decided to drop the &#8220;<code>.html<\/code>&#8221; bit since its not necessary.<\/p>\n<p>However, <a href=\"http:\/\/www.w3.org\/Provider\/Style\/URI\">Cool URIs Don&#8217;t Change<\/a>.  There are plenty of old links to my site out around the Internets, which use category based-permalinks, that I don&#8217;t want to break. Even the internal links within existing posts on this site will continue to use the old URIs, at least until I can get around to cleaning them up. I wanted to keep supporting these existing URIs, so I needed to make sure I can support the old permalinks.<\/p>\n<p>Wordpress supports category based permalinks, but with some caveats.  If I configure the whole site to use category permalinks, then date-based URIs don&#8217;t work.  If I configure the site to use date-based permalinks, category-based URIs don&#8217;t work.  After searching for a plugin to help without success, I tried the Wordpress forums.  When that failed to turn up a solution, I poked around the code looking for a solution.<\/p>\n<p>(A brief aside.  If you want to explore the Wordpress codebase, I recomment this <a href=\"http:\/\/wordpress.taragana.net\/nav.html?index.html\">excellent online cross-reference<\/a>.)<\/p>\n<p>I ended up creating a simple plugin that uses the &#8220;<code>generate_rewrite_rules<\/code>&#8221; action hook to add additional URI rewriting rules to the internal set used by Wordpress to resolve each URI.  I hope to make it available as a plugin someday when I have time to make it more generic; currently its hardcoded to solve my problem.  Here&#8217;s the heart of the code, if you want to build your own version:<\/p>\n<pre><code>function add_permalink_style($rewriteobj) {\n    $extra_rewrite = $rewriteobj-&gt;generate_rewrite_rules(&#039;\/%category%\/%postname%.html&#039;, EP_PERMALINK);\n    $extra_rewrite = apply_filters(&#039;post_rewrite_rules&#039;, $extra_rewrite);\n\n    $rewriteobj-&gt;rules = array_merge($rewriteobj-&gt;rules, $extra_rewrite);\n}\n\nadd_action(&#039;generate_rewrite_rules&#039;, &#039;add_permalink_style&#039;);<\/code><\/pre>\n<p>To help me test everything, I tossed together a quick and dirty <a href=\"http:\/\/jclark.org\/wptests.html\">test suite<\/a>, a simple list of links to test from my browser.  The code above allows them all to pass. <\/p>\n<p>Because my old permalinks all included <code>.html<\/code> at the end, I can use the rewrite rule above to strip it out.  I originally was doing that with a <a href=\"http:\/\/httpd.apache.org\/docs\/1.3\/mod\/mod_rewrite.html\"><code>mod_rewrite<\/code><\/a> rule in my .htaccess file, but that caused problems for old permalinks to my category archives.  Since the <code>mod_rewrite<\/code> rule was stripping the <code>.html<\/code> suffix before wordpress could see the URI, category URIs (e.g. \/weblog\/Apple\/OSX\/) and category-style post permalinks (e.g. \/weblog\/Apple\/OSX\/howto-install-carbon-emacs\/) matched the same pattern, and Wordpress thought the first example was for a post named &#8220;OSX&#8221; in the Apple category.  I believe this is the same thing thats causing my tag URIs (e.g., \/weblog\/tag\/wordpress\/) to fail at the moment; the only workaround I&#8217;ve found requires a <code>mod_rewrite<\/code> RewriteRule, but only works with a browser redirect.  I&#8217;m still working on  this problem, as I really don&#8217;t want to use redirects.  Also, for reasons I haven&#8217;t quite figured out, the above makes category permalinks work (and without a category URI prefix, see below), even though the rule expects a post name (slug) with <code>.html<\/code> appended- but I&#8217;m not going to argue with success.<\/p>\n<p>Another issue I ran into was Wordpress always wanting a prefix in front of category archive links.  For example, my Apple category&#8217;s archive link (by default) was <code>\/weblog\/category\/Apple\/<\/code>.  Even though my plugin&#8217;s extra rewrite rule seems to make URIs like <code>\/weblog\/Apple\/<\/code> work, I wanted the catgeory links on my <a href=\"http:\/\/jclark.org\/weblog\/archive\/\">Archive<\/a> page to omit the extra prefix, for consistancy.  Wordpress allows you to change, but not omit, this prefix.  I came up with a cheap hack for that problem; since I could already handle the URIs without the prefix, I only needed to change the URIs generated on the archive page via Wordpress&#8217; <code>wp_list_cats()<\/code> function.  I was able to do this in my plugin by hooking into the <code>list_cats<\/code> filter:<\/p>\n<pre><code>function fix_category_links($content) {\n    $content = str_replace(&#039;\/category\/&#039;, &#039;\/&#039;, $content);\n    return $content;\n}\n\nadd_filter(&#039;list_cats&#039;, &#039;fix_category_links&#039;);<\/code><\/pre>\n<p>It&#8217;s a hack, but it&#8217;s a hack that works.<\/p>\n<p>At this time, I believe all the old permalinks from my Blosxom blog (both posts and categories) should work, as well as the new date-based permalinks.  A key part of all of this was to import my existing Blosxom content while preserving the filename (slug in Wordpress parlance), I&#8217;ll cover this and rest of the import process in a subsequent post.  If you should find any links that don&#8217;t work, especially from external sources, please let me know.  Now if I can just fix those tag URIs&#8230;.<\/p>","protected":false},"excerpt":{"rendered":"<p>The conversion from Blosxom to Wordpress began (in my head, at least), over a year ago, when I began to consider switching from categories to tags as a way to reduce the &#8220;friction&#8221; of writing. Blosxom is good at many things, but its filesystem based storage isn&#8217;t well suited for tagging. After exploring several possiblilites, [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[115,114,35,37,36,34,38],"class_list":["post-318","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-blogging","tag-blosxom","tag-jclarkorg","tag-mod_rewrite","tag-permalinks","tag-wordpress","tag-wordpress-api"],"_links":{"self":[{"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/posts\/318","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/comments?post=318"}],"version-history":[{"count":0,"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/posts\/318\/revisions"}],"wp:attachment":[{"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/media?parent=318"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/categories?post=318"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/jclark.org\/weblog\/wp-json\/wp\/v2\/tags?post=318"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}