This page looks best with JavaScript enabled

If {{NAMESPACE}} do nothing, otherwise...

 ·  ☕ 9 min read

Say you have a template called item_infobox. It’s used on every item page on your video game wiki, and it shows information about stats that the item gives, what equipment slot it takes, etc. It also automatically adds the page to [[Category:Items]].

One day, you’re happily browsing your wiki, and you visit [[Category:Items]] for the first time in months. You’re shocked - there’s dozens of user pages there! That’s not good at all! How did this happen?

User sandboxes

Typically, MediaWiki editors use pages like User:RheingoldRiver/test item to sandbox possible changes. The number of such pages can grow rapidly, and in fact I encourage making many of these pages. But there’s a problem when your template does things that have “side effects” - things that affect the wiki on a global level.

Some side effects that could cause problems here include:

  • Adding categories
  • Storing Cargo or SMW
  • Overwriting LuaCache values
  • If you use DPL, just the act of transcluding the template can cause issues, which is why in DPL you should always include a namespace (unless you are specifically looking to include User pages)

Because the “User” space belongs to users, and having a dedicated sandbox space is very important to avoid polluting content/code namespaces, it’s on us as wiki administrators to ensure that templates are safe to use in the User namespace without creating side effects.

The “if” parser function

The MediaWiki extension ParserFunctions provides the first of two tools we’re going to use, a parser function called if.

You can read those docs for a thorough explanation, but in brief, #if checks an input value and determines if there’s any content there. If there is content there, then it outputs the first branch; and if not, then it outputs the second branch.

