Hacking Blogger templates
The ultimate goal here was filtering of posts based on inclusion (or even better, exclusion) of posts with certain labels. (Read this post first for context.)
[Update: before you think about imitating the following approach, you should know that I gave up with Blogger and switched to WordPress which is far more flexible and easy to hack.]
Unfortunately it turns out that Blogger’s XML-based template layout system is not quite as flexible as it first seems. It’s almost superb, but has some key omissions:
- Flow control is limited to
<b:if>
,<b:else>
, and<b:loop>
. Crucially, you can’t break out of a loop. - No writable variables of any kind.
- Conditional testing via
<b:cond>
is extremely limited. I kept expecting to find some comprehensive documentation for thexmlns:expr
namespace (which is used via things like<a expr:href='data:blog.homepageUrl + "search/label/foo"'>
), but it doesn’t exist, simply because all you can really do is simple comparisons using a limited set of data and hardcoded strings. - There are no string-handling functions, and you can’t get access to the current
QUERY_STRING
to do any kind of parametrisation (for example, show some HTML saying which label you are currently viewing based on what comes after/search/label/
in the current URL). - The box generated by the status-message includable is hardcoded to either be invisible or say “Showing label foo. Show all posts”. It cannot be customised.
I briefly considered ditching the idea of fiddling with this XML, and just using Javascript and CSS to hide the posts I wanted hidden, but persevered and came up with a solution with the following ingredients:
- I will label all posts with at least one of ‘geek’ or ‘main’, or possibly both. Posts labelled ‘main’ will appear on the default home page view. This is unfortunately necessary since there is no general way of excluding posts with a particular label, only including. The reason for this is that the only way to test for membership in a set of labels is by looping over all the post’s labels, and when you find the one you are including for, you output the post’s contents. If you were trying to exclude, you’d need a temporary variable to flag when you’d found the excluded label, then use that outside the loop.
- I used a standard template modification trick to limit visible posts on the home page to those labelled ‘main’.
- I wrapped my custom code in stuff like
<b:if cond='data:blog.pageType != "item"'>
so that it wouldn’t mess with single-post views. - I figured out that
is a URL which will yield a view containing all posts, circumventing the
<b:if cond='data:blog.url == data:blog.homepageUrl'>
equality test in my default view (also works)
- I reimplemented the status-message box from scratch, using Javascript:
var labelsel = document.URL.match(/\/search\/label\/(.*)/); var view = 'unknown'; if (document.URL == &amp;quot;&amp;amp;lt;data:blog.homepageUrl/&amp;amp;gt;&amp;quot;) { view = 'default'; } else if (labelsel) { switch (labelsel[1]) { case &amp;quot;&amp;quot;: view = 'all'; break case &amp;quot;main&amp;quot;: view = 'default'; break case &amp;quot;geek&amp;quot;: view = 'geek'; break default: view = 'label'; } } else if (document.URL.match(/\/search.*updated-min/)) { view = 'time range'; } else { view = 'unknown'; } switch (view) { case 'all': document.write(&amp;quot;Showing all posts. &amp;amp;lt;a href='/search/label/main'&amp;amp;gt;Hide the geek stuff&amp;amp;lt;/a&amp;amp;gt; or &amp;amp;lt;a href='/search/label/geek'&amp;amp;gt;hide the non-geek stuff&amp;amp;lt;/a&amp;amp;gt;&amp;quot;); break; case 'default': document.write(&amp;quot;Not showing computer stuff. You can see &amp;amp;lt;a href='/search/label/geek'&amp;amp;gt;the geek stuff&amp;amp;lt;/a&amp;amp;gt;, &amp;amp;lt;a href='/search/label/music'&amp;amp;gt;music stuff&amp;amp;lt;/a&amp;amp;gt;, or &amp;amp;lt;a href='/search/label/'&amp;amp;gt;all posts together&amp;amp;lt;/a&amp;amp;gt;.&amp;quot;); break; case 'geek': document.write(&amp;quot;Only showing computer stuff.&amp;amp;lt;br/&amp;amp;gt;&amp;amp;lt;a href='/search/label/'&amp;amp;gt;See all posts&amp;amp;lt;/a&amp;amp;gt; or &amp;amp;lt;a href='/search/label/main'&amp;amp;gt;just the non-geek stuff&amp;amp;lt;/a&amp;amp;gt;&amp;quot;); break; case 'label': document.write(&amp;quot;Only showing posts labelled &amp;amp;lt;strong&amp;amp;gt;&amp;quot; + labelsel[1] + &amp;quot;&amp;amp;lt;/strong&amp;amp;gt;.&amp;amp;lt;br/&amp;amp;gt;&amp;amp;lt;a href='/'&amp;amp;gt;Go to default view&amp;amp;lt;/a&amp;amp;gt;&amp;quot;); break; case 'time range': document.write(&amp;quot;Showing posts within a time range.&amp;amp;lt;br/&amp;amp;gt;&amp;amp;lt;a href='/'&amp;amp;gt;Go to default view&amp;amp;lt;/a&amp;amp;gt;&amp;quot;); break; case 'unknown': document.write(&amp;quot;Unknown viewing mode, but don't panic.&amp;quot;); break; default: document.write(&amp;quot;BUG: Don't know how to handle viewing mode &amp;quot; + view); }
- I replaced the non-item part of the feedLinks includable with a hardcoded version.
- I altered the label-listing widget to exclude the ‘main’ label from being displayed.
If you want to look at my complete layout template, it’s here.