How-to build a links weblog with Movable Type

Published on:

This is a little tutorial on how I built a links weblog using Movable Type.

I recently added a links weblog -- some may call this a linksblog or a linkslog -- to padawan.info and I thought it would be useful to document the how-to process with Movable Type (at least to me when I'll try to do it again properly next time).

Genèse

I'm a subscriber of Kottke's Remainder feed. Needless to say his links weblog inspired this one and remains far better in every aspects (except one, which I explain below). It also fills the need to get rid of my tedious tabs dumps in a more convenient manner.

A little bit of preparation

One strength of Movable Type is the ability to create as many weblogs as wanted with a single installation. An even more hidden jewel is how to combine several weblogs to complement one. I drive two weblogs here, one in English and one in French, and my idea was to create a single facility that would serve both in their respective language.

I started to create a new weblog named Food For Thoughts and gave it its own space. The next step was to think about how to get around the inflexible post model of MT. Here is what MT gives you to describe a "post":

  • Title
  • Entry Body
  • Extended Entry
  • Excerpt
  • Category (Primary/Secondary Categories)
  • Keywords
  • Authored On Date

Here is what I needed for the links, and how I mapped it to the list above:

  • Title -> Title
  • URL -> Entry Body
  • Headline/Excerpt (optional) -> Extended Entry
  • Language -> Category
  • Keywords (optional) -> Keywords

The non obvious choices where to use Extended Entry instead of Excerpt for the Headline/Excerpt, and Category for Language. I went for Excerpt first but hit a wall because of the optional nature of my Headline/Excerpt data. I could not find a way to know if Excerpt was empty because this field is never reported as such by MT (it returns either the excerpt or the 20 first words of the Entry Body). On the contrary, I could test if Extended Entry was empty and adapt the template logic accordingly. Likewise, because I needed to have some template logic to handle the languages, I chose to use Category to hold the language and defaulted to Keywords for the categorization of my links. You will see in the templates how these choices play together.

The next step was to think about the integration of this links weblog into my respective weblogs. I wanted to have a list of recent links on the front page, an RSS links feed and an archive page. Because of the way my weblogs are configured, it was very easy to do using Server Side Includes in addition to the standard MT mechanisms. Let's see the templates.

Templates

The basic lists of links
This is the template for the list of recent links:

<div class="linksblog">
<ul>
<MTEntries lastn="20" category="en">
<li><a href="<$MTEntryBody convert_breaks="0"$>"<MTEntryIfExtended> title="<$MTEntryMore convert_breaks="0"$>"</MTEntryIfExtended>><$MTEntryTitle$></a></li>
</MTEntries>
</ul>
</div>
And the resulting list

In this template, lastn will control how many links will appear on the list and category in which language. The French links list is a simple duplicate of this template, changing the category attribute to "fr" (it produces the list of links in French.) Both are index templates set to rebuild automatically each time I add a link. Note the convert_breaks="0" which is used in many places where I don't want MT to wrap the data around <p> tags or generate <br /> tags. Lastly, if there is a headline or excerpt provided (in the Extended Entry field), the link title will reflect it (most browsers will show it as a tooltip when you mouse over the link).

The lists are wrapped in a div with a class name of "linksblog" in order to style them. I added the following style to each weblog style sheet:

.linksblog ul {
list-style-type:none;
padding-left: inherit;
}

which removes the bullet in front of the list items and align them with the current left padding because I display them within a side column.

The RSS feed of links
This is the template for the RSS feed of recent links:

<?xml version="1.0" encoding="<$MTPublishCharset$>"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
xmlns:admin="http://webns.net/mvcb/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<channel>
<title><$MTBlogName remove_html="1" encode_xml="1"$></title>
<link><$MTBlogURL$></link>
<description><$MTBlogDescription remove_html="1" encode_xml="1"$></description>
<dc:language>en-us</dc:language>
<dc:creator><MTEntries lastn="1"><$MTEntryAuthorEmail$></MTEntries></dc:creator>
<dc:date><MTEntries lastn="1"><$MTEntryDate format="%Y-%m-%dT%H:%M:%S"$><$MTBlogTimezone$></MTEntries></dc:date>
<admin:generatorAgent rdf:resource="http://www.movabletype.org/?v=<$MTVersion$>" />
<sy:updatePeriod>hourly</sy:updatePeriod>
<sy:updateFrequency>1</sy:updateFrequency>
<sy:updateBase>2000-01-01T12:00+00:00</sy:updateBase>
<MTEntries lastn="30" category="en">
<item>
<title><$MTEntryTitle remove_html="1" encode_xml="1"$></title>
<link><$MTEntryBody encode_xml="1" convert_breaks="0"$></link>
<description><MTEntryIfExtended><$MTEntryMore convert_breaks="0" remove_html="1" encode_xml="1"$></MTEntryIfExtended ></description>
<guid isPermaLink="false"><$MTEntryID$>@<$MTBlogURL$></guid>
<dc:subject><$MTEntryKeywords encode_xml="1"$></dc:subject>
<dc:date><$MTEntryDate format="%Y-%m-%dT%H:%M:%S"$><$MTBlogTimezone$></dc:date>
</item>
</MTEntries>
</channel>
</rss>
And the resulting RSS feed

Again, lastn and category are used to control the number of links and their language (French RSS). This template is of course an index template set to rebuild automatically. Also, through the use of <MTEntryIfExtended>, I list the headline or excerpt if present. If no headline/excerpt is present, your newsreader should simply list a line with the title -- I wanted this to contrast with Kottke's Remainder feed which almost always replicates the link title in the description, something I find annoying because the resulting visual noise actually prevents you to detect when he does provide a bit of insight in addition to the title. Keywords are mapped to the RSS 2.0 subject entity, which most newsreaders will display.

The main list
This is the core code of the template for the main list of recent links (I removed the extra bits that belong to my own presentation):

<div id="content">
<div class="blogbody">
<div class="linksblog">
<h1>Food For Thoughts -- links weblog</h1>
<ul>
<MTEntries lastn="30" category="en">
<li><a href="<$MTEntryBody convert_breaks="0"$>"><$MTEntryTitle$></a><MTEntryIfExtended> <br /><$MTEntryMore convert_breaks="0"$></MTEntryIfExtended> <span class="posted"><MTIfNonEmpty tag="MTEntryKeywords"> [<$MTEntryKeywords$>]</MTIfNonEmpty> - <$MTEntryDate format="%x"$></span></li>
</MTEntries>
</ul>
</div>
</div>
</div>
And the resulting page

Here the main difference is the use of <MTIfNonEmpty tag="MTEntryKeywords"> which will display the keywords if present. That's why I couldn't use the Excerpt filed as <MTIfNonEmpty tag="MTEntryExcerpt"> is always true even when this field is empty (which is a wrong design decision, IMHO).

Integration with the weblogs

Another hidden jewel of MT is the way it generates static files when, and only when, needed (like when one creates a post). The drawback of this method is that it requires a little bit of trickery in order to reduce the need for rebuilds, which becomes even trickier when you start to integrate the output of several weblogs into one. My best method to do that is to use server side includes, and how this works is better explained by this bit of template code which sits in my main index template:

<div class="sidetitle">Food for Thoughts</div>
<div class="side">
Elsewhere on the web:<br />
<!--#include virtual="/foodforthoughts/linksblog-en.html" -->
Syndicate this links weblog: <a href="/foodforthoughts/index-en.xml">RSS</a>
</div>

The <!--#include virtual="/foodforthoughts/linksblog-en.html" --> line will ask the web server (Apache in my case, but this syntax works also on MS IIS and other web servers depending on their configuration) to include the content of the file /foodforthoughts/linksblog-en.html which is generated by the first template described above. This way, an update of the list is instantly reflected on the main weblog without having to resort to a rebuild.

If you are using PHP, you can do the same through the use of <? virtual("/path/to/file..."); ?>. Other languages have their own way of including the content of a file, use what is appropriate in your situation.

Caveats

Obviously, the language split is not something that most people will have to deal with. My choice to map the language to categories may prove limiting, or unsuitable, for others. I did this because MT tags provide a convenient way to discriminate entries through categories, which you may find convenient to run a weblog that spans many categories. However, the choice of Keywords has an advantage as this field gets indexed by MT's search engine.

I also rely heavily on SSI to include lots of things in my weblogs. If your web server does not support SSI, the method described here will have to be adapted. You may use MT's own include method, although I don't know how to trigger a cascade of rebuilds of several weblogs at once (i.e. how to rebuild the home page of the main weblog when the links webog is updated).

More to do

By comparing this simple links weblog to Kottke's, there are a few things missing that I might get a shot at:

  • Finish the integration on the French side!
  • Better archiving or, more honestly, some actual design ;-)
  • Better integration with the weblog flow. But I might not be doing it, as I'm not a big fan of the linear flow and confusion of posts types
  • Fix the search engine (currently it will search on the main weblog, not in the links)
  • The ability to comment on links (only if you ask ;-)
  • The ability to store and report the source of the link