User:Steven Crossin/Adoption/AdvancedTemplates

Advanced Templates
Note: You should complete User:Steven Zhang/Adoption/Templates before taking this lesson.

In the previous template lesson, you saw how you could use template parameters to add custom information into a template message. In this lesson, we'll look at how to use other templates to make even more happen, depending on what you enter as a parameter. These other templates are called "ParserFunctions" and are built into the MediaWiki software that Wikipedia is based on. Because of that, you can't edit these templates by going to their template page (because there isn't one), and they also are called in a unique way.

Before we look at these, let's have a quick review of how parameters work, because many of these ParserFunctions are going to depend on them, and you using them correctly. You create a parameter by using three curly brackets around a name (or number), like so:. In that case, calling the template "example" would require you to use a "foo" parameter, like this:. That will cause the word "bar" to appear wherever you have the code " " in the template. If you forget the parameter, though, (just using ), bad things happen. Instead of a useful word, you get a big ugly in the middle of your message. We can avoid this by giving "foo" a default value with a pipe character: Now, instead of an ugly parameter, we get a helpful message that tells us exactly what went wrong and how to fix it. If we'd rather our templates not insult us when we mess up, we can make the default value simply not appear at all:. This works just as well, and in fact is exactly what we want to do for some of the ParserFunctions we're now going to look at.

; #if: ===== #if: =====

The most basic function we have available is. #if: probably looks fairly strange to you - since when do we start templates with a # sign? And what's with the colon? That is what tells us, and MediaWiki, that we're calling a ParserFunction instead of a normal template. Here's how #if: works: Huh?

#if: works a little differently than most "if... then..." structures work. #if: is set up like this: "If this space has something in it, I print this. If it's blank, I print that." How does this help us? Well, remember how we could set our parameters to have a blank default value? Imagine what would happen if I wrote this code: Now, when I call the template that uses this code, I will do one of two things. I will either enter a parameter or I won't. If I don't, this code will display "Goodbye!" because there is nothing displayed between #if: and the first option; we set our parameter 1 to be blank by default, so there is nothing but blank space for #if: to look at. However, if I do enter a parameter, regardless of what it is, that code will display "Hello!". This is because when #if: looks at what you gave it, there's something between it and the first option. It doesn't care what that something is, it just cares that something exists. But now, here's why we had that short review on parameters: The difference between these two sets of code is minor, but causes the whole thing to bork up. This time, there is no pipe in our parameter, so there is no default value. As a result, when we don't set the parameter in the template, #if: still sees right after its colon. So, regardless of what we do, we're always going to get "Hello!" as a result of this function.

; #ifeq: ===== #ifeq: =====


 * 1) ifeq: is a bit more useful. #ifeq: stands for "If equal" - instead of just checking to see if something exists, #ifeq: checks to see if that something is equal to something you specifically told it to look for. Here's how it works:

In the sample above, I want to see if the user typed "foo" as a parameter to my template. If they did, #ifeq: will see that and print out "Hello!". If they enter anything else, though, or in this case, nothing at all, #ifeq: will compare whatever they enter to "foo", see that they don't match, and print "Goodbye!" instead. ( bar =/= foo; =/= foo ) This code is a bit more "secure" - if you want the template to do something if the user enters "yes" as a parameter, #if: is not what you want to use. If you use #if:, it'll do whatever you told it to do even if the user enters "no". By using #ifeq:, the function will only do this thing if they enter "yes", exactly like that. It won't work even if they enter "YES", because uppercase letters and lowercase letters aren't the same.

But what if you don't want to risk confusing the user? What if you do want "YES" to work? It's pretty pointless to make an #ifeq: for every single different capitalization of "yes". There's two options available to you. One is to use another ParserFunction, which we'll get to shortly, which acts like a super #ifeq:, checking for multiple different parameter values at once. Another, much easier way, is to tell the parameter to use all uppercase or lowercase letters. How? Magic. Observe: You can use these codes (which are examples of some Magic words) on just about anything - including your parameters. Obviously, it won't have much of an effect on, but when your user types in "YES" when your #ifeq: is expecting to find "yes", adding the code  will solve all of your problems.

