The Textpattern crystal ball

n: smd_horizon | v: 0.2.0 | f: /

Documentation for the Textpattern plugin smd_horizon by Stef Dawson follows this short message from our sponsor ;-)

Plugin list button Plugin download button Compressed plugin download button

smd_horizon

The existing tags <txp:next_title />, <txp:link_to_next /> and their prev counterparts cease to function when they reach the first/last posted article in a section. If you have ever wanted to navigate off the TXP grid, this plugin can help.

Features

  • Ability to link to articles that are to be published in future; <txp:next/prev_title /> and <txp:link_to_next/prev> work beyond the existing Posted range
  • Detection of when you are about to “fall off the end” of a section or category, in either direction
  • Entire adjacent article contents available in the current article for your (mis)use so you can tease people with what’s coming next or use any article content as a link
  • Navigate seamlessly between many sections, categories or authors

Installation / Uninstallation

Requires Textpattern 4.0.7+

Download the plugin from either textpattern.org, or the software page, paste the code into the TXP Admin -> Plugins pane, install and enable the plugin. Visit the forum thread for more info or to report on the success or otherwise of the plugin.

To uninstall, simply delete from the Admin -> Plugins page.

Tags: <txp:smd_next> / <txp:smd_prev>

Wrap either <txp:smd_next> or <txp:smd_prev> around existing <txp:link_to_next/prev /> and <txp:next/prev_title /> tags to be able to navigate multiple sections or future articles.

Attributes

  • section: navigate among articles in this list of sections. Default: current section
  • category: navigate among articles having this list of categories. Default: current category
  • author: navigate among articles by this list of author IDs. Default: current author
  • realname: navigate among articles by this list of author Real Names. Default: unset. Note this adds one database query to the page so if you can possibly use author instead, do so
  • status: take articles in this list of status into consideration. Use either the name (live, hidden, etc) or their equivalent numeric values. Default: live. Note: you still cannot actually view (i.e. navigate to) articles that are not Live or Sticky but you can see their contents from the current article (i.e. preview them: see example 3)
  • time: choose which timeframe your articles should be in. Either any, future, or past. Default: any
  • datasort: order the articles by these data items1. You should not normally need this as it automatically sorts based on your section, category and author filters. Default: section, category1, category2, author. Note that unlike regular sort options this does not take asc or desc: the sort order is determined by whether you are using smd_prev or smd_next
  • timesort: order articles by these time options1. Can be any of posted, lastmod or expires. Default: posted
  • form: if you prefer to use a form instead of the container to hold your markup and tags, specify it here. Default: unset.

1 The reason there are two types of sort options is because the ‘datasort’ is applied first. Thus, in the case of linking among multiple sections, articles are always ordered by date within a section. If this were not the case, your articles might be muddled up and it would be very difficult to know when you have reached the ‘end’ of a section or category.

Tags: <txp:smd_link_to_next> / <txp:smd_link_to_prev>

A drop-in replacement for the built-in navigation tags, with a few additional attributes:

  • wraptag: the (X)HTML tag, without its brackets, to wrap round the link, e.g. wraptag="div". If used as a Single tag, this attribute is ignored. Default: unset
  • class: the CSS class name to apply to the wraptag. If no wraptag is given, the class is applied directly to the link itself. If used as a Single tag, this attribute is ignored. Default: unset
  • linkparts: which parts of the anchor to include. Choose from rel or title. Set linkparts='' to remove both rel and title from the anchor. Default: rel, title (both parts visible)
  • urlvars: a list of URL variable names to add to the generated link. Default: unset
  • urlformat: how you would like your urlvars added to the address bar. Useful if you have custom rewrite rules in place. For example urlvars="country, territory" urlformat="/{country_var}/{country_val}/{territory_var}={territory_val}" might write a URL like this: site.com/section/article/country/uk/territory=midlands. Each urlvar you specify has two components: 1) its name followed by _var to indicate where you want the URL parameter name, and 2) its name followed by _val to indicate where you want the parameter’s value.

The URL variables may be derived from the current URL line or may be set like this:

<txp:smd_link_to_next urlvars="c, id, myvar=12">
  <txp:title />
</txp:link_to_next>

Thus, c and id will be read from the URL and passed forward, whereas myvar will be added to the URL and initialized with a value of 12 if it does not already exist. If either c or id are missing they will not be passed in the link. If myvar changes the URL’s value will persist, however if it is removed from the URL it will be reinstated when you navigate to another article, and it will be reset to 12.

