Recently in code Category

Previously, I wrote about how to add Edit Links to Entry and Page templates.

This guide uses the mt_user cookie set when logging into Movable Type to condition content (links to screens in MT) on entries and pages published by Movable Type.

Also provides an option to use a non-MT cookie to do the same thing.

Advanced Breadcrumb Navigation

| 2 Comments

I needed a flexible system to append crumbs to an array… potentially remove items from the array, then output the array on the final template as a breadcrumb navigation. This was my solution using Movable Type template tags.

I used the Var function attribute to push, shift, unshift items on an array variable and then used mt:loop to output the items in the array into a list of breadcrumbs. This was used in a Movable Type install with 20 blogs.

In a “global config” template module (included in the first line of every template) set the top level of the breadcrumbs:

<mt:SetVarBlock name="breadcrumbs" function="push">
    <a href="<$mt:BlogURL$>"><$mt:BlogName$></a>
</mt:SetVarBlock>

On an Entry archive template, add crumb to Category archive, Daily archive, and then add the Entry title:

<mt:IfArchiveTypeEnabled archive_type="Category">
    <mt:If tag="EntryCategory">
        <mt:SetVarBlock name="breadcrumbs" function="push">
            <a href="<$mt:EntryLink type="Category"$>"><$mt:EntryCategory$></a>
        </mt:SetVarBlock>
    </mt:If>
</mt:IfArchiveTypeEnabled>
<mt:IfArchiveTypeEnabled archive_type="Daily">
    <mt:SetVarBlock name="breadcrumbs" function="push">
    <a href="<$mt:EntryLink type="Daily"$>"><$mt:EntryDate format="%B %d, %Y"$></a>
    </mt:SetVarBlock>
</mt:IfArchiveTypeEnabled>
<mt:SetVarBlock name="breadcrumbs" function="push">
    <$mt:EntryTitle$>
</mt:SetVarBlock>

On an Entry Listing archive template for a blog with Monthly archives enabled, add crumb for Archive Index page and for the current page.

<mt:IfArchiveTypeEnabled archive_type="Monthly">
    <mt:SetVarBlock name="breadcrumbs" function="push">
        <a href="<$mt:Link template="archive_index"$>">Archives</a>
    </mt:SetVarBlock>
</mt:IfArchiveTypeEnabled>
<mt:SetVarBlock name="breadcrumbs" function="push">
    <$mt:ArchiveTitle$>
</mt:SetVarBlock>

In index templates (such as the Archive Index), add current page as the last crumb to the end of the array:

<mt:SetVarBlock name="breadcrumbs" function="push">
    Archives
</mt:SetVarBlock>

To replace the first crumb if the BlogID is “1”, use the “shift” function to remove and the “unshift” function to prepend a crumb (I added this to a “blog config” template module which is included just after the “global config” template module in the first two lines of each template):

<mt:If tag="BlogID" eq="1">
    <$mt:GetVar name="breadcrumbs" function="shift" setvar="removed_value"$>
    <mt:SetVarBlock name="breadcrumbs" function="unshift">
        <a href="/foo/">Foo</a>
    </mt:SetVarBlock>
</mt:If>

On the main index template for each blog I didn’t want to show breadcrumbs, so I’d set this boolean value:

<$mt:Var name="show_breadcrumbs" value="0"$>

To output the crumbs, use the mt:loop tag:

<mt:If name="show_breadcrumbs">
<div class="breadcrumbs">
    <ol>
    <mt:Loop name="breadcrumbs">
        <li<mt:If name="__last__"> class="last"</mt:If>><$mt:Var name="__value__" trim="1"$></li>
    </mt:Loop>
    </ol>
</div>
</mt:If>

Heading Anchors via jQuery

| No Comments

Markdown is my text markup of choice… but one thing it doesn’t have is the ability to add classes or id elements to any of the html. Because what I wanted to do wasn’t critical without JavaScript enabled, I used jQuery to implement the same solution that the HeadingAnchors plugin does… but in JavaScript. This also creates a navigation menu using all the H2 tag heading text.