; #switch: ===== #switch: =====

This is my favorite ParserFunction (yes, I'm a geek, and you're not one to talk; you ARE reading this, after all). This is the "super #ifeq:" I mentioned earlier. #switch: allows you to check a single line of text for a practically unlimited number of possible results (That is, the limit is set so high there's no way you're going to design a practical template that uses that many options). It works like this: What this template does is this: It takes the value you enter (which is probably a parameter, which is probably forced to be either lower or upper case for the same reason it would be in #ifeq:) and moves down the list, comparing it to each possible value in turn. As soon as it matches something, it stops, and looks for the next equals sign. It then prints whatever you have between that equals sign and the next pipe. Let's look at an example, based on the above format: If I enter "foo", #switch: replies with "bar". Likewise, "ice" gets "cream" as a response, and "burnt" gets "toast". But "french" also gets "toast". This is because "french" doesn't have anything set specifically for it - there's no equals sign after "french". Because of this, #switch: is going to keep looking for the next equals sign, which is after "burnt". This makes sense for me, because I want that to happen. "burnt toast" and "french toast" both make sense. However, I do have to be careful about what order I put things in; this code may look similar, but will cause "french" to come out with a different result: Now, entering "french" will return "cream", because "ice = cream" is the next value in line for #switch: to find. For both of these, anything not listed in the ParserFunction will not return anything - nothing will be printed, because there is no default value. For #switch: to print something out regardless of what I type in, I would need to specify "#default = " at the very end of the template. There's really no technical reason why #default has to be at the end, but it just makes it easier for other users.

; #time: ===== #time: =====

Time is an interesting thing in how it is calculated and how it brings some order to our lives. Because of that, it's important we have a bit of code that allows us to display time however we would like. #time: is just that code, allowing you to enter your own custom time and change it however you wish. It's a very useful code, that you'll see used in many places thoughout Wikipedia - for example, proposed deletion templates "expire" after five days, and those templates use a #time: function to control that.

Time, of course, is rather complicated, and #time: itself is complicated to mirror that. Because there are many different ways to display the time, there are many different things you can tell #time: to do. Before we cover that, though, let's look at how #time: works: OR (to display the time at which the page was viewed) As you can see, there are two ways to set this code up. You can display the current time, or a custom time that you specify. This custom time can be simply a change in timezone, a certain about of time before or after the current time, or a fixed time that you set. Here are some examples below of how that works. You can ignore the formatting code for just the moment, we'll cover that shortly. Just focus on what I have entered in the time slot on the right hand side.

With me so far? You can do almost anything you want with the time that way, but there are some limitations to the template. For example, I was trying to set up a stopwatch here, that would display how many months, days, hours, and minutes had gone by since I saved the code onto the page. This is the code I tried to use:. There's nothing wrong with the format, and the time looks as though it might work, but instead I got this:. Obviously not what I wanted. The problem was that I didn't specify any units for it to subtract, and the number spits out is way to big to be considered a time zone. #time: is very finicky about what it will accept as a time - it has to be something it can easily recognize and use, or it's not going to bother. Here are some examples:

Now that you roughly know how to tell #time: what time to show, let's look at how to get it to show it (following my grammar lesson on antecedants; moving on...). You'll have noticed from above that I've been sticking what seem to be random letters in the format side of #time:. #time: appears to be written for the sole purpose of being confusing as hell, because few of the codes for the format make any sort of sense whatsoever. No, simply typing "day" won't work - if you're lucky, #time: will simply print "day" out and it won't look horrible, but it's also possible you'll get another big red error message. So what does it take? Let's figure it out:

Anything that doesn't appear in this list will generally be treated as what it actually is. So, you can wikilink dates by enclosing the format code in square brackets: produces. If, however, you want to type a letter that is in this list, you'll need to enclose it in quotes: comes out to:

To get the template to display as you intend it to, you'll need to use  or something similar.

Things get easier from here out, don't worry!

; #expr: ===== #expr: =====

