« May 2005 | Main | July 2005 »

June 29, 2005

Dynamic Tag Archiving with Tagwire and MT-XSearch

Instead of static tag/category archives supported by Tags Plugin, Tagwire provides Dynamic Tag Archiving by using Tim Appnel's MT-XSearch. Dynamic Tag Archiving is slower than static one but preferable to support more flexible archiving. For example, Tagwire supports listing entries having two or more tags.

In this article, I will explain how Tagwire plugin cooperates with Tim Appnel's MT-XSearch and realize Dynamic Tag Archiving.

How to setup

Now assume that you've already installed Tagwire plugin.

  1. Install MT-XSearch:
    First, download mt-plus-1.01.zip from Download mt-plus | Appnel Internet Solutions. And unpack it and copy/upload the following files into your Movable Type directory.
    • mt-xsearch.cgi (this file should be executable)
    • plugins/mt-xsearch.pl
    • extlib/MT/XSearch.pm
  2. Modify mt-xsearch.cgi and plugins/mt-xsearch.pl a little. These are for multilingual support and bug fix, and they may be included in the future releases of MT-Plus/MT-XSearch.
    --- mt-xsearch.cgi.bak	Fri Aug 27 12:06:24 2004
    +++ mt-xsearch.cgi	Sat Jun 11 02:58:33 2005
    @@ -41,7 +41,8 @@
         $ctx->stash('CGI',$q);
         my $out = $tmpl->build($ctx)
             or die "Building search template failed: ".$tmpl->errstr;
    -    print $q->header.$out;
    +    my $charset = $mt->{cfg}->PublishCharset;
    +    print $q->header(-charset=>$charset).$out;
     };
     if ($@) {
         print "Content-Type: text/html\n\n";
    
    --- plugins/mt-xsearch.pl.bak	Sat May 14 06:01:19 2005
    +++ plugins/mt-xsearch.pl	Sat Jun 11 00:31:39 2005
    @@ -63,7 +63,7 @@
         my $pages = $limit ? ($count-($count % $limit)) / $limit : 1;
         $pages += ($limit && $count % $limit) ? 1 : 0;
         my $offset = $xsearch->args->{offset} || 0;
    -    my $current = $offset / $limit + 1;
    +    my $current = $limit ? ($offset / $limit + 1) : 1;
         $ctx->stash('MT::XSearch::current_page',$current);
         $ctx->stash('MT::XSearch::pages',$pages);
         my $builder = $ctx->stash('builder');
    
  3. Create a template module named "XSearch Tagwire" (other name is not permitted):
    <html>
    <body>
    <form method="get" action="<$MTCGIPath$>mt-xsearch.cgi">
    <input type="hidden" name="blog_id" value="<$MTBlogID$>" />
    <input type="hidden" name="search_key" value="Tagwire" />
    <label for="search" accesskey="4">Search this site:</label>
    <input id="search" name="search" size="20" value="<$MTSearchString decode_url="1" encode_html="1"$>" />
    <input type="submit" value="Search" />
    </form>
     
    <MTSearchResults>
    <MTSearchHeader>
    Results found: <$MTSearchResultCount$> 
    <ol>
    </MTSearchHeader>
    <li><a href="<$MTEntryLink$>"><$MTEntryTitle$></a></li>
    <MTSearchFooter>
    </ol>
    <p>Searched on: <em><$MTSearchString decode_url="1"$></em></p>
    </MTSearchFooter>
    </MTSearchResults>
    <MTNoSearch><p>No search performed.</p></MTNoSearch>
    <MTNoSearchResults><p>Nothing found.</p></MTNoSearchResults>
     
    </body>
    </html>
    
    This template can be customized as you like.

Usage

Tag Search Form:

To create tag search form, simply add the following code to your index/archive templates:

<form method="get" action="<$MTCGIPath$>mt-xsearch.cgi">
<input type="hidden" name="blog_id" value="<$MTBlogID$>" />
<input type="hidden" name="search_key" value="Tagwire" />
<label for="search" accesskey="4">Search this site:</label>
<input id="search" name="search" size="20" value="<$MTSearchString decode_url="1" encode_html="1"$>" />
<input type="submit" value="Search" />
</form>

Tag Archive Links:

To generate directly tag archive links instead of forms, you can write as like follows:

<MTEntryTags glue=", ">
<a href="<$MTCGIPath$>mt-xsearch.cgi?blog_id=<$MTBlogID$>&search_key=Tagwire&search=<$MTTag encode_url="1"$>"><$MTTag$></a>
</MTEntryTags>

Smarter Tag Archive Links:

To realize smarter representation of the tag archive URL such as "http://my.host.tld/tag/Tag", add the following to your .htaccess:

RewriteEngine on
RewriteRule ^tag/(.*) /mt/mt-xsearch.cgi?blog_id=1&search_key=Tagwire&search=$1 [QSA,L]

Once the above setting complete, you need only to write as follows:

<MTEntryTags glue=", ">
<a href="<$MTBlogURL$>tag/<$MTTag encode_url="1"$>"><$MTTag$></a>
</MTEntryTags>

Options

The following options can be added as hidden input fields of the Tag Search Form or as query strings of the Tag Archive Links:

blog_id (REQUIRED)
A blog ID.
search_key (REQUIRED)
Must be set to "Tagwire" for tag search.
search (REQUIRED)
An URL-encoded search string. You can specify one or more tags separated by the delimiter character. When you specify two or more tags, MT-XSearch generates the list of entries with all these tags. And the default delimiter is a whitespace, which should be represented as "%20" or "+" in a URL-encoded string. So if you specify "movable+type" as the search string and omit the delimiter option, MT-XSearch generates the list of entries having both of "movable" and "type" tags.
delimiter
Sets the delimiter character for the search string. For example, if you set "movable,type" as the search string and "," as the delimiter, MT-XSearch generates the list of entries having both of "movable" and "type" tags.
case_sensitive [0 | 1]
Chooses whether the plugin treats tags as the case-sensitive manner or not. The default is case_sensitive="1".
sort_order [ascend | descend]
Chooses the sorting order. The default sort_order is "descend".

Advanced Issue: CGI::Cache enabled MT-XSearch

The above mentioned method is not so a fast solution. Because, everytime you access tag archives, it invokes CGI process and retrieves the tag information from the DB engine and then generates the result page.

But, generally saying, the tag information is not so often added or modified. In short, by caching pages and suppressing page transfers, we make it possible to reduce the overhead of dynamic tag archives effectively.

Based on this observation, I introduce a speedup method for Tagwire/MT-XSearch, with enabling CGI::Cache module. CGI::Cache enabled MT-XSearch can cache the result pages for a fixed period (e.g, 1 hour) and return the pages to the clients with the Last-Modified HTTP Header (the cached time may be preferable).

To realize CGI::Cache enabled MT-XSearch, install CGI::Cache module from CPAN, and modify mt-xsearch.cgi as follows:

--- mt-xsearch.cgi.bak	Fri Aug 27 12:06:24 2004
+++ mt-xsearch.cgi	Sat Jun 11 03:44:32 2005
@@ -17,6 +17,7 @@
 }
 
 use CGI;