Notes:

  • the URL variables and values are read from both GET and POST submissions; POST overrides GET if the names clash
  • you can use the shorthand SMD_ALL to read all current URL variables
  • you may add :ESCAPE to any variable name (including SMD_ALL) to have HTML entities escaped
  • you may add :FORCE to any variable name (including SMD_ALL) to include the variable in the link even if it is empty
  • you may add :TAG_PRIORITY to any variable name (including SMD_ALL) so any values you may have given in the tag are used regardless if the same variable name exists in the URL. Without this option, ‘URL priority’ is assumed, so if a variable of the same name exists and is used, your given value will be ignored. TAG_PRIORITY is useful for making sure a variable exists, is initialized to a known value and remains at that value, even if the variable is altered by the visitor or removed from the URL
  • the above three modifiers can be used in combination if you wish

Tag: <txp:smd_if_end>

Anything inside this conditional tag will only be displayed if the end of current postable articles has been reached. The tag can look for the ‘end’ of a variety of things governed by the type attribute:

  • type: can be any of list, category, category1, category2, author or section. Default: list
  • logic: can be or which means that if any of the items reach their end the container will be triggered; or it could be and which means that all of the items have to end simultaneously before the container will fire

Use this to take action and display something different if you wish to differentiate between ‘future’ and ‘current’ articles or if you have reached the end of a list of sections.

Tag: <txp:smd_if_start>

Whatever is inside this conditional tag will only be displayed if the beginning of current postable articles has been reached. The tag can look for the ‘start’ of a variety of things governed by the type attribute:

  • type: can be any of list, category, category1, category2, author or section. Default: list
  • logic: can be or which means that if any of the items have reached the beginning, the container will trigger; or it could be and which means that all of the items have to be at their respective start points simultaneously before the container will fire

This could be used to offer “wraparound” navigation so if you click “previous” when you are at the first article you can perhaps take visitors to the last article, or maybe to the most recent article in another section.

Examples

Important: akin to the built-in tags, smd_horizon is limited to use in an individual article context and will either throw a warning or produce weird results if used in an article list.

Example 1: URL var persistence and wrapping

Can be used as a drop-in replacement for <txp:link_to_....> but with the extra ability to apply wraptag and class. Also, the URL variable uname is passed along from article to article, if it is used in the URL.

<txp:if_individual_article>
  <txp:smd_link_to_prev wraptag="div"
     class="nav_prev" urlvars="uname">&#171;
     <txp:prev_title /></txp:smd_link_to_prev>
  <txp:smd_link_to_next wraptag="div"
     class="nav_next"
     urlvars="uname"><txp:next_title />
     &#187;</txp:smd_link_to_next>
</txp:if_individual_article>

Example 2: Navigating to future articles

Enhance the standard link_to_next/prev tags by wrapping smd_next/smd_prev around them. With its default setting time="any", you can allow visitors to navigate to future articles either using the standard link_to_next/prev tags (as used in this example) or via the smd_link_to_next/prev tags.

<txp:if_individual_article>
   <txp:smd_prev>
      <txp:link_to_prev>
         &#171; <txp:title />
      </txp:link_to_prev>
   </txp:smd_prev>
   <txp:smd_next>
      <txp:link_to_next>
         <txp:title /> &#187;
      </txp:link_to_next>
   </txp:smd_next>
</txp:if_individual_article>

Example 3: Sneak peek of unpublished articles

If navigating directly to future articles is not your idea of fun, how about offering a sneak preview of the next chapter of your book that you are serialising?

<txp:if_individual_article>
   <txp:title />
   <txp:body />
   <txp:smd_if_end type="section">
      <txp:smd_next>
         <h3>Coming up next week...</h3>
         <txp:excerpt />
      </txp:smd_next>
      <txp:link_to_prev>
         <txp:title />
      </txp:link_to_prev>
   <txp:else />
      <txp:link_to_prev>
         <txp:title />
      </txp:link_to_prev>
      <txp:link_to_next>
         <txp:title />
      </txp:link_to_next>
   </txp:smd_if_end>
</txp:if_individual_article>

Imagine you have each chapter as an article and have published, say, the first 3 chapters. You have written the 4th chapter but have set its posted date to next week. Your visitors can read and navigate through chapters 1, 2, and 3, as they normally would with any TXP articles. When they reach Chapter 3, they are shown the excerpt from the unpublished article to whet their appetites.

One side-effect of TXP’s content handling is that future articles can still be displayed in the browser if a visitor guesses the URL. For serialised article titles such as chapter-1, chapter-2, and so on, it is not a great leap of faith for someone to gain access to your future articles.

