Template extensions
This is a big part of the underlying changes that allowed us to implement the forum and thread type system. To sum this concept up in a sentence, it's an inverted template include. I know, that doesn't make sense but hopefully it will after some examples.
[code=html]
<xf:extends template="thread_view" />
<xf:extension name="above_messages">
<xf:if is="$poll">
<xf:macro template="poll_macros" name="poll_block" arg-poll="{$poll}" />
</xf:if>
</xf:extension>
[/code]
This happens to be the thread_view_type_poll
template, which is what is used when viewing a poll thread. It's one of the simplest thread type templates.
<xf:extends>
The first line is one of the most significant. This marks the current template as extending thread_view
(the standard template used when viewing a thread). When this line is present, it essentially means that we output the named template instead (though there's more to it
). If we had nothing else in the template, this page would look exactly like a basic thread.
<xf:extension>
This is where most of the magic happens. You might be able to see where this is going. In this example, we've provided some content for the above_messages
extension point.
To understand what this means, we need to look at part of the thread_view
template itself. The relevant section:
[code=html]
<xf:macro template="lightbox_macros" name="setup" arg-canViewAttachments="{$thread.canViewAttachments()}" />
<xf:extension name="above_messages"></xf:extension>
<xf:ad position="thread_view_above_messages" arg-thread="{$thread}" />[/code]
If you compare this to the 2.1 version, it'll look pretty familiar. We had a poll reference here before and now we have the <xf:extension>
tag.
Within the thread_view
template, we're effectively marking this as an overrideable extension point. By default, it won't display anything, but another template can choose to change what is displayed in this location. Since the poll template is overriding this position, we'll get the poll displayed above the messages.
This is only one way to use <xf:extension>
tags. In some cases, the default template might contain some content. A good example of this is the extended article forum example. In forum_view
we now have something like this:
[code=html]
<div class="block-body">
<xf:extension name="thread_list">
<div class="structItemContainer">
<!-- all of the standard stuff to display sticky threads, normal threads, etc -->
</div>
</xf:extension>
</div>
[/code]
Then in forum_view_type_article
we can override that like this:
[code=html]
<xf:extension name="thread_list">
<xf:if is="$forum.type_config.display_style == 'expanded' AND $forum.canViewThreadContent()">
<!-- display extended versions of articles instead of the standard thread list -->
<xf:else />
<xf:extensionparent />
</xf:if>
</xf:extension>[/code]
The new thing to note here is the <xf:extensionparent>
tag. This allows you to output the parent/original version of the extended area. In this case, we're using it as a fallback to the original display when extended mode isn't enabled, though it can also be used to add things at the beginning or end of the area or to wrap the output.
Macro extensions
Macros can also be extended in the same way. The child macro just needs to be defined like this:
[code=html]<xf:macro name="answer" extends="post_macros::post">...</xf:macro>[/code]
From there, you define extensions roughly as shown above.
Advantages over includes and macros
Template extensions aren't a replacement for includes or macro calls. They provide a more structured approach for situations where you want to replace or add functionality based on a more specific context.
Notably, marking up an area as an extension point doesn't require significant changes to the parent template. In most cases, an empty extension point can be added with virtually no interaction with the surrounding code or a "wrapped" extension point can be added simply by indenting the related code once more. Without this system, the code in question would likely become a macro, which would likely be moved to a different location in the template (or a different template entirely) and may introduce variable scoping issues, making it harder to change the functionality.
Other notable bits
<xf:extension>
tags can also be defined with a value
attribute which can allow them to be used for more than just HTML blocks, similar to the two approaches to <xf:set>
.- There's also an
<xf:extensionvalue>
tag and extension_value()
template function. These allow you to reference an extension multiple times if needed. The template function also allows you to use an extension with specific HTML attributes. We use this in several places to changes the classes applied to an element. <xf:extensionparent>
can also take an extension name to render the parent version of that. This is unlikely to be used often but there are some particular cases that are difficult without it.- When calling a macro, we now generally prefer
<xf:macro name="template_name::macro_name">
over <xf:macro template="template_name" name="macro_name">
although both options are supported. The single attribute approach makes it more straightforward to dynamically switch to a different macro, something done quite commonly in the forum and thread types system. - We now enforce a-z, 0-9, and _ restrictions for macro names properly. Due to the fact that we didn't do this before, it's possible that add-ons/customizations have added improperly named macros. Therefore, we only enforce this in development mode and not in the install app, to prevent an upgrade from being blocked.