Code

Requires dirify function and dirify_table.

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">
    google.load("jquery", "1.3.2");

    // When the document is ready for javascript fun...
    google.setOnLoadCallback(function() {
        // initalize variable
        var $menu = '';
        // select all the page headings, assuming that h1 is only used once (for the main page title)
        $("h2, h3, h4, h5, h6").each(function () {
            // Dirity heading text to create the anchor
            $anchor = dirify($(this).text());
            // If heading is h2, add list item to menu
            if ("H2" == $(this)[0].nodeName) {
                $menu += '<li><a href="#' + $anchor + '">' + $(this).text() + '</a></li>';
            };
            // Append anchor to heading
            $(this).append('<a title="Link to this section" class="anchor" href="#' + $anchor + '"> ¶</a>').attr('id', $anchor);
        });
        // Add menu to sidebar
        $('.sidebar').prepend('<div class="tag-menu"><h3>Tag Types</h3><ul>' + $menu + '</ul></div>');
    });
</script>

Dirify Function in Javascript

| 2 Comments

Was messing around with JavaScript and jQuery and needed a dirify function… found one in the Movable Type core JavaScript file. Thought I’d post it here for the value of all vis-à-vis Google as the first results weren’t as useful as I hope this is.

And I added two features to the default code in MT:

  1. I like to use ampersands in my titles because they look spiffy, so I added ampersands to the dirify_table such that “&” is now dirified to “and”.

    dirify("Me & Ed's Pizza");
    // result: me_and_eds_pizza
    
  2. Sometimes you want dashes instead of underscores. This dirify function takes a second parameter that is the default separator.

    dirify("What the Duck?", "-");
    // result: what-the-duck
    

Examples

Place these examples in html <body>:

<script type="text/javascript">
var foo = "You & Me";
var bar = dirify(foo);
document.write('"' + foo + '" dirified is "' + bar + '"');

document.write('<br />');

// dirify on on one line
document.write(dirify("Me & Ed's Pizza"));

document.write('<br />');

// Use a dash
document.write(dirify("¿Is Open-Source is the bomb'd?", "-"));
</script>

Dirify Code

Place the dirify function and the dirify_table in the <head> of the html page… or in the referenced JavaScript file:

<script type="text/javascript">
function dirify (s,d) {
    if (!d)
        d = "_";
    s = s.replace(/<[^>]+>/g, '');
    for (var p in dirify_table)
        if (s.indexOf(p) != -1)
            s = s.replace(new RegExp(p, "g"), dirify_table[p]);
    s = s.toLowerCase();
    s = s.replace(/&[^;\s]+;/g, '');
    s = s.replace(/[^-a-z0-9_ ]/g, '');
    s = s.replace(/\s+/g, '_');
    s = s.replace(/_+$/, '');
    s = s.replace(/_+/g, d);
    return s;
}
var dirify_table = {
    "\u0026": 'and',  // ampersand
    "\u00C0": 'A',    // A`
    "\u00E0": 'a',    // a`
    "\u00C1": 'A',    // A'
    "\u00E1": 'a',    // a'
    "\u00C2": 'A',    // A^
    "\u00E2": 'a',    // a^
    "\u0102": 'A',    // latin capital letter a with breve
    "\u0103": 'a',    // latin small letter a with breve
    "\u00C6": 'AE',   // latin capital letter AE
    "\u00E6": 'ae',   // latin small letter ae
    "\u00C5": 'A',    // latin capital letter a with ring above
    "\u00E5": 'a',    // latin small letter a with ring above
    "\u0100": 'A',    // latin capital letter a with macron
    "\u0101": 'a',    // latin small letter a with macron
    "\u0104": 'A',    // latin capital letter a with ogonek
    "\u0105": 'a',    // latin small letter a with ogonek
    "\u00C4": 'A',    // A:
    "\u00E4": 'a',    // a:
    "\u00C3": 'A',    // A~
    "\u00E3": 'a',    // a~
    "\u00C8": 'E',    // E`
    "\u00E8": 'e',    // e`
    "\u00C9": 'E',    // E'
    "\u00E9": 'e',    // e'
    "\u00CA": 'E',    // E^
    "\u00EA": 'e',    // e^
    "\u00CB": 'E',    // E:
    "\u00EB": 'e',    // e:
    "\u0112": 'E',    // latin capital letter e with macron
    "\u0113": 'e',    // latin small letter e with macron
    "\u0118": 'E',    // latin capital letter e with ogonek
    "\u0119": 'e',    // latin small letter e with ogonek
    "\u011A": 'E',    // latin capital letter e with caron
    "\u011B": 'e',    // latin small letter e with caron
    "\u0114": 'E',    // latin capital letter e with breve
    "\u0115": 'e',    // latin small letter e with breve
    "\u0116": 'E',    // latin capital letter e with dot above
    "\u0117": 'e',    // latin small letter e with dot above
    "\u00CC": 'I',    // I`
    "\u00EC": 'i',    // i`
    "\u00CD": 'I',    // I'
    "\u00ED": 'i',    // i'
    "\u00CE": 'I',    // I^
    "\u00EE": 'i',    // i^
    "\u00CF": 'I',    // I:
    "\u00EF": 'i',    // i:
    "\u012A": 'I',    // latin capital letter i with macron
    "\u012B": 'i',    // latin small letter i with macron
    "\u0128": 'I',    // latin capital letter i with tilde
    "\u0129": 'i',    // latin small letter i with tilde
    "\u012C": 'I',    // latin capital letter i with breve
    "\u012D": 'i',    // latin small letter i with breve
    "\u012E": 'I',    // latin capital letter i with ogonek
    "\u012F": 'i',    // latin small letter i with ogonek
    "\u0130": 'I',    // latin capital letter with dot above
    "\u0131": 'i',    // latin small letter dotless i
    "\u0132": 'IJ',   // latin capital ligature ij
    "\u0133": 'ij',   // latin small ligature ij
    "\u0134": 'J',    // latin capital letter j with circumflex
    "\u0135": 'j',    // latin small letter j with circumflex
    "\u0136": 'K',    // latin capital letter k with cedilla
    "\u0137": 'k',    // latin small letter k with cedilla
    "\u0138": 'k',    // latin small letter kra
    "\u0141": 'L',    // latin capital letter l with stroke
    "\u0142": 'l',    // latin small letter l with stroke
    "\u013D": 'L',    // latin capital letter l with caron
    "\u013E": 'l',    // latin small letter l with caron
    "\u0139": 'L',    // latin capital letter l with acute
    "\u013A": 'l',    // latin small letter l with acute
    "\u013B": 'L',    // latin capital letter l with cedilla
    "\u013C": 'l',    // latin small letter l with cedilla
    "\u013F": 'l',    // latin capital letter l with middle dot
    "\u0140": 'l',    // latin small letter l with middle dot
    "\u00D2": 'O',    // O`
    "\u00F2": 'o',    // o`
    "\u00D3": 'O',    // O'
    "\u00F3": 'o',    // o'
    "\u00D4": 'O',    // O^
    "\u00F4": 'o',    // o^
    "\u00D6": 'O',    // O:
    "\u00F6": 'o',    // o:
    "\u00D5": 'O',    // O~
    "\u00F5": 'o',    // o~
    "\u00D8": 'O',    // O/
    "\u00F8": 'o',    // o/
    "\u014C": 'O',    // latin capital letter o with macron
    "\u014D": 'o',    // latin small letter o with macron
    "\u0150": 'O',    // latin capital letter o with double acute
    "\u0151": 'o',    // latin small letter o with double acute
    "\u014E": 'O',    // latin capital letter o with breve
    "\u014F": 'o',    // latin small letter o with breve
    "\u0152": 'OE',   // latin capital ligature oe
    "\u0153": 'oe',   // latin small ligature oe
    "\u0154": 'R',    // latin capital letter r with acute
    "\u0155": 'r',    // latin small letter r with acute
    "\u0158": 'R',    // latin capital letter r with caron
    "\u0159": 'r',    // latin small letter r with caron
    "\u0156": 'R',    // latin capital letter r with cedilla
    "\u0157": 'r',    // latin small letter r with cedilla
    "\u00D9": 'U',    // U`
    "\u00F9": 'u',    // u`
    "\u00DA": 'U',    // U'
    "\u00FA": 'u',    // u'
    "\u00DB": 'U',    // U^
    "\u00FB": 'u',    // u^
    "\u00DC": 'U',    // U:
    "\u00FC": 'u',    // u:
    "\u016A": 'U',    // latin capital letter u with macron
    "\u016B": 'u',    // latin small letter u with macron
    "\u016E": 'U',    // latin capital letter u with ring above
    "\u016F": 'u',    // latin small letter u with ring above
    "\u0170": 'U',    // latin capital letter u with double acute
    "\u0171": 'u',    // latin small letter u with double acute
    "\u016C": 'U',    // latin capital letter u with breve
    "\u016D": 'u',    // latin small letter u with breve
    "\u0168": 'U',    // latin capital letter u with tilde
    "\u0169": 'u',    // latin small letter u with tilde
    "\u0172": 'U',    // latin capital letter u with ogonek
    "\u0173": 'u',    // latin small letter u with ogonek
    "\u00C7": 'C',    // ,C
    "\u00E7": 'c',    // ,c
    "\u0106": 'C',    // latin capital letter c with acute
    "\u0107": 'c',    // latin small letter c with acute
    "\u010C": 'C',    // latin capital letter c with caron
    "\u010D": 'c',    // latin small letter c with caron
    "\u0108": 'C',    // latin capital letter c with circumflex
    "\u0109": 'c',    // latin small letter c with circumflex
    "\u010A": 'C',    // latin capital letter c with dot above
    "\u010B": 'c',    // latin small letter c with dot above
    "\u010E": 'D',    // latin capital letter d with caron
    "\u010F": 'd',    // latin small letter d with caron
    "\u0110": 'D',    // latin capital letter d with stroke
    "\u0111": 'd',    // latin small letter d with stroke
    "\u00D1": 'N',    // N~
    "\u00F1": 'n',    // n~
    "\u0143": 'N',    // latin capital letter n with acute
    "\u0144": 'n',    // latin small letter n with acute
    "\u0147": 'N',    // latin capital letter n with caron
    "\u0148": 'n',    // latin small letter n with caron
    "\u0145": 'N',    // latin capital letter n with cedilla
    "\u0146": 'n',    // latin small letter n with cedilla
    "\u0149": 'n',    // latin small letter n preceded by apostrophe
    "\u014A": 'N',    // latin capital letter eng
    "\u014B": 'n',    // latin small letter eng
    "\u00DF": 'ss',   // double-s
    "\u015A": 'S',    // latin capital letter s with acute
    "\u015B": 's',    // latin small letter s with acute
    "\u0160": 'S',    // latin capital letter s with caron
    "\u0161": 's',    // latin small letter s with caron
    "\u015E": 'S',    // latin capital letter s with cedilla
    "\u015F": 's',    // latin small letter s with cedilla
    "\u015C": 'S',    // latin capital letter s with circumflex
    "\u015D": 's',    // latin small letter s with circumflex
    "\u0218": 'S',    // latin capital letter s with comma below
    "\u0219": 's',    // latin small letter s with comma below
    "\u0164": 'T',    // latin capital letter t with caron
    "\u0165": 't',    // latin small letter t with caron
    "\u0162": 'T',    // latin capital letter t with cedilla
    "\u0163": 't',    // latin small letter t with cedilla
    "\u0166": 'T',    // latin capital letter t with stroke
    "\u0167": 't',    // latin small letter t with stroke
    "\u021A": 'T',    // latin capital letter t with comma below
    "\u021B": 't',    // latin small letter t with comma below
    "\u0192": 'f',    // latin small letter f with hook
    "\u011C": 'G',    // latin capital letter g with circumflex
    "\u011D": 'g',    // latin small letter g with circumflex
    "\u011E": 'G',    // latin capital letter g with breve
    "\u011F": 'g',    // latin small letter g with breve
    "\u0120": 'G',    // latin capital letter g with dot above
    "\u0121": 'g',    // latin small letter g with dot above
    "\u0122": 'G',    // latin capital letter g with cedilla
    "\u0123": 'g',    // latin small letter g with cedilla
    "\u0124": 'H',    // latin capital letter h with circumflex
    "\u0125": 'h',    // latin small letter h with circumflex
    "\u0126": 'H',    // latin capital letter h with stroke
    "\u0127": 'h',    // latin small letter h with stroke
    "\u0174": 'W',    // latin capital letter w with circumflex
    "\u0175": 'w',    // latin small letter w with circumflex
    "\u00DD": 'Y',    // latin capital letter y with acute
    "\u00FD": 'y',    // latin small letter y with acute
    "\u0178": 'Y',    // latin capital letter y with diaeresis
    "\u00FF": 'y',    // latin small letter y with diaeresis
    "\u0176": 'Y',    // latin capital letter y with circumflex
    "\u0177": 'y',    // latin small letter y with circumflex
    "\u017D": 'Z',    // latin capital letter z with caron
    "\u017E": 'z',    // latin small letter z with caron
    "\u017B": 'Z',    // latin capital letter z with dot above
    "\u017C": 'z',    // latin small letter z with dot above
    "\u0179": 'Z',    // latin capital letter z with acute
    "\u017A": 'z'     // latin small letter z with acute
};
</script>

