Documentation for the Textpattern plugin smd_horizon by Stef Dawson follows this short message from our sponsor ;-)
If you like my code and it makes your site better, feel free to show your appreciation with something from my UK Amazon wish list (or US) or donate to the Stef Dawson community coding pot, either via paypal.me/stefdawson or by following the Donate button below to PayPal. Thanks!
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
, orpast
. 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 takeasc
ordesc
: 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
orexpires
. 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
ortitle
. Setlinkparts=''
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 exampleurlvars="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
orsection
. 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 beand
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
orsection
. 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 beand
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">«
<txp:prev_title /></txp:smd_link_to_prev>
<txp:smd_link_to_next wraptag="div"
class="nav_next"
urlvars="uname"><txp:next_title />
»</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>
« <txp:title />
</txp:link_to_prev>
</txp:smd_prev>
<txp:smd_next>
<txp:link_to_next>
<txp:title /> »
</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>
« <txp:title />
</txp:link_to_prev>
</txp:smd_prev>
<txp:smd_next>
<txp:link_to_next>
<txp:title /> »
</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"
(ortype="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
andcat2
if you don’t like spelling outcategory1
andcategory2
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
Source code
If you’d rather spend time with the bytes, you’ll need to step into the view source page.
Legacy software
If, for some inexplicable reason, you need an ancient version of a plugin, it can probably be found on the plugin archive page.
Experimental software
If you’re feeling brave, or fancy dipping your toe in shark-infested water, you can test out some of my beta code. It can be found on the plugin beta page.