Here are a few examples:

  • {{#if:hello|cat|bat}} will output cat.
  • {{#if: |cat|bat}} will output bat (whitespace counts as being empty).
  • {{#if:{{{hello|}}}|cat|bat}} will output cat if the user passes a non-empty parameter called hello to the template, and otherwise it will output bat.

The NAMESPACE magic word

Our second tool is {{NAMESPACE}}. This is a magic word1 provided by the MediaWiki software that returns the name of the current page’s namespace. If you’re in the Template namespace, it returns Template; if you’re in the User namespace, it returns User; and if you’re in the main namespace it returns an empty string.

Putting it together

With these two tools, we have everything we need to protect ourselves against side effects during sandboxing! 99% of the time, you only want to do things like add categories in the main namespace,2 so the value of {{NAMESPACE}} will be empty. Then, instead of [[Category:Items]], you can write {{#if:{{NAMESPACE}}||[[Category:Items]]}}.

Adding a few comments to improve readability, we get the following:

{{#if:{{NAMESPACE}}
|<!-- do nothing, otherwise -->
|[[Category:Items]]
}}

What if I expect this to be used outside of the main namespace?

If you expect this to be used only in a single namespace, for example in the Category namespace, then you might notice that we cannot accomplish this with #if, because the “true” case is Category, which is not an empty string.

Fortunately, for this case we can use ifeq, which has the following logic:

  • If the first value equals the second value, print the third value
  • Otherwise print the 4th value

For example:

  • If you write {{#ifeq:hello|world|cat|bat}}, you will get bat, because hello and world are not equal.
  • But if you write {{#ifeq:hello|hello|cat|bat}} then you get cat.

Combining this with {{NAMESPACE}}, we get this:

{{#ifeq:{{NAMESPACE}}|Category|[[Category:Items]]
}}

In all but one namespace

If you want to exclude from User only, you can do:

{{#ifeq:{{NAMESPACE}}|User
|<!-- This time we want to AVOID user, so if it's equal to User then do nothing, otherwise -->
|[[Category:Items]]
}}

This method will work in many cases, but not all. For example, if you want to transclude your template as part of template documentation, you’ll get unwanted data from the Template namespace as well as from the User namespace.

In two or more namespaces

If you expect to use this template in two or more namespaces where you want side effects, say you want it to work “Category” and “Template,” then we COULD do it like this:

{{#ifeq:{{NAMESPACE}}|Category
|[[Category:Items]]
|<!-- it's not Category, but maybe it's template?
--> {{#ifeq:{{NAMESPACE}}|Template|[[Category:Items]]}}
}}

But, as you can guess, this is (1) hard to read and (2) can get out of control really quickly if you want to allowlist several namespaces.

Fortunately, there’s another parser function designed exactly for this case:3 switch!

We can write it like this:

{{#switch:{{NAMESPACE}}
|Category=[[Category:Items]]
|Template=[[Category:Items]]
|#default=
}}

Here’s how this evaluates:

  1. Evaluate {{NAMESPACE}}. This is the value we’ll compare to each of the cases.
  2. Does {{NAMESPACE}} equal Category? If so, print [[Category:Items]] and then return (don’t continue down this list).
  3. Does {{NAMESPACE}} equal Template? If so, print [[Category:Items]] and then return (don’t continue down this list).
  4. Okay, we didn’t find any match. Let’s do nothing.

Including the main namespace in a switch statement

If we want to add the main namespace to the above, then we can do this, with an empty string as one of the “compare to” values:

{{#switch:{{NAMESPACE}}
|=[[Category:Items]]
|Category=[[Category:Items]]
|Template=[[Category:Items]]
|#default=
}}

Switch syntax sugar

Syntax sugar refers to times when a coding languages gives you an easier way to type something without changing what’s happening at all.4 There’s a convenient syntax sugar for #switch in MediaWiki that lets us write:

{{#switch:{{NAMESPACE}}
|Category
|Template=[[Category:Items]]
|#default=
}}

Here, we’ve removed the equal sign after Category, so the compiler receives no instructions about what to do if it receives that value. So it keeps on going until it gets to the very next instruction that does have an equal sign, which in this case, is Template. Then it will add [[Category:Items]], just like how we wanted.

And yes, you can also include the main namespace like this:

{{#switch:{{NAMESPACE}}
|
|Category
|Template=[[Category:Items]]
|#default=
}}

Edge case: the Project namespace

If your wiki is called Cute Kittens Wiki, then you probably nave a namespace called Cute Kittens Wiki. This namespace is called the “Project” namespace, and it’s often used for meta information about the wiki. For example, you might want a page called Cute Kittens Wiki:Pet pictures policy that includes rules like, you cannot upload any picture where the kitten was put in distress in any way to coax behavior out of it.

If you evaluate {{NAMESPACE}} in this namespace, you’ll get Cute Kittens Wiki. However, this could be changed in the future, for example if you expanded your wiki’s mission and became the Cute Cats Wiki, or if you were endorsed by the newly-formed Cat Supergeniuses League that seeks to enslave all of humanity to the whims of our cat overlords, then you might become the Official Cute Kittens Wiki.

So, it’s not a great idea to write {{#ifeq:{{NAMESPACE}}|Cute Cats Wiki}}. Instead, I would recommend writing this:

{{#ifeq:{{NAMESPACENUMBER}}|4|[[Category:My Category Name]]}}

The namespace number of the project namespace is always 4, on every single MediaWiki installation. Normally, I don’t recommend using {{NAMESPACENUMBER}}, because names are a lot more user friendly than internal ID numbers, but in this one case the name is extremely subject to change, so it’s better to write code that won’t break. You can mitigate some confusion here with a comment:

{{#ifeq:{{NAMESPACENUMBER}}|4<!-- project namespace -->|[[Category:My Category Name]]}}

(As a note, you can write [[Project:Pet pictures policy|pet pictures policy]], and this has exactly the same effect as [[Cute Kittens Wiki:Pet pictures policy|pet pictures policy]]. You should ALWAYS ALWAYS ALWAYS ALWAYS use the Project: version when creating internal links to the project namespace.)

What about in Lua?

If you are adding categories or storing Cargo from Lua, I like to do this via a utility module that has a singleton with the correct namespaces. For example, in my Cargo utility module, I have this function:

1
2
3
4
5
6
7
8
function p.setStoreNamespace(ns)
	-- note this is a global variable, not local
	CARGO_NAMESPACE = ns
end

function p.setStoreBasePage(basepage)
	CARGO_BASEPAGE = basepage
end

And then storing looks like this:

1
2
3
4
5
6
7
function p.store(tbl)
	local title = mw.title.getCurrentTitle()
	if CARGO_NAMESPACE and title.nsText ~= CARGO_NAMESPACE then
		return
	elseif CARGO_BASEPAGE and util_title.titleTable(title.prefixedText)[1] ~= CARGO_BASEPAGE then
		return
	end

In addition to specifying a required namespace, you can also set a required basepage, for example only store to the NewsData table from subpages of Data:News. I’ve never needed to support storing in multiple namespaces with one template, but you could easily make this an array, too.

But I WANT to test Cargo in my user space!

If you are testing your template in your user space and want to make sure that Cargo is working, then I recommend adding a parameter |cargo= that can be set to yes and override the {{NAMESPACE}} check. There’s a couple ways you can implement it.

Using a switch statement:

{{#switch:{{{cargo|{{NAMESPACE}}}}}
|
|yes={{#cargo_store:
<!-- implement store here -->
}}
|#default=}}

If {{{cargo|}}} is yes then send an empty string to the outer #if statement, else check for {{NAMESPACE}}:

{{#if:{{#ifeq:{{{cargo|}}}|yes||{{NAMESPACE}}<!-- end ifeq -->}}
|<!-- do nothing, otherwise -->
|{{#cargo_store:
<!-- implement store here -->
}}
<!-- end if -->}}

Variable (#var:cargo gets set to yes if {{NAMESPACE}} is empty; otherwise it’s the value of {{{cargo|}}}):

{{#vardefine:cargo|{{#if:{{NAMESPACE}}|{{{cargo|}}}|yes<!-- end if -->}}<!-- end var -->}}<!--
-->{{#ifeq:{{#var:cargo}}|yes
|{{#cargo_store:
<!-- implement store here -->
}}
<!-- end ifeq -->}}

I like the 2nd method because it’s very easily modified to remove support for the |cargo= param when you’re done with it, but it can be a bit hard to understand. Pick the one that works best for you, or write your own using #switch, #var, #if, and/or #ifeq!

Keeping track of your sandbox pages

You may want to accompany your conditional store with some code like this:

{{#if:{{NAMESPACE}}|{{#ifeq:{{{cargo|}}}|yes|[[Category:Sandbox pages storing Cargo]]
<!-- end ifeq -->}}<!-- endif -->}}

This will let you monitor your sandbox pages via a utility category, Sandbox pages storing Cargo. You can expect this category to be empty unless you are actively sandboxing something.

Conclusion

This {{#if:{{NAMESPACE}}|<!-- do nothing -->}} pattern is a good way to practice defensive programming in MediaWiki. This article has been mostly about categories because I wanted to make sure anyone can understand it, but it’s even more important if you are using Cargo and #cargo_store. So be careful about any time your template has side effects, and happy coding!


  1. That page listing all the magic words (here it is again) is super helpful. You should bookmark it! ↩︎

  2. This is one of the reasons it’s discouraged to make “secondary content namespaces,” like Item: or Location: ↩︎

  3. Pun absolutely, gloriously intended (In many programming languages, for example JavaScript, the “switch” keyword is combined with another keyword called “case”) ↩︎

  4. For example, in Lua, normally you access table keys with the [] syntax, like p["kittens"] or p[catType]. In the first example, you retrieve the value with index kittens. In the second example, catType is a variable, so first we figure out the value of that variable. Then we use that value as the index at which to look up the final result in p. But there is a convenient syntax sugar that lets us write p.kittens in the first case, as long as there are no spaces or special characters in the string, and the first character is not a number. ↩︎

Share on

river
WRITTEN BY
River
River is a developer most at home in MediaWiki and known for building Leaguepedia. She likes cats.


What's on this Page