User:Tigerpaw28/Sandbox/HowToCodeTemplates
Where to begin?
So, you want to make a template? Well, before we become too preoccupied with whether or not you could, let's stop to think if you should. The main goal of a template is to make it easy to reuse a section of markup on multiple pages.
You write the markup once and then refer to it multiple times on different pages. Thus, the first consideration should be "Will I or other editors use this more than once?". The second thing to consider is will making this code a template save time. If it takes longer to configure the template, than it would to
just write it out, you might be a redneck looking at something that shouldn't be a template. The third thing to consider is "Is there a template that already does this?" No sense reinventing the wheel and all that. To see all of the templates that MediaWiki has to offer and what they do, check our Template Guide.
How to begin?
Okay, now that we know that we should we can become preoccupied with could. All live templates exist under the "Template" namespace, i.e. their page names will be prefixed with "Template:". For our new template, we don't want to put it in the Template namespace until it's ready to go live. So where do we put it? In a sandbox of course! Just like articles, templates can also be mocked up in sandbox. To refer to a sandboxed template, just use the entire page name as the template name in the reference. For example, if I create the sandboxed template article "User:Tigerpaw28/Sandbox/MyTestTemplate" with no parameters then I would refer to the template like this: "{{User:Tigerpaw28/Sandbox/MyTestTemplate}}"
This is also applies to testing changes to an existing template. Always follow this process for an existing template:
- Copy the existing template to a sandbox page.
- Create a second sandbox page to transclude your template on and open it in a second browser tab. This will allow you to make a change, save it and then quickly switch tabs and reload the second sandbox to see how your changes look. You will not be able to preview your changes by including your template sandbox within itself because the preview will transclude the last saved copy and not your unsaved version.
- Make your changes to the sandbox.
- Verify that they work. If and only if you have made and verified all desired changes, you can proceed to step four.
- Replace the live template code with your sandbox code. More advanced/experienced coders can (probably copy things piecemeal safely, but a full replacement ensures that broken code is not being put in a live template.
- Update the template documentation to reflect any changes in how or where the template is/can be used. Visual updates typically do not need to be documented unless they affect usage.
This will ensure our users do not experience broken formatting, missing information, broken links or other issues as a result of us changing our template code.
Before you begin, it is recommended to download and install Microsoft Visual Studio Code and install the "Wikitext" extension by Rowe Wilson and Fredrisk Holme. This extension marks and styles Wiki markup text making it much easier to see where the various parts of your markup begin and end. You'll need to save a .Wiki file on your local computer and then copy or start writing your markup in the new file in order for the extension to take effect. There is also a related gadget that will let you open VSCode right in your browser to edit a page with all of the same enhancements.
Template coding
Intro
Here's where the magic happens. In order to perform our tricks, we need to learn some basics about how to do our magic. As mentioned previously, a template is just a chunk of Wiki markup at it's core. Any markup you'd write on a normal page can also be put in a template:
| Markup | Result |
|---|---|
== The episode where Megatron retreats == * '''Airdate''': 11/20/20XX * '''Director''': Doc Light * '''Editor''': Cut Man |
The episode where Megatron retreats
|
While it would perhaps not create a useful template, the above is a perfectly valid as the content of a template article. Let's try to turn this basic template into a useful one. Right now we're stuck with showing the same episode title, director, editor and airdate every time. It would be much better if we could tell the template what values we want to see in those locations.
It just so happens that we can do that quite easily with parameters.
Parameters
A parameter is a value passed into a template. Consider this example template reference:
{{MyTemplate|color=green|trumpet}}
This example demonstrates the two types of parameters: named and unnamed. Named parameters, as their name suggests, are identified by a name. This name can be as short as one letter but can also be longer. Only letters are valid in a parameter name, no numbers, spaces or special characters. In the example, "color" is a named parameter and its value is "green". Unnamed parameters are identified by their ordinal position, without counting named parameters. In the above example, "trumpet" is the value of the second, unnamed parameter.
To use these in our template, we need only include the parameter name/ordinal inside of triple curly braces like so:
{{{color}}}
{{{1}}}
The result will be to display the value of the parameter in place of the reference. Now let's apply this to our episode template. Instead of displaying the fixed title, airdate, etc., we'll use parameters to display whatever values the user provides:
| Markup | Result |
|---|---|
== {{{title}}} ==
* '''Airdate:''' {{{airdate}}}
* '''Director:''' {{{director}}}
* '''Editor:''' {{{editor}}}
|
{{{title}}}
|
And, voila! We have a template that will populate our set values. Let's make a tweak to the parameters. Right now, if a value isn't passed to one of the parameters then the parameter markup itself will de displayed. This isn't ideal. To avoid that, we can set a default value to populate if no value is provided.
To do that, we place a pipe character ("|") after the parameter name followed by the default value. If we set our fields to default to "Unknown" then the updated template would be:
| Markup | Result |
|---|---|
== {{{title}}} ==
* '''Airdate:''' Unknown
* '''Director:''' Unknown
* '''Editor:''' Unknown
|
{{{title}}}
|
We'll leave the title without a default as we can reasonably assume that the title is always the first piece of info we'll have and thus we'll always be able to provide that value.
Doing it with style
Our template is now functionally useful but visually bland. We can use other wiki markup to adjust the layout/formatting. HTML markup and CSS can also be used in wiki templates to set the layout and styling of the template. By default, the CSS from MediaWiki:Monobook.css will be used. While the finer points of HTML and CSS are outside the scope of this guide, we'll use some HTML and CSS alongside some layout related wiki markup to punch up the appearance of our template. We'll start by throwing our fields into a table layout. Wiki markup tables are really just an alternate way of coding an HTML table layout. When the MediaWiki software renders the wiki table markup, it converts it into the equivalent HTML markup. Wiki table markup has the following parts:
- {| - The table starts with a left curly brace and a pipe.
- |- - Denotes the start of a row. There should be one of these for each row of the table. While the first row doesn't technically need this delimiter, including one for the first row is recommended for readability.
- | - Separates the values of each column. The columns can all be on the same line and could even be on the same line as the row delimiter. For readability, it is recommended to at least have the columns on a separate line from the row delimiter. Depending on the complexity of the column values, it may or may not be more readable to have columns on separate lines.
- |} - The table ends with a pipe and a right curly brace.
With the table markup in place our template looks like this:
| Markup | Result | ||||
|---|---|---|---|---|---|
{|
|-
| {{{title}}}
|-
| '''Airdate:''' Unknown
|-
| '''Director:''' Unknown
|-
| '''Editor:''' Unknown
|}
|
|
Not bad, but still just a bunch of text. Let's add some CSS. At the table level we'll set the following class and style values: 'class="infobox" style="width: 315px;"'. The "infobox" class will give us a background color and a border for our box. And then our style sets the width measured in pixels. To apply this to the table, we will place it after the opening curly brace and pipe:
| Markup | Result | ||||
|---|---|---|---|---|---|
{|class="infobox" style="width: 315px;"
|-
| {{{title}}}
|-
| '''Airdate:''' Unknown
|-
| '''Director:''' Unknown
|-
| '''Editor:''' Unknown
|}
|
|
Looking better. Let's add some CSS at the row level to turn the title row into a title bar. We'll add another style parameter to change the background, font size and text centering of the title row.
Here is the style tag: "style="background-color:#e7d492;text-align:center;font-size:12px;"
And here is the updated template markup:
| Markup | Result | ||||
|---|---|---|---|---|---|
{|class="infobox" style="width: 315px;"
|-style="background-color:#e7d492;text-align:center;font-size:12px;"
| {{{title}}}
|-
| '''Airdate:''' Unknown
|-
| '''Director:''' Unknown
|-
| '''Editor:''' Unknown
|}
|
|
This is but a small sample of the many ways you can incorporate HTML and CSS into a template. See the external links below for more on how HTML and CSS works.
Inclusion control
Another feature that would be useful to add to our template is to have it add a category to any article we use it on. We could just add [[Category:Episodes]] to our template and that will get added to the articles that transclude our template as well because by default any markup added to a template is included into pages that use the template. There is a catch though. Adding the category link will also put the template in that category, which we don't want. We have the opposite problem too, when it comes to adding some documentation for our template. We want that documentation visible on the template, but not on the articles that uses the template. This is where the three inclusion control tags come in:
- <noinclude></noinclude> - Anything betweeen <noinclude> tags will not be included when the template is used on another article. It is only visible when viewing the template itself.
- <includeonly></includeonly> - Anything between <includeonly> tags will only be visible on pages that include the template and, of course, when editing the template. It will not appear when viewing the template.
- <onlyinclude></onlyinclude> - Anything between <onlyinclude> tags will be the only markup on the template that is included into articles that use the template. The distinction between this and <includeonly/> is that onlyinclude focuses on excluding text outside the tags from being included in an article and and includeonly focuses on excluding text within the tags from the template view.
These tags can be used in combination to precisely denote what to include and what not to include in both when viewing the template and when viewing an article that uses the template. For our template, we will first add the category link inside includeonly tags. Next, we'll put everything inside of onlyinclude tags. Finally, we'll put our documentation outside those tags. The result:
<onlyinclude>
{|class="infobox" style="width: 315px;"
|-style="background-color:#e7d492;text-align:center;font-size:12px;"
| {{{title}}}
|-
| '''Airdate:''' {{{airdate|Unknown}}}
|-
| '''Director:''' {{{director|Unknown}}}
|-
| '''Editor:''' {{{editor|Unknown}}}
|}
<includeonly>
[[Category:Episodes]]
</includeonly>
</onlyinclude>
== Documentation ==
To use this template, do blah....
This prevents our documentation from being included because it's outside the onlyinclude tags. It allows the template markup within the onlyinclude tags to still be visible as an example when viewing the template directly. The includeonly tags prevent the category link from being included in the template view, so that the template isn't categorized (but anything that includes the template is).
Magic Words
Magic words are text strings that MediaWiki recognizes as having a special meaning/returning a specific value. There are three types of magic words:
Behavior switches: Behavior switches are used to control the behavior and layout of the page. They are written as uppercase words surrounded by double underscores, e.g. __FOO__. As templates are included within multiple pages, you will usually not use these in a template as they would affect any page that includes the template. Variables: Variables return information about the current page, the wiki itself or the current date. Variables are written as uppercase words surrounded by double braces much like templates, e.g. {{FOO}}. Parser functions: Parser functions are effectively variables that use parameters. They are written in the form {{foo:...}} or {{#foo:...}}. MediaWiki is on MediaWiki 1.45.3. The only base parser function supported in this version of MediaWiki is {{PAGESIZE:...}}.
We can make use of the {{PAGENAME}} magic word to create our title instead of a parameter. Granted, this won't be ideal for pages whose title includes a disambig, but we'll take care of that later. Our template now looks like this:
<onlyinclude>
{|class="infobox" style="width: 315px;"
|-style="background-color:#e7d492;text-align:center;font-size:12px;"
| {{PAGENAME}}
|-
| '''Airdate:''' {{{airdate|Unknown}}}
|-
| '''Director:''' {{{director|Unknown}}}
|-
| '''Editor:''' {{{editor|Unknown}}}
|}
<includeonly>
[[Category:Episodes]]
</includeonly>
</onlyinclude>
== Documentation ===
To use this template, do blah....
Parser Extensions
Amongst the many extensions installed on MediaWiki, two of them provide additional parser functions for use in templates. The ParserFunctions extension adds a number of new parser functions, most of which are related to conditional logic. The other extension is StringFunctions which, as its name suggests, adds functions for string manipulation.
ParserFunctions extension
We won't get into all of the functions available through this extension but we will look at the two most important and useful ones: #if and #switch.
#if
An if conditional is used for the purpose of doing one thing if the conditional is true and another if the conditional is false. There are multiple flavors of #if that are used for differnt types of comparisons:
- #if - The most basic of the ifs. #if is used to test if a string is empty (or whitespace) or not. This is most frequently used to test if a value has been passed in for a particular parameter. If no value was passed, the parameter will be an empty string. Format:
{{#if: testString {{!}} value if testString is not empty {{!}} value if testString is empty (or only white space) }} - #ifeq - Compares two strings to see if they are equal. Format:
{{#ifeq: string 1 {{!}} string 2 {{!}} value if strings are equal {{!}} value if strings are not equal }} - #iferror - Checks to see if an input string equates to a template call or other expression that returns an HTML object with
class="error"
which represents an error. Format:{{#iferror: testString {{!}} value if no error returned {{!}} value if error is returned }} - #ifexpr - Evaluates a mathmatical expression. Format:
{{#ifexpr: expression {{!}} value if expression is true{{!}} value if expression is false }} - #ifexist - Checks to see if an input string equates to the title of an existing article. Format:
{{#ifexists: page title {{!}} value if page title exists {{!}} value if page title doesn't exist }}
Let's see how we can use #if and #ifexpr to add a "ratings" section to our template. We'll start by uaing Template:Factions/icons to create some Autobot symbols for the rating icons:
{{Factions/icons|option=autobot}}{{Factions/icons|option=autobot}}{{Factions/icons|option=autobot}}{{Factions/icons|option=autobot}}{{Factions/icons|option=autobot}}
}}
That gives us five icons. But we don't want them showing all at once. We only want to show as many of them as apply to our rating, which we'll be passing in as a parameter. So, let's use #ifexpr to see if our rating is high enough for each icon. For the first icon, we'll only show it if the rating is one or more. We'll only show the second if the rating is two or higher and so on:
{{#ifexpr: {{{rating}}} > 0{{!}}{{Factions/icons{{!}}option=autobot}}{{!}}}}{{#ifexpr: {{{rating}}} > 1{{!}}{{Factions/icons{{!}}option=autobot}}{{!}}}}{{#ifexpr: {{{rating}}} > 2{{!}}{{Factions/icons{{!}}option=autobot}}{{!}}}}{{#ifexpr: {{{rating}}} > 3{{!}}{{Factions/icons{{!}}option=autobot}}{{!}}}}{{#ifexpr: {{{rating}}} > 4{{!}}{{Factions/icons{{!}}option=autobot}}{{!}}}}
Now that we have our ratings meter built, let's put it in a table and spruce it up with a label and some additional text to add context:
{|
|-
|'''Rating'''
|-
|{{#ifexpr: {{{rating}}} > 0|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 1|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 2|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 3|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 4|{{Factions/icons|option=autobot}}|}} out of five.
|}
Let's add this to our template. We'll make it a row unto itself, and only have it show if the rating parameter was passed in. We can check that using #if. Here's where the curly brace nesting gets annoying really fun:
<onlyinclude>
{|class="infobox" style="width: 315px;"
|-style="background-color:#e7d492;text-align:center;font-size:12px;"
| {{PAGENAME}}
|-
| '''Airdate:''' {{{airdate|Unknown}}}
|-
| '''Director:''' {{{director|Unknown}}}
|-
| '''Editor:''' {{{editor|Unknown}}}
|-
|{{#if:{{{ratings}}}|{|
|-
|'''Rating'''
|-
|{{#ifexpr: {{{rating}}} > 0|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 1|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 2|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 3|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 4|{{Factions/icons|option=autobot}}|}} out of five.
|}|}}
|}
<includeonly>
[[Category:Episodes]]
</includeonly>
</onlyinclude>
== Documentation ===
To use this template, do blah....
And just like that we have a ratings section. Two thumbs up for if!
#switch
But wait, what if we want to check for more than two possibilities? We could use multiple nested ifs if we're checking different conditions. For example, if we want to do something only if two parameters both exist, we first write an if for the first parameter:
{{#if:{{{a}}}|a exists|}. This will show the text "a exists" if a does in fact exist. To restrict it further, so that our text shows only if a and b exist, we put an if for b inside the true value of our if that checks a:
{{#if:{{{a}}}|{{#if:{{{b}}}|a exists|}}|}. You can nest ifs to your heart's content.
But the more nesting you have the harder it can be to decipher what the code is doing. If you're checking against different values everytime, you're either stuck doing lots of nesting or you can just have fewer conditions. But if you want to check against the same parameter three or more times, our friend #switch comes in very handy.
Consider this scenario: ItsWalky has noticed our template coding prowess and has asked us to add a continuity family link to the template. He says the link should be generated based on an abbreviation for the continuity family. We could nest a bunch of ifs to check for each possible continuity family, but a switch statement will be much cleaner and simpler. A switch compares one value against several test cases. This is exactly what Walky is asking us to do for the continuity family link: Compare the abbreviation to our predefined values and display the content for the matching value.
While the Transformers brand has seen many continuity families, for the purposes of our example we'll limit our test cases to the following:
- G1 - The Transformers.
- BW - Beast Wars: Transformers.
- UT - The Unicron Trilogy.
- Movie - The live action movies.
- IDW2005 - The comic started by IDW Publishing in 2005.
A switch statement is written in the following format:
{{#switch|comparison string
| case1 = result
| case2 = result
|...
| case x = result
| #default = result
}}
The comparison string will be compared to each case value until either a match is found or it reaches the default result. Alternatively, you may omit the default result and no text will display if a match is not found. The default case can be alternately written as
| default '''result'''
where "result" can not include an equals sign ('='). The reason for this is that including the equal sign will cause the default case to instead be treated as a normal case.
The default case does not have to be the last case in the list, but having it last is a best practice for ease of reading. It is possible to have two or more cases return the same result. This is known as having "fall through" cases and helps reduce duplication. A fall through case does not have a result or an equals sign following it. For our scenario, we'll treat BW and IDW2005 as fall through cases for G1 as they are all in the Generation 1 continuity family.
Our switch statement will then look like this:
{{#switch|{{continuity}}
| BW
| IDW2005
| G1 = [[Generation 1 continuity family]]
| UT = [[Unicron trilogy continuity family]]
| Movie = [[Movie continuity family]]
}}
Plugging it into the template gives us this:
<onlyinclude>
{|class="infobox" style="width: 315px;"
|-style="background-color:#e7d492;text-align:center;font-size:12px;"
| {{PAGENAME}}
|-
| '''Airdate:''' {{{airdate|Unknown}}}
|-
| '''Director:''' {{{director|Unknown}}}
|-
| '''Editor:''' {{{editor|Unknown}}}
|-
| '''Continuity family:''' {{#switch|{{continuity}}
| BW
| IDW2005
| G1 = [[Generation 1 continuity family]]
| UT = [[Unicron trilogy continuity family]]
| Movie = [[Movie continuity family]]
}}
|-
|{{#if:{{{ratings}}}|{|
|-
|'''Rating'''
|-
|{{#ifexpr: {{{rating}}} > 0|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 1|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 2|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 3|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 4|{{Factions/icons|option=autobot}}|}} out of five.
|}|}}
|}
<includeonly>
[[Category:Episodes]]
</includeonly>
</onlyinclude>
== Documentation ===
To use this template, do blah....
String Functions extension
You're probably wondering when we're going to get around to fixing that title issue we discussed a few sections back. How about now? The StringFunctions extension gives us a number of ways to manipulate a string and we will use some of them to remove the disambig (if any) from the title text. While StringFunctions has been integrated into ParserFunctions, MediaWiki uses the extensions separately. StringFunctions provides the following functions:
- #len - Gets the length of a string
- #pos - Finds the first position of a string within another string.
- #rpos - Finds the last position of a string within another string.
- #sub - Returns a substring (i.e. a part of a string) from a given string based on a starting position and string length.
- #pad - Extends a string to a given width.
- #replace - Replaces all occurences of a string within another string with a third string.
- #explode - Splits a given string into pieces based on a delimiter and returns one of the pieces.
- #urldecode and #urlencode - Used to decode a URL into a string or encode a string into a URL.
We can use the #explode function to get the non-disambig part of the title. #explode takes three parameters: {{#explode:string|delimiter|position}} The string will be the {{PAGENAME}} magic word. The delimiter will be a space, and position will be zero since the pieces are counted from zero and we want the first one. Our #explode function becomes:
{{#explode:{{PAGENAME}}| |0}}
We swap this for Tigerpaw28/Sandbox/HowToCodeTemplates in our template and the result is as follows:
<onlyinclude>
{|class="infobox" style="width: 315px;"
|-style="background-color:#e7d492;text-align:center;font-size:12px;"
| {{#explode:{{PAGENAME}}| |0}}
|-
| '''Airdate:''' {{{airdate|Unknown}}}
|-
| '''Director:''' {{{director|Unknown}}}
|-
| '''Editor:''' {{{editor|Unknown}}}
|-
| '''Continuity family:''' {{#switch|{{continuity}}
| BW
| IDW2005
| G1 = [[Generation 1 continuity family]]
| UT = [[Unicron trilogy continuity family]]
| Movie = [[Movie continuity family]]
}}
|-
|{{#if:{{{ratings}}}|{|
|-
|'''Rating'''
|-
|{{#ifexpr: {{{rating}}} > 0|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 1|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 2|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 3|{{Factions/icons|option=autobot}}|}}{{#ifexpr: {{{rating}}} > 4|{{Factions/icons|option=autobot}}|}} out of five.
|}|}}
|}
<includeonly>
[[Category:Episodes]]
</includeonly>
</onlyinclude>
== Documentation ===
To use this template, do blah....
Style guide
Unlike magicians, who usually prefer to keep the way their tricks work a secret (or at least their really good tricks), we want other editors to know how our templates work. It is far easier to modify a template when we can read the code and understand how and why it works. And boy can template code get unreadable in a hurry. Template markup frequently involves a lot of curly braces and pipes, create a maze characters that makes it hard to tell where one command ends and another begins. Sometimes, even if it isn't a maze, it can be tricky to tell what the code does or was intended to do. To help address these problems, MediaWiki has a style guide.
- Comments - Comments should be used to document the logic (ifs, switches, etc.) of a template to make it easier to figure out how the template works.
- Minimize nesting - Nesting template calls and parser functions inside of other template calls and parser functions can quickly lead to having a mess of curly braces that is difficult to decipher. While it can't always be avoided, using switches where possible instead of nested ifs will help with keeping things readable.
- Clear parameter names - Parameter names (if not anonymous) should be self explanatory and descriptive. So a parameter that is used to pass in the name of a toy, should be called something along the lines of "name" or "toyname".
- Wiki Tables - Each row and each column in a Wiki table should be on its own line. If there are only a few columns or the columns are concise, then it is okay to have all of the columns of a row on one line.
- HTML and CSS - HTML and CSS in templates should follow normal formatting conventions.
- Documentation, documentation, documentation - A template should always have clear documentation that states what it does, how it should be used and what its parameters are.
External Links
- MediaWiki Template documentation
- MediaWiki documentation for Magic Words
- MediaWiki documentation for ParserFuntions
- MediaWiki documentation for StringFuntions
- Web Design 101: How HTML, CSS, and Javascript work
- HTML 101: Coding for Non-Developers
- Beginners' Guide To Writing Good HTML
- Learn CSS in 5 minutes: A tutorial for beginners
- Visual Studio Code
- Wikitext Extension for VS Code