nostodnayr.net

The Near Past

Producing a ‘Recent Articles’ block with Movable Type

Several weeks ago I added a ‘Recent Articles’ box to the site. At times I may go a couple of weeks between writing these more focused, ostensibly interesting, pieces of writing. It depends mostly on my free time, but also motivation.

Writing these pieces takes several hours, often across multiple days. If I were to set aside the self-deprecation for a moment, I might even say I like these articles and maybe even be proud of them. (I had better stop before I pass out.)

The consequence of these feelings it that I want them to be noticeable and not get buried under the slightly more frivolous link posts. Thus the ‘Recently’ block was born to highlight some of my recent entries. I decided to put it on two pages: the most recent link entry page and ‘below the fold’ of the home page.

Note: The templates discussed below are only the relevant excerpts and the indentation may a bit wonky, at least for my tastes. Links are provided to view the full templates. Alternatively you can download them all at once.

Generating the content block

The most integral part to this is generating the HTML that builds the block. Because it’s used in two places, I saved it as a template module that saves the generated content in a variable called recentlyContent. The items it generates differs slightly depending on where I want the block to go.

If the Recent block is appearing on a link page, then I want the last three articles displayed, no matter when they were posted. When it’s being placed on the home page, there’s a bit more nuance. If the latest article is one of the three most recent posts to the site, I don’t want it to appear in the block.

Plugin Note: This requires the MultiBlog plugin which allows the system to pull data from multiple blogs at once. I call it using the blog_ids attribute. In my case, blog 1 is articles and blog 2 is for linked posts.

The initial <mt:Unless> block handles this logic. The value for the variable on_link_page is specified in another template we’ll see later. If it is unset, <mt:Entries> gets three posts and loops through them testing whether any entries are from blog 1. If one is found, it sets an offset variable which will be used later in this template.

Full template: recentlyContent.mtml

<mt:SetVarBlock name="recentlyContent">
    <mt:Unless name="on_link_page">
        <mt:Entries blog_ids="1,2" lastn="3">
            <mt:If tag="EntryBlogID" eq="1">
                <mt:Var name="articleOffset" value="1">
            </mt:If>
        </mt:Entries>
    </mt:Unless>

The second block of code, in an <mt:If>, gives the Recently block a header and opens the <div>. It handles a special case for setting margins when the block is on a link page, again evaluating the on_link_page variable.

<mt:If name="on_link_page">
    <h2 id="recently-head" class="on-link-page">Recent Articles</h2>
    <div id="recently" class="on-link-page">
<mt:Else>
    <h2 id="recently-head">Recent Articles</h2>
    <div id="recently">
</mt:If>

The final block does the work of putting the article titles and subtitles into HTML. The articleOffset variable appears here as the <mt:Entries> requests the last three articles starting from 0, unless the offset was set to 1 in the initial logic.

    <mt:Entries blog_ids="1" lastn="3" offset="$articleOffset">
        <a href="<mt:EntryPermalink replace='.html',''>" title="Read ‘<mt:EntryTitle>’">
            <p class="recently-title"><mt:EntryTitle widont='1'></p>
            <p class="recently-subtitle"><mt:EntryExcerpt widont='1'></p>
        </a>
    </mt:Entries>
</div>
</mt:SetVarBlock> 

Permalinks have their file extension removed (for clean URLs) and the titles are processed by the Widon’t plugin. The finished <div> is saved in the aforementioned recentlyContent variable to be output in the template which calls/includes the module.

‘Recently’ on the home page

The Recently block is put onto a page in two steps. First, the HTML is generated and placed into the recentlyContent variable as detailed above. This process is called by using the <mt:Include> function.

Full template: index.mtml

<mt:Include module="Variable – Recently">

The template continues by opening up the <main> content block and entering the primary <mt:Entries> loop, calling additional modules to display articles and links.

<main class="page-content">   
    <mt:Entries blog_ids="1,2" lastn="23">
    <mt:If tag="EntryBlogID" eq="2">
        <mt:Include module="Entry Body – Link">
    <mt:Else>
        <mt:Include module="Entry Body – Article">
    </mt:If>

The second step in the process is simply outputting the contents of the variable. For me, choosing where is a bit complicated. On the home page I prefer for the block to appear ‘below the fold’. Which is to say that I want it to appear slightly or completely below the initial viewable area; you have to scroll to see it.

To accomplish this is an inexact science only using HTML and CSS. I concluded that using a basic word count would suffice. If the first item listed is fewer than 110 words, then Recently will be ‘delayed’ and placed after the second.

To get this, I check if the current entry is the first using the __first__ meta variable. If it is, I use the setvar tag modifier (a shortcut for <mt:SetVarBlock>) and count_words, which unsurprisingly counts the number of words returned from a tag.

<mt:If __first__>
    <mt:EntryBody count_words="1" setvar="firstEntryWordCount">

The following <mt:If> evaluates the word count and if there are more than 110 words, it gets the content saved in recentlyContent – done and dusted. Otherwise it sets a variable indicating that Recently should be delayed until after the first entry. The loop then continues to the next entry.

<mt:If var="firstEntryWordCount" gt="110">
    <mt:GetVar name="recentlyContent">
<mt:Else>
    <mt:SetVar name="delayRecently" value="1">
</mt:If>

As the next entry is displayed, it’s no longer __first__, causing the following to be evaluated. If delayRecently is 1 (true), then recentlyContent will be output. This would be true for all following posts, so the final piece is to set the delay variable to 0 (false), ensuring the block is not displayed again.

    <mt:ElseIf var="delayRecently" eq="1">
        <mt:GetVar name="recentlyContent">
        <mt:SetVar name="delayRecently" value="0">
    </mt:If>

    </mt:Entries>
</main>

Once the 23 entries have been evaluated, the main content block is complete.

Compared to the logic used on the home page, link pages are quite simple. I only need to place the block on the most recent entry. These link pages are produced as an ‘individual archive’ in Movable Type parlance. The biggest difference is that the pages aren’t produced in the <mt:Entries> loop, so we can’t use the __first__ variable. Each entry is effectively both first and last as it’s being written by the system.

Instead we can use a slightly roundabout way to evaluate if this is the latest entry by using the <mt:EntryNext> tag. If a next entry doesn’t exist, then include the Recently template and pass the on_link_page variable. (blog_id specifies which blog the template belongs to and is necessary since the Link template is in blog 2.) Finally, display the content generated by the variable.

Full template: link.mtml

<mt:Unless tag="EntryNext">
    <mt:Include module="Variable – Recently" blog_id="1" on_link_page="1">
    <mt:GetVar name="recentlyContent">
</mt:Unless>

That’s all that’s required.

Though it is possible for Movable Type to generate dynamic pages, I still prefer to keep plain, static, HTML pages. Every time I publish a new item its file is created by Movable Type.

Following that logic, every Link page would end up with the Recently block on it. When the page was generated, it didn’t have a next entry. The block would also be stale. But it’s not.

Working around this would be convoluted, but fortunately we can lean on Movable Type’s behaviour to correct the issue. The link pages include navigation links to next and previous entries. When a new entry is created, the previous needs to be updated to add a link to it, regenerating the page. This means our <mt:Unless> block is re-evaluated and Recently is not displayed. Very convenient.

16 June 2019