+use CGI::Cache;
 use MT;
 use MT::ConfigMgr;
 use MT::Template;
@@ -27,10 +28,13 @@
     my $mt = MT->new( Config => $MT_DIR . 'mt.cfg', Directory => $MT_DIR )
         or die MT->errstr;
     my $q = new CGI;
+    CGI::Cache::setup({ cache_options => { cache_root => './cache', default_expires_in => 3600 } });
     my $blog_id = $q->param('blog_id') or
         die "Missing parameter blog_id";
     my $key = $q->param('search_key') or
         die "Missing parameter key";
+    CGI::Cache::set_key($q->Vars);
+    CGI::Cache::start() or exit;
     my $search = MT::XSearch->execute($q);
     my $tmpl = MT::Template->load( { 
                         name=>'XSearch '.$key, 
@@ -41,7 +45,14 @@
     $ctx->stash('CGI',$q);
     my $out = $tmpl->build($ctx)
         or die "Building search template failed: ".$tmpl->errstr;
-    print $q->header.$out;
+    my $charset = $mt->{cfg}->PublishCharset;
+    my @m = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+    my @w = qw(Sun Mon Tue Wed Thu Fri Sat);
+    my ($sec, $min, $hour, $mday, $mon, $year, $wday) = gmtime(time);
+    my $now = sprintf("%3s, %02d %3s %04d %02d:%02d:%02d GMT",
+        $w[$wday], $mday, $m[$mon], $year+1900, $hour, $min, $sec);
+    print $q->header(-charset=>$charset,-Last_Modified=>$now).$out;
+    CGI::Cache::stop();
 };
 if ($@) {
     print "Content-Type: text/html\n\n";

Reds mean the cache directory and the cache lifetime by seconds. In the above example, the directory is "./cache" and the lifetime is 3600 secs(1 hour).

June 24, 2005

Tagwire Plugin

A plugin for handling entries' keywords as "tag" in Movable Type.

tagwire.zip

0.10 (2005.03.19):
  • Initial release of AllKeywords Plugin
0.20 (2005.06.24):
  • Change plugin name to "Tagwire Plugin" and released with English document
0.21 (2005.06.25):
  • Add encode_urlplus filter and its document
0.22 (2005.06.30):
  • Minor bug fix.
  • Support newer plugin interface of MT3.X?
0.23 (2005.07.06):
  • Fix for the duplicated PluginData problem when using with SQLite.
0.24 (2005.07.13):
  • Add MTTagDate for displaying the last date the tag added.
  • Add MTRelatedTags for listing tags related to the current tag.
  • Add MTXSearchTags for listing tags specified by MT-XSearch's argument.
  • Modify README.txt for these changes.
  • Add tagwire-pdcleaner.cgi which is a simple tool for cleaning PluginData for tagwire.
0.24a (2005.07.14):
  • Fix minor bugs.
0.25 (2005.07.20):
  • More bug fixes.

Tagwire Plugin (aka AllKeywords Plugin) provides an easier way to handling "tags" in Movable Type. The keywords fields of any entries are used as "tags".

Unlike Tags and Tagslite Plugin, this plugin has the following features:

  • Multilingual Tagging:
  • Flexible Tag Format:
  • MT's categories are Untaint:
    Tagwire Plugin uses only "keywords" field of entries, so users can use "categories" just as themselves separately.
  • Full-fledged support for tag handling:
    Tagwire provides various functions including blog-wide/entry-wise tag listing and counting, listing entries that have user-specified tags, and listing related entries.
  • Moderate Rebuilding Time:
    Tagwire realizes tag indexing without support of your DB engines. It often leads to performance degradation especially for rebuilding, but Tagwire employs PluginData and Request Cache effectively and achieves enough speed.
  • Dynamic Tag Archiving coupled with MT-XSearch:
    Though Tagwire generates no static tag archives, it couples with Tim Appnel's MT-XSearch and supports Dynamic Tag Archiving.

Installation

To install this plugin, upload or copy 'tagwire.pl' into your Movable Type's plugin directory.

After proper installation, you will see a new "Tagwire Plugin" listed on the Main Menu of your Movable Type.

Tag Format

Tagwire automatically interprets the string which inputs the keywords field of the entry, as "tags". Any strings enclosed by brackets([,]), sigle and double quotes are parsed as a single tag. And any strings separated by delimiter characters(;,|) are parsed as a single tag. And if no delimiter characters appear, whitespaces work as delimter.

The following all examples are recognized as two tags "Movable Type" and "Plugin":

"Movable Type" Plugin
Movable Type, Plugin
Movable Type|Plugin|
[Movable Type][Plugin]

And the following example is parsed as two tags "MT" and "Plugin":

MT Plugin

From here I will explain MT container/varable tags introduced by Tagwire Plugin.

MTTags Container

A container tag for listing all available "tags" for the current blog.

Option(s):

glue="glue"
Separates each of the tags with a string specified by "glue". This is useful when you wish to separate the tag names with a comma, for example.
sort_by="tag|tag-case|count"
Chooses sorting method for tags. "tag" means case-insensitive alphabetical sort, "tag-case" means case-sensitive alphabetical sort, "count" means sort by tag's count. Default setting is "tag".
sort_order="ascend|descend"
Chooses sorting order. The default sort_order is "ascend".
lastn="N"
Shows only last N tags. By default, all tags are displayed.
case_sensitive="0|1"
Chooses whether the plugin treats tags as the case-sensitive manner or not. In the case-sensitive manner, the plugin recognizes "Apple" and "appLE" as two different tags, on the other hand, it recognizes them as a same tag in case-insensitive manner. The default is case_sensitive="1".

Available tags in this container:

<$MTTag$>
Shows the tag.
<$MTTagCount$>
Shows the appearance count of the tag.
<$MTTagDate$>
Shows the last date the tag added. As well as MTEntryDate, "format", "language", and "utc" options are avaiable.
<$MTTagsTotal$>
Shows the count of all tags.
<$MTTagsTotalSum$>
Shows the total appearance count of all tags.

Example:

To list 10 most appeared tags and link them to Technorati:

<ul>
<MTTags sort_by="count" sort_order="descend" lastn="10">
<li><a href="http://www.technorati.com/tag/<$MTTag encode_url="1"$>"
  rel="TAG" title="TAG:<$MTTag$>"><$MTTag$></a> (<$MTTagCount$>)</li>
</MTTags>
</ul>
<ul>
<li>Total Tags: <$MTTagsTotal$></li>
<li>Total Tags(Sum): <$MTTagsTotalSum$></li>
</ul>

And Ogawa::Memoranda Tags is an example of using MTTags container.

MTEntryTags Container

A container tag for listing tags for an entry. It can only be used in "entry context" which means "the inside of MTEntries" or Individual Archives.

Option(s):

glue="glue"
Separates each of the tags with a string specified by "glue". This is useful when you wish to separate the tag names with a comma, for example.
case_sensitive="0|1"
Chooses whether the plugin treats tags as the case-sensitive manner or not. The default is case_sensitive="1".

Available tags in this container:

<$MTTag$>
Shows the tag.

Example:

To list tags of the entries and link them to Technorati:

<MTEntries lastn="10">
<h2><$MTEntryTitle$></h2>
 
<ul>
<MTEntryTags>
<li><a href="http://www.technorati.com/tag/<$MTTag encode_url="1"$>"
  rel="TAG" title="TAG:<$MTTag$>"><$MTTag$></a></li>
</MTEntryTags>
</ul>
 
<$MTEntryBody$>
</MTEntries>

MTRelatedTags Container

A container tag for listing tags *related* to the current tag. The relationship between tags is defined by how many common *entries* includes them. This container can only be used in "tag context" which means the inside of MTTags, MTEntryTags, or MTXSearchTags.

Option(s):

glue="glue"
Separates each of the tags with a string specified by "glue". This is useful when you wish to separate the tag names with a comma, for example.
sort_by="tag|tag-case|count"
Chooses sorting method for tags. "tag" means case-insensitive alphabetical sort, "tag-case" means case-sensitive alphabetical sort, "count" means sort by tag's count. Default setting is "tag".
sort_order="ascend|descend"
Chooses sorting order. The default sort_order is "ascend".
lastn="N"
Shows only last N tags. By default, all tags are displayed.
case_sensitive="0|1"
Chooses whether the plugin treats tags as the case-sensitive manner or not. The default is case_sensitive="1".

Available tags in this container:

<$MTTag$>
Shows the tag.
<$MTTagCount$>
Shows the appearance count of the tag.
<$MTTagDate$>
Shows the last date the tag added. As well as MTEntryDate, "format", "language", and "utc" options are avaiable for this tag.

Example:

To list tags of the entries and their related tags, and to link all of them to Technorati:

<MTEntries lastn="10">
<h2><$MTEntryTitle$></h2>
 
<ul>
  <MTEntryTags>
  <li><a href="http://www.technorati.com/tag/<$MTTag encode_url="1"$>"
    rel="TAG" title="TAG:<$MTTag$>"><$MTTag$></a>
    <ul>
      <MTRelatedTags>
      <li><a href="http://www.technorati.com/tag/<$MTTag encode_url="1"$>"
        rel="TAG" title="TAG:<$MTTag$>"><$MTTag$></a></li>
      </MTRelatedTags>
    </ul>
  </li>
  </MTEntryTags>
</ul>
 
<$MTEntryBody$>
</MTEntries>

MTEntriesWithTags Container

A container tag for listing entries which have user-specified tags.

Option(s):

tags="tag-list"
User-specified tags for listing. It can be a single tag or multiple tags separated by a delimiter.
delimiter="delim"
Sets the delimiter character for the abovementioned "tags" option. The default delimiter is whitespaces.
case_sensitive="0|1"
Chooses whether the plugin treats tags as the case-sensitive manner or not. The default is case_sensitive="1".
sort_order="ascend|descend"
Chooses sorting order. The default sort_order is "descend".
lastn="N"
Shows only last N entries. By default, all entries including user-specified tags are displayed.

Available tags in this container:

As like MTEntries container, various tags including MTEntry* are available inside this container.

Example:

To list all entries which have "movable" and "type" tag:

<MTEntriesWithTags tags="movable type">
<h2><a href="<$MTEntryPermalink$>"><$MTEntryTitle$></a></h2>
 
<$MTEntryBody$>
<$MTEntryExtended$>
 
</MTEntriesWithTags>

MTMostRelatedEntries Container

A container tag for listing entries *related* to the current entry. The relationship between entries is defined by how many common *tags* they have. This container can only be used in "entry context" which means "the inside of MTEntries" or Individual Archives.

Option(s):

case_sensitive="0|1"
Chooses whether the plugin treats tags as the case-sensitive manner or not. The default is case_sensitive="1".
sort_order="ascend|descend"
Chooses sorting order. The default sort_order is "descend".
lastn="N"
Shows only N most related entries. By default, all related entries are displayed.

Available tags in this container:

As like MTEntries container, various tags including MTEntry* are available inside this container.

Example:

To list 10 most related entries for the current entry:

<MTEntries>
<h2><a href="<$MTEntryPermalink$>"><$MTEntryTitle$></a></h2>
<$MTEntryBody$>
 
<ul>
<MTMostRelatedEntries lastn="10">
<li><a href="<$MTEntryPermalink$>"><$MTEntryTitle$></a></li>
</MTMostRelatedEntries>
</ul>
 
</MTEntries>

MTXSearchTags Container

A container tag for listing the query string of MT-XSearch as tags. It can only be used in "MT-XSearch" Template.

Option(s):

glue="glue"
Separates each of the tags with a string specified by "glue". This is useful when you wish to separate the tag names with a comma, for example.

Available tags in this container:

<$MTTag$>
Shows the tag.

Example:

To list tags given by the query string of MT-XSearch and their related tags, and to link them to Technorati:

<MTXSearchTags> <h2><$MTTag$></h2> <ul> <MTRelatedTags> <li><a href="http://www.technorati.com/tag/<$MTTag encode_url="1"$>" rel="TAG" title="TAG:<$MTTag$>"><$MTTag$></a></li> </MTRelatedTags> </ul> </MTXSearchTags>

encode_urlplus Filter

A variation of encode_url filter. First this filter converts whitespaces of the target string into '+'s, and then converts it into URL-safe string.

Example:

<ul>
  <MTEntryTags>
  <li><a href="http://www.technorati.com/tag/<$MTTag encode_urlplus="1"$>"
    rel="TAG" title="TAG:<$MTTag$>"><$MTTag$></a></li>
  </MTEntryTags>
</ul>

Contribution

This software is available at no charge. Please consider making a contribution to support our continuous development if possible.

SEE ALSO

Ogawa::Hacks: Dynamic Tag Archiving with Tagwire and MT-XSearch

Japanese document has not been provided yet.

LICENSE

This code is released under the Artistic License. The terms of the Artistic License are described at http://www.perl.com/language/misc/Artistic.html.

AUTHOR & COPYRIGHT

Copyright 2005, Hirotaka Ogawa (hirotaka.ogawa at gmail.com)