This code should display entries with publish date of today or in the future… didn’t test it though, just posting it here for future reference.

<$mt:Date format="%Y%m%d" setvar="today"$>
<mt:Entries>
   <$mt:EntryDate format="%Y%m%d" setvar="entry_date"$>
   <mt:If name="today" lte="$entry_date" >
       <p><a href="<$mt:EntryLink$>"><$mt:EntryTitle$></a><br/><$mt:EntryDate$></p>
   </mt:If>
</mt:Entries>

Note: because MT publishes static files, “today” is the date that the template is published. Bob the Rebuilder or other similar plugins can trigger rebuilds to ensure this content is fresh daily. Otherwise it may be wise to use a scripting language (php, js, etc) to hide non-desired content.

Change “lte” to “gte” and then the script will display entries with dates before today.

Publish Entries in Two Columns

| No Comments

Was working on a project for a major television network who needed to place the output of the <mt:Entries> loop into two columns. Because they wanted to have the most recent entries at the top of the columns I suggested the following.

Start by appending each entry into either the left or right column based upon the odd and even template loop meta variables:

<mt:Entries>
    <mt:If name="__odd__">
        <mt:SetVarBlock name="odd_column" append="1">
            <$mt:Include module="Entry Summary"$>
        </mt:SetVarBlock>
    </mt:If>
    <mt:If name="__even__">
        <mt:SetVarBlock name="even_column" append="1">
            <$mt:Include module="Entry Summary"$>
        </mt:SetVarBlock>
    </mt:If>