This is the last ParserFunction we'll cover, although there are more: this is the last of the more commonly used ones, however. #expr: stands for "expression", referring to the mathematical sort. #expr: is your calculator, allowing you to play with parameters and variables to spit out something that may or may not be useful. It also can be used for logical statements, where 0 is considered false and anything else is considered true. Here's what you can do with it:

You can combine mathematical stuff and logical stuff in the same #expr:, as well as add parentheses to group operations - for example, will produce  ((30 + 2) / 16 = 32 / 16 = 2, which is less than 3, so false or 0). The function follows a specific order of operations, with all things going from left to right: Make sure to be careful about this; just as in school, failing to pay attention to order of operations can easily cause your equation to come out to something you didn't expect.
 * 1) Stuff in parentheses
 * 2) Positive and negative signs (+1, -1, not)
 * 3) Multiplication and division (*, /, mod)
 * 4) Addition and subtraction (+, -)
 * 5) Rounding with round
 * 6) Comparisons (=, <>, <, <=, >, >=, !=)
 * 7) and
 * or

Obviously there are some things you can't do with #expr: - it doesn't like letters, so using exponential formats such as 6.67E-11, or mathematical constants like e won't work. Also, it's limited by the usual laws of mathematics (for example, you can't divide by zero, etc.)

;Other ParserFunctions ===== Other ParserFunctions =====

There are a total of 5 other ParserFunctions I haven't covered. I'll list these below, but won't go into detail about them because they are rarely used outside of meta-level templates, such as db-meta. Each of these is either fairly basic (along with what you already know) or can be easily represented by using one of the functions already covered. If you have an interest in these templates, they, along with the ones mentioned above, are covered in full detail at m:Help:ParserFunctions (Note: this page is on MetaWiki).
 * #ifexist: Similar to #if:, this checks to see if a page exists at the given title, and returns one of two possible lines of text.
 * #ifexpr: Combines #ifeq: and #expr:, checks a mathematical or logical expression to see if it results in zero, and returns one of two possible lines of text.
 * #rel2abs: Converts a relative link to a direct link - for example, /Subpage is a relative link which can be converted to the direct link, User:Steven Zhang/Adoption/AdvancedTemplates/Subpage.
 * #titleparts: Returns a portion of a given page title as specified by the user.
 * #iferror: Checks ParserFunctions that could return an error message, including #time:, #expr:, #ifexpr:, and #rel2abs:, and returns a specified line of text if an error is the result.

;Templates and Tables ===== Templates and Tables =====

You've noticed that all of these functions use pipes, just like regular templates do. You've probably also noticed that most templates use tables to keep their formats in a readable order, and that these tables also use pipes. So, how does MediaWiki know when a pipe is a template pipe or a table pipe? Well, it doesn't.

Say you're setting up a template, that displays a table with an optional third row, triggered by the parameter. Here's the code you try: So, let's see what happens when we test this out. We made be blank by default, so we should see a table that has only two rows. Bleah. That's not quite what we wanted. Things came out this way because when we condense the #if: code to a single line, this is what we get:. #if: doesn't know that it's in a table. #if: just sees that for some reason it's being given four different bits of text to choose from. However, it only cares about the first two: the blank section between the first two pipes, and the dash between the second two. Oops. So how can we tell #if: it's in a table, and needs to ignore some of those pipes? We trick it.

The templates ! !! and !- are all designed for this purpose. Since we can't put an actual pipe in there and have it work, we fake it with another template. What happens is that #if: sees the template as a template, that is, like this:. Since that's not a pipe, it assumes that's part of the text you want it to print out. But when it prints that text out into the table, it strips the template down into what it actually is, in this case, a pipe |. Now it's the table's turn. It goes through and sees the code left behind by #if:. Since there's a pipe there now, it deals with it accordingly, and spits out the table as we wanted to see it. So, this is how our code above should be written:

And so we get: That's it! Remember, !- produces |-, ! produces |, and !! produces ||.

That's everything! Try testing out these templates in a sandbox, and seeing what all they can do. Once you're confident with what they do, feel free to add this userbox to your user page - you'll have earned it!