With smd_horizon you can circumvent this. Set your Chapter 4, future article to the ‘hidden’ status and add status="live, hidden" to your <txp:smd_next> tag. You can still offer ‘previews’ of the content.

Example 4: Make next/prev links using images

Article content can be anything from the article; not just its excerpt. For example, category and custom field contents are all available. Say you published an online comic; you can even preview the article image if you wish.

With this example, article images are used as links to the next/previous article. It also maintains the URL variables m and y so that a nearby calendar on the same page remains showing the month and year the visitor has chosen:

<txp:if_individual_article>
  <txp:smd_prev time="past">
    <txp:smd_link_to_prev urlvars="m,y">
      <txp:article_image thumbnail="1" />
    </txp:smd_link_to_prev>
  </txp:smd_prev>
  <txp:smd_next time="past">
    <txp:smd_link_to_next urlvars="m,y">
      <txp:article_image thumbnail="1" />
    </txp:smd_link_to_next>
  </txp:smd_next>
</txp:if_individual_article>

If you wanted to use the title as a fallback in case an article image wasn’t assigned, you can use TXP 4.2.0’s <txp:if_article_image /> tag:

<txp:if_individual_article>
  <txp:smd_prev time="past">
    <txp:smd_link_to_prev urlvars="m,y">
      <txp:if_article_image>
         <txp:article_image thumbnail="1" />
      <txp:else />
         <txp:title />
      </txp:if_article_image>
    </txp:smd_link_to_prev>
  </txp:smd_prev>
  <txp:smd_next time="past">
    <txp:smd_link_to_next urlvars="m,y">
      <txp:if_article_image>
         <txp:article_image thumbnail="1" />
      <txp:else />
         <txp:title />
      </txp:if_article_image>
    </txp:smd_link_to_next>
  </txp:smd_next>
</txp:if_individual_article>

Example 5: Multiple section navigation

Navigate over more than one section and take action when you reach either end of the list.

<txp:if_individual_article>
   <txp:smd_prev section="articles, about">
      <txp:smd_if_start type="list">
      The articles begin here
      </txp:smd_if_start>
      <txp:link_to_prev>
         <txp:title />
      </txp:link_to_prev>
   </txp:smd_prev>
   <txp:smd_next section="articles, about">
      <txp:smd_if_end type="list">
      The end of the road
      </txp:smd_if_end>
      <txp:link_to_next>
         <txp:title />
      </txp:link_to_next>
   </txp:smd_next>
</txp:if_individual_article>

Notes:

  • this is achieved by simply wrapping the standard link_to_next/prev tags with smd_next and smd_prev. No other trickery involved
  • if you are using smd_next/prev to iterate over a list of categories or authors, you can detect the end of those lists as well using the same type="list" syntax. If you are iterating over more than one item at once the plugin has no way of knowing which of the ‘last’ items it has reached (section, category, author…) so it’ll be the very last one that triggers it

Example 6: At the end of each section, do…

An extension of example 5, this one detects when you reach the end of one of the sections in your list and displays an appropriate message to guide you onwards.

<txp:if_individual_article>
   <txp:smd_prev section="articles, about">
      <txp:smd_if_start type="section">
         <txp:link_to_prev>
            Step back into <txp:section />
         </txp:link_to_prev>
      <txp:else />
         <txp:link_to_prev>
            <txp:title />
         </txp:link_to_prev>
      </txp:smd_if_start>
   </txp:smd_prev>
   <txp:smd_next section="articles, about">
      <txp:smd_if_end type="section">
         <txp:link_to_next>
            Move onwards to <txp:section />
         </txp:link_to_next>
      <txp:else />
         <txp:link_to_next>
            <txp:title />
         </txp:link_to_next>
      </txp:smd_if_end>
   </txp:smd_next>
</txp:if_individual_article>

Example 7: Nesting smd_if_start/end

A further extension of example 6:

<txp:if_individual_article>
   <txp:smd_prev section="articles, about, products">
      <txp:smd_if_start type="list">
      The articles begin here
      <txp:else />
         <txp:smd_if_start type="section">
            (previous section)
         </txp:smd_if_start>
      </txp:smd_if_start>
      <txp:link_to_prev>
         <txp:title />
      </txp:link_to_prev>
   </txp:smd_prev>
   <txp:smd_next section="articles, about, products">
      <txp:link_to_next>
         <txp:title />
      </txp:link_to_next>
      <txp:smd_if_end type="list">
      The end of the road
      <txp:else />
         <txp:smd_if_end type="section">
             (next section)
         </txp:smd_if_end>
      </txp:smd_if_end>
   </txp:smd_next>