</mt:Entries>

Then output the two variables into your columns:

<div id="odd-column">
    <$mt:Var name="odd_column"$>
</div>
<div id="even-column">
    <$mt:Var name="even_column"$>
</div>

I love simple elegant solutions.

Other Object Loops in Two Columns

This solution also works for other loops such as categories, comments, pages, assets, trackbacks, tags, users.

Here’s an example to list categories in two columns (plus the last three entries for each category).

Use SetVarTemplate to define mtml structure which is rendered where the template is used later:

<mt:SetVarTemplate name="category_summary">
    <div>
        <h3><a href="<$mt:CategoryArchiveLink$>"><$mt:CategoryLabel$></a></h3>
        <ul>
        <mt:Entries lastn="3">
            <li><a href="<$mt:EntryPermalink$>"><$mt:EntryTitle$></a></li>
        </mt:Entries>
        </ul>
    </div>
</mt:SetVarTemplate>

Separate the category output into two variables;

<mt:Categories>
    <mt:If name="__odd__">
        <mt:SetVarBlock name="odd_column" append="1">
            <$mt:var name="category_summary"$>
        </mt:SetVarBlock>
    </mt:If>
    <mt:If name="__even__">
        <mt:SetVarBlock name="even_column" append="1">
            <$mt:var name="category_summary"$>
        </mt:SetVarBlock>
    </mt:If>
