For a while back in March I had changed the look of my main blog page so that it showed entries from my Quick Reviews blog interleaved with my main blog entries, in the appropriate chronological order. Frank suggested that I should put together a tutorial on how to do this with Movable Type, so here it is.
We’re currently running 7 distinct blogs on sunpig.com, all using the same Movable Type installation. Previously, when we wanted to include content from one blog in another one we would create a new template in blog 1, and then use a server-side include in blog 2 to pull in the content. Here’s an example:
Headlines index template in blog 1:
<div class=”side”>
<MTEntries lastn=”10″>
<a href=”<$MTEntryPermalink$>”><$MTEntryTitle$></a><br />
</MTEntries>
</div>
Main index template in blog 2:
This is probably the simplest way of including content from one blog into another. All it requires is the basic Movable Type installation (no plugins needed), and a server that supports server-side includes (SSI).
Although the example above shows how to include content in your sidebar, the technique is exactly the same if you want to put the full entries in the main body of your page. All you have to do is change the template in blog 1 to produce the full entries (or excerpts) instead of just the headlines, and put the #include
line in the appropriate position in blog 2.
Unfortunately this method doesn’t allow you to interleave entries from both blogs. You will just end up with two blocks of content:
- Blog 2 entry 3 (Friday)
- Blog 2 entry 2 (Wednesday)
- Blog 2 entry 1 (Monday)
- Blog 1 entry 3 (Thursday)
- Blog 1 entry 2 (Tuesday)
- Blog 1 entry 1 (Sunday)
We want to get to a position where the blog entries are merged together in (reverse) chronological order:
- Blog 2 entry 3 (Friday)
- Blog 1 entry 3 (Thursday)
- Blog 2 entry 2 (Wednesday)
- Blog 1 entry 2 (Tuesday)
- Blog 2 entry 1 (Monday)
- Blog 1 entry 1 (Sunday)
To do this we need two things:
- Your MT installation must be running on MySQL, or another SQL-capable database, rather than the default Berkeley File DB. (See the notes on how to updgrade.)
- Brad Choate’s MTSQL plugin
The MTSQL plugin does many things, but primarily it gives you much finer control over what entries you want to extract from your blog entries. The basic <MTEntries> allows you to select a list of entries based on category and/or author, to limit the number of entries shown, and to sort them by a small set of criteria (title, date, etc.). MTSQL provides a <MTSQLEntries> tag that allows you to select entries based on pretty much any set of criteria you can imagine, and sort them however you want. You need some knowledge of the SQL database language to put together complex selections, but interleaving two (or more!) blogs is exceedingly simple.
All you have to do is go into your main index template and replace the <MTEntries> tag with an <MTSQLEntries> tag, like so (make sure that this stays all on one line):
<MTSQLEntries> tag for interleaved blog entries:
The query
parameter describes the list of entries you want to display. The query above does three things:
- It selects published entries from the blogs with ID numbers 1 and 2: “
select entry_id from mt_entry where entry_blog_id in (1,2) and entry_status=2
“. To make this work for your own installation, you need to know the ID numbers of the blogs you want to interleave. You can merge as many blogs as you like by expanding the list in brackets. Theentry_status
value is there to make sure that you only show entries whose status is set to “published”. - It shows these blog entries in reverse chronological order: “
order by entry_created_on desc
“. - It limits the entries displayed to the first ten in this reverse chronological list: “
limit 0,10
“. You can change this to show as many entries as you like, e.g. “limit 0,50
” would show the most recent 50. If you wanted to skip the first 10 in the list, and show the block of entries from 10 to 20, you would set the clause to “limit 10,20
” (like the “offset
” parameter in the standard <MTEntries> tag..
The unfiltered
parameter must to be set to 1, to stop Movable Type from further restricting your list of entries. Without the parameter, MT would exclude “draft” entries (which we’re already doing by saying “entry_status=2
“), and entries that don’t belong to the current blog, which is what we’re trying to avoid in the first place.
Here’s a section from the default Movable Type index template, with the <MTEntries> tag replaced by <MTSQLEntries>:
Movable Type code to show interleaved entries from multiple blogs:
<MTDateHeader>
<h2 class=”date”>
<$MTEntryDate format=”%x”$>
</h2>
</MTDateHeader>
<div class=”blogbody”>
<a name=”<$MTEntryID pad=”1″$>”></a>
<h4 class=”title”><$MTEntryTitle$></h4>
<$MTEntryBody$>
<MTEntryIfExtended>
<span class=”extended”><a href=”<$MTEntryPermalink$>#more”>Continue reading “<$MTEntryTitle$>”</a></span><br />
</MTEntryIfExtended>
<div class=”posted”>Posted by <$MTEntryAuthor$> at <a href=”<$MTEntryPermalink$>”><$MTEntryDate format=”%X”$></a> in the blog <a href=”<$MTBlogURL$>”><$MTBlogName$></a>
<MTEntryIfAllowComments>
| <a href=”<$MTCGIPath$><$MTCommentScript$>?entry_id=<$MTEntryID$>” onclick=”OpenComments(this.href); return false”>Comments (<$MTEntryCommentCount$>)</a>
</MTEntryIfAllowComments>
<MTEntryIfAllowPings>
| <a href=”<$MTCGIPath$><$MTTrackbackScript$>?__mode=view&entry_id=<$MTEntryID$>” onclick=”OpenTrackback(this.href); return false”>TrackBack (<$MTEntryTrackbackCount$>)</a>
</MTEntryIfAllowPings>
</div>
</div>
</MTSQLEntries>
That’s all you need to produce simple, interleaved entries. But why stop there? With just a little additional effort you can take the next step and give entries from the different blogs their own distinct look. The simple way to do this is by using Kevin Shay’s Compare plugin. Just like MTSQL, the Compare plugin is a bit of a Swiss Army knife, but all you really need is the <MTIfEqual> tag it provides.
The following code is another version of the default Movable Type index template. As before, it uses the <MTSQLEntries> tag to retrieve an interleaved list of entries. But inside the <MTSQLEntries> tag we’re using <MTIfEqual> to check which blog an entry belongs to, and to format the entries accordingly:
Movable Type code with interleaved blog entries and different visual styles:
<MTDateHeader>
<h2 class=”date”>
<$MTEntryDate format=”%x”$>
</h2>
</MTDateHeader>
<MTIfEqual a=”[MTSQLColumn column=’2′]” b=”1″>
<div class=”blogbody” style=”background-color:khaki;”>
<a name=”<$MTEntryID pad=”1″$>”></a>
<h4 class=”title”><$MTEntryTitle$></h4>
<$MTEntryBody$>
<MTEntryIfExtended>
<span class=”extended”><a href=”<$MTEntryPermalink$>#more”>Continue reading “<$MTEntryTitle$>”</a></span><br />
</MTEntryIfExtended>
<div class=”posted”>Posted by <$MTEntryAuthor$> at <a href=”<$MTEntryPermalink$>”><$MTEntryDate format=”%X”$></a> in the blog <a href=”<$MTBlogURL$>”><$MTBlogName$></a>
<MTEntryIfAllowComments>
| <a href=”<$MTCGIPath$><$MTCommentScript$>?entry_id=<$MTEntryID$>” onclick=”OpenComments(this.href); return false”>Comments (<$MTEntryCommentCount$>)</a>
</MTEntryIfAllowComments>
<MTEntryIfAllowPings>
| <a href=”<$MTCGIPath$><$MTTrackbackScript$>?__mode=view&entry_id=<$MTEntryID$>” onclick=”OpenTrackback(this.href); return false”>TrackBack (<$MTEntryTrackbackCount$>)</a>
</MTEntryIfAllowPings>
</div>
</div>
</MTIfEqual>
<MTIfEqual a=”[MTSQLColumn column=’2′]” b=”2″>
<div class=”blogbody” style=”background-color:lightsteelblue;”>
<a name=”<$MTEntryID pad=”1″$>”></a>
<h4 class=”title”><$MTEntryTitle$></h4>
<$MTEntryBody$>
<MTEntryIfExtended>
<span class=”extended”><a href=”<$MTEntryPermalink$>#more”>Continue reading “<$MTEntryTitle$>”</a></span><br />
</MTEntryIfExtended>
<div class=”posted”>Posted by <$MTEntryAuthor$> at <a href=”<$MTEntryPermalink$>”><$MTEntryDate format=”%X”$></a> in the blog <a href=”<$MTBlogURL$>”><$MTBlogName$></a>
<MTEntryIfAllowComments>
| <a href=”<$MTCGIPath$><$MTCommentScript$>?entry_id=<$MTEntryID$>” onclick=”OpenComments(this.href); return false”>Comments (<$MTEntryCommentCount$>)</a>
</MTEntryIfAllowComments>
<MTEntryIfAllowPings>
| <a href=”<$MTCGIPath$><$MTTrackbackScript$>?__mode=view&entry_id=<$MTEntryID$>” onclick=”OpenTrackback(this.href); return false”>TrackBack (<$MTEntryTrackbackCount$>)</a>
</MTEntryIfAllowPings>
</div>
</div>
</MTIfEqual>
</MTSQLEntries>
Note that the query
parameter on the <MTSQLEntries> tag is slightly different. Instead of selecting just a list of entry ids, the SQL code also grabs the blog id for each entry: “select entry_id, entry_blog_id from mt_entry
“. We need this so that the <MTIfEqual> tags can check this value for every entry:
Using <MTIfEqual> to check the blog id:
The <MTIfEqual> has two parameters, a
and b
. If the values of these two parameters are equal, then code inside the <MTIfEqual> block will be run. If not, then Movable Type will skip over the block. In this example, the a
parameter holds a reference to the blog id for each entry, as represented by “[MTSQLColumn column='2']
“.
The chunk of template code contains two <MTIfEqual> blocks. The first block processes entries from blog 1, and the second deals with entries from blog 2. In this case, the only difference between the two blocks is that the background colour applied to the entries (khaki or lightsteelblue), but you can customize these blocks in exactly the same way as you would any other Movable Type template.
As thousands of highly individual Movable Type blogs can attest, there’s almost no limit to the number of ways you can customize a list of blog entries. Go wild!
Hi!
I did the first option and tried to pull the contents from excerpt.html from two other blogs in 2 different subdomains at cooking.houseonahill.net and women.houseonahill.net. Nothing came out. They were supposed to appear on the right column.
You can view it at http://houseonahill.net.
Can you help me out please?
Thanks.
First of all, the #include directives are coming through in the output HTML (view source) from your home page. This probably means one of two things: either your web server is not configured to allow SSI includes, or type type of file that is being output (.html, .php, etc.) is not being parsed for SSI directives. If the directives were getting parsed, you’d be seeing an output error like “[an error occurred while processing this directive]”.
Your first point of call should therefore be to have a look at the Apache tutorial on SSI includes, which includes instructions on how to get your web server to behave properly: http://httpd.apache.org/docs-2.0/howto/ssi.html and look in the section “Configuring your server to permit SSI.”
Once you’ve got beyond this, though, there may be a second problem. The #include technique at the start of the article will probably not work if you have your blogs in different subdomains (i.e. women.houseonahill.net as opposed to http://www.houseonahill.net).
There are two ways to do a SSI include, with the “virtual” keyword, and with the “file” keyword. If you use the “virtual” keyword to specify file to be included, the web server will look for the file in starting in the root of the the current virtual directory. http://www.houseonahill.net and women.houseonahill.net will be different virtual directories.
If you use the “file” keyword to specify the file you want to pull in (i.e.: <!–#include file=”blah/blah/blah.html” –>), your includes can span virtual directories, provided that you know the exact file path, relative to the page in which the #include directive is embedded.
There is one other big “but” with regard to using the “file” keyword, and that is: your file path can’t be an absolute file path (starting with “/”), and it can’t move “up” a directory (i.e., it can’t contain “../”). So you can only use files *in the same directory as*, or in directories *below* the current one. Which can be a bit of a bummer. (See the section “Including a standard footer” in the Apache SSI tutorial for a little more detail.)
In other words, if your web server is set up so that your subdomains are in parallel directories at the same level of the file hierarchy (e.g. /www/users/houseonahill/www, /www/users/houseonahill/cooking, /www/users/houseonahill/women, etc.) you’re screwed. The “virtual” keyword won’t work across subdmains, and the “file” keyword won’t allow you to go up a directory from “www”, and then back down into “cooking”.
If this is the case, you’ve still got options. You can use Brad Choate’s MTSQL plugin (http://www.bradchoate.com/past/mtsql.php), or David Raynes’ MTOtherBlog plugin (http://mt-plugins.org/archives/entry/otherblog.php) to generate content from one blog directly in the main index template of another. The MTSQL way is very similar to the technique I described in the article above, but the MTOtherBlog way is much simpler. Have a look at its page home on the MT Plugins directory for more details.
Hope this helps!
Hi Martin,
Thanks.
It still isn’t working. My webhost said that SSI is installed by default.
The subdomains are folders inside the public_html folder. SO I guess that means they are “below” the mail directory. The path would be:
http://houseonahill.net/women
and
http://houseonahill.net/cooking
My webhost said to use .shtml on the files. So I did. In http://houseonahill.net/index.shtml which is the page where the output files should appear.
The “sources” were also converted to .shtml.
http://houseonahill.net/cooking/excerpt.shtml
and
http://houseonahill.net/women/excerpt.shtml
Still, I get[an error occurred while processing this directive] on the output page.
Acually, before I tried the SSI option, I first tried Brad Choate’s plugin. But my index page became inaccesible. I couldn’t upload; I couldn’t rebuild. I had to delete all 4 of Brad Choate’s files.
I’m going nuts because my index page looks awful with a “blank” right column.
Hope there’s a solution? Thanks again. Really appreciate it.
Well, you’re getting an [an error occurred while processing this directive] message, which is actually progress. Also, knowing that http://cooking.houseonahill.net/ is equivalent to http://www.houseonahill.net/cooking/ is useful. This means that your “cooking” subdomain lies in a directory immediately below your main “www” domain in the file structure.
Based on this, I think your include statements should like like this: <!–#include file=”cooking/excerpt.shtml” –> and <!–#include file=”women/excerpt.shtml” –> Give this a try?
I finally got it right. I feel so stupid. I was looking for some complex problem as to why nothing was working when it was so simple.
When I copied the tag from the textbox above, I installed it as it is. I FORGOT TO ADD THE CLOSE QUOTATION.
So everything’s working now. Thanks very much. Your solution was much simpler than all the others I read.
No problem. I’ve fixed the missing quotation mark in the body of the article now. Oops…
If you don’t want to mess with the MT-SQL plugin, here’s a way to achieve the same effect with the GlobalListings plugin: http://www.hitormiss.org/archives/2004/01/integrated-quicklinks-tutorial
Thanks for the really useful tutorial! Working from your tutorial, I expanded on using MTSQL to integrate two blogs, including combining the category archives and date-based archives for multiple weblogs, which you can find here: http://blog.blameitoneve.com/archives/000363.php
Hi Martin,
I’m just learning to play with MT.
I want to use the simple, first approach you describe, nut I’m not sure how to make it work.
I’m uncertain about 2 things:
1 – Is the code you pasted for your headlines file all you need, or should I include the surrounding tags, including the header content?
2 – I haven’t been able to find a place to define an output file for my new template.
thanks
I just found the answer to my second cuestion: It must be a new Index Template (Create New Index Template) – I guess this makes the first one easier to answer.
thanks anyway for a great article.
Javier,
The container page should be a normal HTML page, with its own <head> and <body>. The output from the headlines index template will be included as part of the container file–just as if it were a simple <div>–so it doesn’t need any more HTML markup than is shown in the example.
Hope this helps!
Worked great!
Thanks for the help and great article.