</txp:if_individual_article>

Notice that:

  • detection of the start/end of the list of sections occurs first
  • if that fails, we check if we are at the end of a conventional section. If we did not do this nested inside the ‘else’, the ‘end of section’ trigger would be true at the extremities of the section list as well
  • there is no conditional surrounding the link_to_next/prev because they will automatically return nothing when the end of the list is reached

Example 8: At end of each category1, do…

If you order your articles by category1 you can identify when the next (or previous) category is about to be reached. This example just shows a message when a different category1 is detected:

<txp:if_individual_article>
   <txp:smd_prev>
      <txp:link_to_prev>
         &#171; <txp:title />
      </txp:link_to_prev>
   </txp:smd_prev>
   <txp:smd_next>
      <txp:link_to_next>
         <txp:title /> &#187;
      </txp:link_to_next>
      <txp:smd_if_end type="category1">
         Next cat: <txp:category1 link="1" />
      </txp:smd_if_end>
   </txp:smd_next>
</txp:if_individual_article>

Notes:

  • Use type="category1, category2" (or type="category") to take action if either category changes in the next article
  • Use type="category1, category2" logic="and" to take action if both categories change in the next article
  • You can use the shorthand cat1 and cat2 if you don’t like spelling out category1 and category2 every time :-)

Example 9: Loop over an author’s articles

No matter if an author has written stuff in multiple sections, you can iterate over them all using smd_link_to_next/prev.

<txp:if_individual_article>
   <txp:smd_prev author="Bloke" section="articles,about">
      <txp:smd_link_to_prev>
         <txp:title />
      </txp:smd_link_to_prev>
   </txp:smd_prev>
   <txp:smd_next author="Bloke" section="articles,about">
      <txp:smd_link_to_next>
         <txp:title />
      </txp:smd_link_to_next>
   </txp:smd_next>
</txp:if_individual_article>

Instead of hard-coding the author name, if you wish to use the current author, replace author="Bloke" with realname='<txp:author />'. Very useful in magazine-style sites for showing ‘next article by this author’ links.

Note that this example does not work with TXP’s built-in link_to_next/prev tags because they still ‘see’ other articles in the same section, irrespective of author (and in fact category, so the same restriction applies there).

Example 10: Loop over many author’s articles

And across multiple sections too:

<txp:if_individual_article>
   <p>Current article by: <txp:author /></p>
   <txp:smd_prev author="Stef, John"
     section="articles,products"
     datasort="author, section">
      <txp:smd_link_to_prev>
         <txp:title /> (by <txp:author />)
      </txp:smd_link_to_prev>
   </txp:smd_prev>
   <txp:smd_next author="Stef, John"
     section="articles, products"
     datasort="author, section">
      <txp:smd_link_to_next>
         <txp:title /> (by <txp:author />)
      </txp:smd_link_to_next>
   </txp:smd_next>
</txp:if_individual_article>

Notice that:

  • datasort is used to order by author first, then section
  • iterating over author lists must be done inside smd_next/prev tags

Example 11: Detect when an author change occurs

An extension of example 10 that shows a notification every time you are about to “step off” an author’s article list into the next or previous author:

<txp:if_individual_article>
   <p>Current article by: <txp:author /></p>
   <txp:smd_prev author="Stef, Dale, Jakob"
     section="articles, products"
     datasort="author, section">
      <txp:smd_link_to_prev>
         <txp:title /> (by <txp:author />)
      </txp:smd_link_to_prev>
      <txp:smd_if_start type="author">
      (Previous author)
      </txp:smd_if_start>
   </txp:smd_prev>
   <txp:smd_next author="Stef, Dale, Jakob"
     section="articles, products"
     datasort="author, section">
      <txp:smd_link_to_next>
         <txp:title /> (by <txp:author />)
      </txp:smd_link_to_next>
      <txp:smd_if_end type="author">
      (Next author)
      </txp:smd_if_end>
   </txp:smd_next>
</txp:if_individual_article>

Author / credits

Stef Dawson.

Source code

If you’d rather wander aimlessly through thousands of lines of PHP source code, you’ll need to step into the view source page.

Legacy software

If, for some inexplicable reason, you need an old version of a plugin, it can probably be found on the plugin archive page.

Experimental software

If you’re feeling brave, or fancy going bareback, you can test out some of my beta code. It can be found on the plugin beta page.