</mt:Categories>

Output the variables:

<div id="odd-column">
    <$mt:Var name="odd_column"$>
</div>
<div id="even-column">
    <$mt:Var name="even_column"$>
</div>

If you have other examples of this, please blog them and leave a comment or just leave a comment with an example.

Wondering how to customize the Global Templates “Profile Edit Form” or the “Registration Form”? Here’s a few options.

Basic reordering of custom fields is possible on the edit profile page in the MT app. Using the “Reorder Fields” in the “related content” sidebar.

If you want to intersperse custom fields between default fields it’s a little more trickey; by default all built-in fields are output and then all custom fields are output in the order specified on the edit profile page.

The <mt:AuthorCustomFields> tag should be used for this but as of MT4.2 I couldn’t get it to work on the edit profile or registration form, so for now you can use the same loop that currently outputs the custom fields, but limit the output with an <mt:if> tag. Here’s an example of how you can do it…

Description

Use three loops each containing an conditional tag (if or unless) to limit the output of the loop by custom field basename. The first and second loops limit by checking to see if the basename is equal to a value. The third loop outputs all fields except the the two I’ve specified (if fields are duplicated on the form the html will be invalid and only the second input’s value will be saved)

Example

Thi example uses the custom fields “Level” and “Bio”. You may place each loop anywhere in the Edit Profile template’s form.

<mt:Loop name="field_loop">
   <mt:If name="__first__">
   <input type="hidden" name="_type" value="author" id="obj_type" />
   <input type="hidden" name="customfield_beacon" value="1" id="customfield_beacon" />
   </mt:If>
   <mt:if name="basename" eq="level">
   <!-- start-customfield_<$mt:Var name="basename"$> -->
   <mtapp:setting
   id="$field_id"
   label="$name"
   hint="$description"
   shown="$show_field"
   show_hint="$show_hint"
   required="$required">
   <$mt:Var name="field_html"$>
   </mt:App:Setting>
   <!-- end-customfield_<$mt:Var name="basename"$> -->
   </mt:if>
</mt:Loop>

<!-- Default field here -->

<mt:Loop name="field_loop">
   <mt:If name="__first__">
   <input type="hidden" name="_type" value="author" id="obj_type" />
   <input type="hidden" name="customfield_beacon" value="1" id="customfield_beacon" />
   </mt:If>
   <mt:if name="basename" eq="bio">
   <!-- start-customfield_<$mt:Var name="basename"$> -->
   <mtapp:setting
   id="$field_id"
   label="$name"
   hint="$description"
   shown="$show_field"
   show_hint="$show_hint"
   required="$required">
   <$mt:Var name="field_html"$>
   </mt:App:Setting>
   <!-- end-customfield_<$mt:Var name="basename"$> -->
   </mt:if>
</mt:Loop>

<!-- Default field here -->

<mt:Loop name="field_loop">
   <mt:If name="__first__">
   <input type="hidden" name="_type" value="author" id="obj_type" />
   <input type="hidden" name="customfield_beacon" value="1" id="customfield_beacon" />
   </mt:If>
   <mt:unless name="basename" like="(bio|level)">
   <!-- start-customfield_<$mt:Var name="basename"$> -->
   <mtapp:setting
   id="$field_id"
   label="$name"
   hint="$description"
   shown="$show_field"
   show_hint="$show_hint"
   required="$required">
   <$mt:Var name="field_html"$>
   </mt:App:Setting>
   <!-- end-customfield_<$mt:Var name="basename"$> -->
   </mt:unless>
</mt:Loop>

Aaron Vanderzwan offers a similar solution and Elise summarized the solution on LearningMT4.com

MTML Code in Entries or Pages

| No Comments

You want a page with a title and a few paragraphs… but you also want a list of entries as a part of the page. How to do it?

You can use the mteval global modifier, but in the note I added there due to the way MT is optimized to publish, using the mteval global modifier isn’t really an option for most cases.

But there is a better way. The concept is to use an index template but to build the content of the template by combining content from a page and from entries.

Here’s how to create a page for entries tagged as events:

  1. Create a page titled “Events”.

    Add the body text “These are our events”.

    Add the tag “@eventpage”.

  2. Create a few entries with sample event info and add the tag “event”.

  3. Create a new index template called “Events” and set the output file as events.html.

    In the body of the template add this code to list the most recent 10 events tagged with the private tag “@event”:

    <mt:Pages tag="@eventpage" lastn="1">
        <h1><$mt:EntryTitle$></h1>
        <$mt:EntryBody$>
    </mt:Pages>
    <ul>
    <mt:Entries tag="event" lastn="10">
        <li><a href="<$mt:EntryPermalink$>"><$mt:EntryTitle$></a></li>
    </mt:Entries>
    </ul>
    
  4. Save the index template… and then publish the index template.

That’s it. =)

Update: I’ve placed this upgrade script on the wiki, a better place for more people to enjoy and collaborate: wiki.movabletype.org/MT_Shell_Script_Installer

Automation is awesome! This script I use to set up a new MT instance probably saves me 15-20 hours a year, that 2-3 workdays.

I use the below shell script to:

  • checkout target release of MT
  • checkout addons (Packs [ProPack, MTCS, Enterprise], static files, & Tristan Theme)
  • checkout addons themes (Tristan
  • issue make
  • set up a static directory for the release
  • set permissions
  • create a mt-config.cgi file
  • and finally… open the release in TextMate

Advanced Archive Widget

| 4 Comments

Anil ArchivesMie has been blogging every month since 2003 on kokochi.com, the list of 57 monthly archives in her sidebar was getting really long.

Mie asked if I could add an archive widget to her blog’s sidebar like the one Anil sports on dashes.com. After looking at the source HTML I realized that coding it in MTML was gonna be kinda complex. In MTML typically for each year in your archives you’d list all the months, but—because of the way that html tables are built—I’d need to list all the months for each year. For example, I need to list all the Jan archives, then all the Feb archives, etc. etc.)

So I asked Anil for his code. Doh! He admitted he was manually updating it. =P

mie-archives.gifI mentioned this to Brad, and he got so excited that he wrote a rough draft. I added the code to the sidebar on Kokochi.com, but the code wasn’t working. =/ After some testing, I realized that there were a few limitations (year’s w/o posts, and the inability to output the month as a number w/o a “0” prefix on single digit months), but then after consulting with Brad, solutions were found!

Follow the below code and you too can have this advanced archive table widget as seen on kokochi.com and dashes.com/anil.

Update: The blog must be publishing a yearly archive file. This file can have zero content, but needs be published in order for the Yearly ArchiveList block tag to output content. 2008 Oct 7

Update 2: Added a comment regarding default output when a month doesn’t have any entries and added a note about the use of the sprintf attribute to ensure that the month number contain two digits. 2009 Jun 22

The MTML Code

<table id="archives-table">
<mt:ArchiveList type="Yearly" sort_order="ascend">
    <mt:ArchiveListHeader><tr><$mt:ArchiveDate format="%Y" setvar="start_year"$></mt:ArchiveListHeader>
    <th><$mt:ArchiveDate format="'%y"$></th>
    <$mt:ArchiveDate format="%Y" setvar="archive_year"$>
    <$mt:SetVar name="is_posts_year_{$archive_year}" value="1"$>
    <mt:ArchiveList type="Monthly">
        <$mt:ArchiveDate format="%m_%Y" setvar="month_year"$>
        <mt:SetVarBlock name="links_{$month_year}"><a href="<$mt:ArchiveLink$>"><$mt:ArchiveDate format="%b"$></a></mt:SetVarBlock>
    </mt:ArchiveList>
    <mt:ArchiveListFooter></tr><$mt:ArchiveDate format="%Y" setvar="end_year"$></mt:ArchiveListFooter>
</mt:ArchiveList>
<mt:For var="month" from="1" to="12">
    <tr>
    <mt:For var="year" from="$start_year" to="$end_year">
        <mt:If name="is_posts_year_{$year}">
            <mt:SetVarBlock name="month_year"><$mt:Var name="month" sprintf="%02d"$>_<$mt:Var name="year"$></mt:SetVarBlock>
        <td><$mt:Var name="links_{$month_year}" default="&nbsp;"$></td>
        </mt:If>
    </mt:For>
    </tr>
</mt:For>
</table>

The Code Explained

Here’s a play-by-play on the MTML code used to produce this list…

Open the table.

<table id="archives-table">

List yearly archives in an ascending order (2001, 2002, 2003, etc).

<mt:ArchiveList type="Yearly" sort_order="ascend">

Open the TR; set the $start_year variable.

    <mt:ArchiveListHeader><tr><$mt:ArchiveDate format="%Y" setvar="start_year"$></mt:ArchiveListHeader>

Output the two-digit year in a TD.

    <th><$mt:ArchiveDate format="'%y"$></th>

Set $archive_year variable with the current year in the archive list.

    <$mt:ArchiveDate format="%Y" setvar="archive_year"$>

Set $is_posts_year_{$archive_year} using $archive_year variable set above. This is a boolean variable that determines if there are posts within the year in context.

    <$mt:setvar name="is_posts_year_{$archive_year}" value="1"$>

List monthly archives for the current year.

    <mt:ArchiveList type="Monthly">

Set the $month_year variable after removing the “0” from the front of the month if it exists (eg. 1_2005, 2_2005 ... 12_2005).

        <$mt:ArchiveDate format="%m_%Y" regex_replace="/^0/","" setvar="month_year"$>

Set the links_$month_year variable. This variable name is dynamically set using the $month_year var set above. The value will be a month linked to the correct archive (eg. <a href="http://kokochi.com/archives/2004/05/">May</a>).

        <mt:SetVarBlock name="links_{$month_year}"><a href="<$MTArchiveLink$>"><mt:ArchiveDate format="%b"$></a></mt:SetVarBlock>

Close monthly archive list.

    </mt:ArchiveList>

Close the TR. Set the $end_year variable.

    <mt:ArchiveListFooter></tr><$mt:ArchiveDate format="%Y" setvar="end_year"$></mt:ArchiveListFooter>

Close the yearly archive list.

</mt:ArchiveList>

For months 1 thorugh 12 do the following.

<mt:for var="month" from="1" to="12">

Open TR.

    <tr>

Loop through the years between $start_year and $end_year.

    <mt:for var="year" from="$start_year" to="$end_year">

If there is posts in the current year, then continue.

        <mt:if name="is_posts_year_{$year}">

Set the $month_year variable using the $month and $year variables from the above for loops. (sprintf will pad the month number with a zero if the number is not a two digit number.)

            <mt:SetVarBlock name="month_year"><$mt:Var name="month" sprintf="%02d"$>_<$mt:Var name="year"$></mt:SetVarBlock>

Use the $month_year variable to output the links_$month_year set in the archive lists above.

        <td><$mt:var name="links_{$month_year}" default="&nbsp;"$></td>

Close all the MTML loops, MTML conditionals, and HTML tags.

        </mt:if>
    </mt:for>
    </tr>
</mt:for>
</table>

Scaleable?

As long as your sidebar is wide enough, you’ll have room for this widget as it get’s wider. But by hacking the above code it shouldn’t be to hard to break the table into two vertically stacked tables or add some javascript to produce a solution involving hiding/showing of content.

If you hack the above, I’d love to hear about it.

Enjoy.

And thx again to Brad Choate, Mie Yaginuma, and Anil Dash for the instigation and assistance to produce this advanced example of MTML.