The UESPWiki – Your source for The Elder Scrolls since 1995
Jump to: navigation, search

MetaTemplate is a Mediawiki extension written for UESP that provides several template-related parser functions and tag extensions. These features are intended to permit more powerful templates, more legible templates, and data-sharing between articles. Better documentation for some of the features can be found at Magic Words.

This is one of several custom extensions on UESP: see UespCustomCode and Active Users for documentation on the other extensions.


Conditional Expressions[edit]

On older versions of Mediawiki, several of the parser functions added by this extension could not be used within conditional expressions (#if, #ifeq, #iferror, #ifexpr, #ifexist, #switch, etc.) because they would be evaluated for every argument to the function, regardless of the final result. This limitation applied to all of the functions that are classified as parser subroutines: #define, #preview, #local, #unset, #inherit, #return, #save, #load.

To provide an alternative way to conditionally execute the subroutines, they all recognize two conditional arguments:

  • |if=expression: the subroutine will only be executed if the expression evaluates to true (0, an empty string, or a string containing only spaces are false; any other string is true)
  • |ifnot=expression: the subroutine will only be executed if the expression evaluates to false

Only one of these arguments should appear for a single subroutine (if multiple if/ifnot arguments appear, only the last one will have any effect). The expression can be as complex as necessary; all that matters is the text displayed after the entire expression has been evaluated. If the same condition needs to be used multiple times, then it is probably most efficient to use #define to evaluate the condition once, then use the #define'd variable in each necessary subroutine.

Note: Ironically, it is now MetaTemplate itself that evaluates all arguments to its functions, even when the if/ifnot should prevent it. It's unclear when or why this changed. Example: {{#local:test|{{#local:test2|Hello}}World|if=0}}{{{test2}}} {{{test|}}} should produce nothing, but will in fact produce "Hello".

Reserved Words[edit]

Several words have special meaning for the MetaTemplate extensions. Some are used as flags for one or more extension functions, and therefore can never be used as variables for those functions. Other variables have special significance for the extension, and therefore can alter how features work. This is a complete list of words with a special meaning, and therefore there are limitations on how variables using those names can be used.

  • author: used as a special variable by #load, for backwards compatibility with current wiki pages. If a request is made to #load author data, but no entry for the requested article is found in the #save data, then #load will try to retrieve the author information from a /Author subpage.
  • case: used as a flag by subroutines such as #define and #inherit. Cannot be used as a variable within any extension functions or subroutines (even those that do not recognize case as a flag).
  • description: used as a special variable by #load, for backwards compatibility with current wiki pages. If a request is made to #load description data, but no entry for the requested article is found in the #save data, then #load will try to retrieve the description information from a /Description subpage.
  • if, ifnot: used as flags by many extension subroutines (see Conditional Expressions). Cannot be used as variables within any extension functions or subroutines (even those that do not recognize if/ifnot as flags).
  • namespace: used as a special variable by #listsaved and catpagetemplate
  • nestlevel: used as a special variable by NESTLEVEL. NESTLEVEL will return the value of nestlevel if the variable exists.
  • ns_base, ns_id: used as special variables by the namespace functions in UespCustomCode. Setting either of these variables will alter the default namespace used by all UespCustomCode functions.
  • order: used as a flag by #listsaved
  • pagename: used as a special variable by #listsaved
  • seed: used as an optional input parameter by #pickfrom. However, it can safely be used as a variable in all extension functions.
  • separator: used as an optional input parameter by #pickfrom. However, it can safely be used as a variable in all extension functions.
  • subset: used as special variables by all Data Sharing routines. If a variable named subset exists in a template that uses #save or #load functions, its value will be used by #save/#load as the setting for subset.


Variable Definition[edit]

All of the following commands are parser subroutines that alter the values of the template's (or the parent template's) variables. They always return an empty string. All of the subroutines recognize the |if= and |ifnot= arguments (see Conditional Expressions).



#define can be used to provide a default value for any template variable. The value is only assigned to the variable if the variable has not already been defined -- in other words, if the variable varname was not specified when the template was called.

It has two required arguments:

  1. The name of the variable
  2. The value that should be assigned to the variable

There are also three optional arguments:

  • |case=any: checks the existing variables to see whether other variable names are a case-insensitive match to the requested variable. If so, varname is set to be equal to the other variable's value; varvalue is only assigned to varname if no other matches can be found.
  • |if=<condition>: execution of the subroutine only occurs if the <condition> is true.
  • |ifnot=<condition>: execution of the subroutine only occurs if the <condition> is false.

At its most basic, a #define tag is equivalent to calling the template with an additional parameter: if it appeared in a template Test, then it would be equivalent to {{Test|varname=varvalue}}. However, #define tags can be used to do more than simply replace an input argument:

  • The arguments to a #define tag can contain any combination of other variables, parser functions, or other wiki code. In particular, any variables that have previously been defined anywhere in the template or the template's arguments will be recognized. Therefore, #define tags can refer to any of the template's input arguments, and can be used to combine or process those arguments.
  • The processing required to parse the arguments is only done once (no matter how many times the #define'd variable is subsequently used), therefore #define'd variables can increase the efficiency of templates.

Each of the following examples assumes that the example #define statement appears in a template named Test, which has been called using the code shown under Template call.

Example Template call Result of {{{text}}} Explanation
{{#define:text|foo}} {{Test}} foo Equivalent to calling the template using |text=foo.
{{#define:text|foo}} {{Test|text=bar}} bar The value used when calling the template takes precedence over the #define statement
{{#define:text|foo}} {{Test|TEXT=bar}} foo The template variable is not recognized because its case is different
{{#define:text|foo|case=any}} {{Test|TEXT=bar}} bar Since case=any has been specified this time, TEXT is considered equivalent to text
{{#define:text|{{{alttext}}}}} {{Test|alttext=bar}} bar Since alttext is defined but text is not, text takes on alttext's value.



#local provides a non-default value for any variable. It is nearly identical to #define, except that a #local variable overrides any definition for that variable that was provided when the template was called.

Example Template call Result of {{{text}}} Explanation
{{#local:text|foo}} {{Test}} foo Identical to a #define call
{{#local:text|foo}} {{Test|text=bar}} foo Unlike #define, the value used when calling the template is overridden by the #local statement



#preview is another near-synonym for #define, the only difference being that a #preview tag only takes effect when you are previewing a template.

This tag can be used to test exactly what a template will look like for a given set of input arguments. The advantage of using #preview instead of #define for such tests is that if the template is accidentally saved without removing them, the #preview statements will have no unwanted side-effects on pages that use the template. Note that #preview only works in Template space.



#inherit tells a nested template to inherit the values for all of the listed variables from the template(s) that called it.

As many variable names as necessary can be listed in an #inherit statement. There are also three optional arguments:

  • |case=any: case-insensitive matches are used for all of the listed variables (whether they appear before or after the |case=any argument). If some variables should be case-sensitive and others case-insensitive, then two separate #inherit statements are necessary.
  • |if=<condition>: execution of the subroutine only occurs if the <condition> is true.
  • |ifnot=<condition>: execution of the subroutine only occurs if the <condition> is false.

#inherit will check recursively through the entire stack of templates to find any definition of a requested variable. It starts by checking the template itself (i.e., a #define statement preceding a #inherit statement will take precedence, as will any value provided when calling that specific template); it then checks the parent template (the template that called the one containing the #inherit statement); then the grandparent, etc. It will recognize variables defined by any possible method (#define, #local, #inherit, #return).

Note: MediaWiki caches template calls with no parameters and returns the same text for each call. If you're #inheriting variables in a parameterless template and the values may change between calls, add a dummy parameter to the call to prevent the caching (e.g., {{ParameterlessInheritTemplate|dummy=}}).



#return copies the values of all of the listed variables to the parent template. If the variable has already been defined in the parent template, #return will override that existing value.

Note that execution of a template does not stop at a #return statement. If the specified variables are subsequently altered in the template, the altered values are not copied to the parent template.



#unset unsets all listed variables. It does not just set the variable to an empty string, but instead erases the variable, as if it had never been set.

Data Sharing[edit]

These functions allow template variables from one page to be used on any other page. They provide an efficient mechanism for sharing information between pages, and therefore make it unnecessary to create stub-like subpages (/Description, /Author) for that purpose.


Example: {{#save:description|author}} could be placed in Template:Lore Book, which would result in the description and author variables being saved for every book page that directly or indirectly includes the Lore Book template.

Each of the variables listed in the parser function is written to the mt_save_data database table, using the current article's name as the filename under which the variables are saved. As many (or as few) variables as needed can be listed in the parser function.

Optional arguments for #save are:

  • if and ifnot: enable/disable the entire function, see Conditional Expressions for details.
  • subset: can be specified if there are multiple sets of data that need to be #save'd for a single article. After subset has been specified for a #save call, that is taken to be the default subset for any subsequent #save calls (until another value of subset is provided). The default value for subset (used on all articles where subset is never specified) is an empty string. Any arbitrary string, up to 20 characters in length, can be used for a subset name.

Additional information:

  • The variables are saved after being fully expanded and parsed. Therefore, if the variables contain complex processing, the processing does not need to be redone by any articles that load the variables.
  • Variables should never be named "subset", to avoid conflicting with the API's representation of subsets.
  • #save parser functions are always ignored when saving template pages and special pages. This prevents unwanted entries from being added to the database.
  • #save parser functions can be used on redirect pages, even if the #save function appears after the #REDIRECT command.
  • The actual #save operation (writing the data to the database) is only done once per savefile, no matter how many #save commands appear in the savefile. The #save database operation is done after the wiki has saved the article itself to the database. This ensures that the #save database has the correct file revision ID; it also ensures that #load commands can safely be executed concurrently (in separate apache threads) with #save commands without extended database locks.


Example: {{#load:{{NAMESPACE}}:{{{title|{{{1}}}}}}|description|author}} could be placed in Template:Book Link, in order to load the description and author variables for the requested book.

Each of the variables listed in the parser function is read from the mt_save_data database table, using the specified filename. The variables can subsequently be used just like any other template variable. As many (or as few) variables as needed can be listed in the parser function.

Optional arguments for #load are:

  • if and ifnot: enable/disable the entire function, see Conditional Expressions for details.
  • subset: can be specified multiple sets of data that have been #save'd for the requested article, to specify which of the subsets should be #load'ed. The default value for subset is an empty string.

Additional information:

  • #load recognizes redirects: if filename is a redirect and there is no entry for filename in the database, then #load will check for data using the redirect article name. As with standard wiki usage, only a single redirect is followed.
  • A temporary feature is that #load will also read the contents of a /Description or /Author subpage if necessary. Specifically
    • if a #load command requests either the "description" or "author" variable, and
    • if the specified filename has no entry in the mt_save_data database table (there must be no entries, for any variables, not just no entry for the description or author)
    • then the #load command will check whether an article filename/Description (for description) or filename/Author (for author) exists; if the file exists, then its contents are transcluded into the variable.
  • Each #load command causes at least one database read. However, multiple variables in a single #load command do not cause any extra database reads. Therefore, it is much more efficient to pass a long list of variables to a single #load command than to use a separate #load command for each variable.
    • This is a change from how the functions were originally written. The change was introduced in response to the possibility that a #save file could have dozens of variables in the database -- only two or three of which might be needed by an article that #loads the data. In that case, #load'ing all of the variables would be inefficient; for an article such as Oblivion:Places that could mean processing and storing in memory hundreds or thousands of unnecessary variables. Furthermore, there is no reason why multiple #load commands should be necessary for a single savefile, so there is no advantage that could justify the wasted processing.
  • #load will not override existing variables of the same name.


Example: {{#listsaved:Place Link|type=Ayleid Ruin|namespace=Oblivion|description|maplink}} would create a list of all Ayleid Ruins in Oblivion, formatting each place using the Place Link template.

#listsaved is a parser function that prints a list of pages with #save'd data that meet specified criteria. Each page is printed by passing the requested list of #save'd variables to the requested template.

The function requires the following arguments:

  1. The template that should be called to print each page
  2. The remaining arguments should be a list of variable names. All of the variables in the list are passed to the template each time it is called.
    • If a value is provided for a variable, then the variable is a condition that determines the pages included in the list: only pages for which the variable equals the requested value are included. At least one condition must be specified for any #listsaved call. If more than one condition is provided, then all of the conditions must be met. For example, |type=Ayleid Ruin|namespace=Oblivion, means that type must be set to "Ayleid Ruin" and namespace must be set to "Oblivion" for an article to be a match.
    • If any of the variables do not exist in a given page's #save'd data, then the function checks to see whether those variables have been defined in the article where #listsaved is being called. If so, that value is passed to the template; if not, then the variable is not passed to the template. This feature can be used to pass any additional arguments to the template. Note that this function's treatment of already-defined variables is the opposite of #load (and several other MetaTemplate functions): in #listsaved, a #save value is given priority over any pre-existing definition of the variable; in #load, the pre-existing definition is given priority over any #save value.

There are three optional arguments to the function:

  • if and ifnot: enable/disable the entire function, see Conditional Expressions for details.
  • order: specify variable(s) that should be used to sort the list of pages. The default is to sort according to pagename and subset. (Note: this currently only works for the built-in variables like namespace, pagename, and subset. Saved fields do not work.)

Several variables have a special meaning for #listsaved:

  • namespace can be used as a condition for the function (although it cannot be the only condition). namespace is not expected to be a #save'd value for the article; rather namespace is determined directly from the article name.
  • pagename, namespace, and subset are variables that are always passed to the template, whether or not they are specified in the argument list, and whether or not the variables exist in the #save data for the article (in fact, using these variable names for #save data is not recommended). The values are taken from the article data, and correspond to the {{NAMESPACE}}, and {{PAGENAME}} variables for the article.

Additional information:

  • #listsaved retrieves all of the #save data, for all of the selected pages, in a single SQL query. Therefore, #listsaved will result in fewer database reads than a manual list of articles, where each article requires its own #load function. However, the single SQL query used by #listsaved is a complex query that may potentially take longer to process than multiple #load SQL queries. The problem is that a simple SQL WHERE clause cannot be used to generate the list of articles; instead each condition (other than namespace conditions) requires an additional table JOIN.
  • The options for the #listsaved conditions are limited, intentionally. If more complex conditions are necessary for a #listsaved call, then a new #save variable should be created to make the #listsaved call possible. For example, if a list containing both Ayleid Ruins and Forts was needed, it is not possible within #listsaved to request "(type=Ayleid Ruin) OR (type=Fort)". Instead, a new #save variable can be created, for example "alttype=Ruin or Fort".


  • #save/#load variables will automatically update as expected in nearly all situations:
    • Changing an article with #save'd data will automatically force any pages that #load the data to update: if the description variable is changed on Lore:King, then Lore:Books_K will automatically retrieve the new description.
      • This is accomplished by making the wiki think that the #save file is transcluded in any files that #load its data. In the above example, Lore:Books_K would be listed in Lore:King's "What Links Here" list as a transcluding article; Lore:King would appear in the list of "Templates used on this page" for Lore:Books K. This is purely a book-keeping trick. Lore:King is not actually transcluded in Lore:Books_K (this can be confirmed by looking at the template processing stats hidden in the HTML for Lore:Books_K; "Template argument size" is much smaller than it would be if actual transclusions were being done).
    • #save'd data is correctly updated if the article is renamed or deleted.
    • #save'd data is always updated if the article is purged.
    • #save'd data is automatically deleted from the database if the #save tag is deleted from the article.
    • #save'd data is automatically updated if any templates on the page are updated.
  • However, there is one slight glitch with automatic updates of variables
    • #save'd data is not automatically recreated if an article is undeleted. The undeleted article needs to be purged or edited to recreate the article's database entries. This is an omission due to laziness: is it really worth coding in undelete functions, especially given that an article is likely to be edited soon after being undeleted?
  • <nowiki>...</nowiki> tags are stripped within any saved variables, in both the save document and the load document. In other words, the nowiki tags are effectively ignored; the text within the tags will be wikified.
    • However, the nowiki tags are only removed immediately before the variable is saved. Therefore, nowiki tags can be used if there is a need to force specific variables or templates to be expanded after the variables are #loaded.
      • For example, if {{NAMESPACE}} is used in a saved variable, normally NAMESPACE would always provide the namespace of the original save file -- ignoring the namespace of the file that #loads the data.
      • To instead force NAMESPACE to provide the namespace of the final article, even after being loaded, then the original variable definition should use <nowiki>{{NAMESPACE}}</nowiki>.
    • This behavior is definitely unusual, but it's basically an attempt to make the worst of a bad situation.
      • nowiki tags do not like the #save/#load processing. Without any special treatment, any nowiki'd text would be replaced by mangled nonsense (e.g., "UNIQ69c231c93c7370c8{{#if:{{{dosave|}}} | {{#save:variable}} }}") in any #load documents.
      • To make nowiki tags work "as usual" would require special treatment before a variable is saved, then more special treatment after the variable is loaded -- special treatment that would require extra processing for every single variable, not just those variables that contain nowiki tags.
      • Effectively, only half of that special treatment has been implemented, namely, the check for nowiki'd text before a variable is #save'd. This unmangles the nowiki'd text before saving it to the database; it does not reinsert the nowiki tags.
      • The decision to not do any special processing after #load'ing the variables is intentional. It is assumed that #load commands may appear multiple times on pages that need a lot of processing, including some traditional "problem" pages (Oblivion:Merchants, Oblivion:Places). Therefore, including extra processing (that is hardly every going to be used) in the #load command may have a noticeable negative impact on site performance. Furthermore, a mechanism was needed for occasionally preserving wiki tags in #save variables (the {{NAMESPACE}} example is likely to be needed on Lore book pages); other ways of accomplishing that goal would be difficult to implement. The upshot is that processing is improved and a new capability is added -- at the expense of eliminating the standard nowiki function within #saved variables.
  • Any other nowiki-like tags that "hide" text from the wiki parser are also broken. For example, <pre>...</pre>, <math>...</math>, <cleanspace>...</cleanspace>, and HTML comments.
    • All content within such tags is removed before the value is #save'd. This is the exact same problem that affects nowiki tags, but I'm handling it more simplistically in these cases.
    • The problem is that, during parser processing any such tags get replaced by a mangled "UNIQ...QINU" string (a marker for text that is being hidden from the parser). The original template knows the full context, so it restores the hidden text after parsing is done. But when #load'ed, all information about the hidden text is lost.
      • Instead of even bothering to save the mangled "UNIQ-QINU" string to the database, the code is now completely removing that marker. So in the end there is no visible sign that there was a chunk of problematic text.
    • In general, use of tags like these should be avoided in any templates that are going to be used within #save'd variables
    • However, in some cases, the problem tags can be used completely safely. The basic test is: does the #load'ed value content the correct text, or is it missing a chunk of text?
      • HTML comments can always be safely used in templates (they won't appear in the HTML source code, but presumably they don't need to).
      • If cleanspace tags are being used solely to handle a chunk of initialization (using #local, #define, #preview, etc. to set variables) and does not end up generating any actual text, then cleanspace tags can be used in templates without any problems. Just double-check that the cleanspace tags do not also contain categories.
  • There are actually two database tables associated with these functions: mt_save_set and mt_save_data. mt_save_data stores the actual variables; a numeric index, mt_save_id identifies the savefile. mt_save_set contains a single entry for each savefile, translating the savefile name into the numeric ID used in mt_save_data. mt_save_set also contains bookkeeping data necessary to ensure that database entries are cleaned up when necessary -- namely, the article revision ID and a timestamp.

Tag Functions[edit]

  • A displaycode tag function, whose only purpose is to alter how the template is displayed on the template page.
    • It's specifically intended for templates such as Hide -- where the template page tells you nothing, so the only way to see what the template does is to edit the page. Putting <displaycode>...</displaycode> around the code on the hide template would act like a pair of nowiki tags -- but only when looking at the template page.


The catpagetemplate tag can be used on category pages to customize the appearance of the listed articles. (MetaTemplate version 1.0)

The contents of the tag should be template code. The code is executed once for each article listed in the category; each time, the parameter 'pagename' is set to the name of the article and 'sortkey' is set to the article's sortkey. By default, the text produced by the code is used for the article's label. In addition, #define statements can be used to customize many other aspects of the category listing.


  • <catpagetemplate>{{#label:{{{pagename}}}}}</catpagetemplate>
    • The displayed text for each page would no longer contain any disambiguation data (e.g., "Oblivion:Umbra (person)" would be displayed as "Oblivion:Umbra")
  • <catpagetemplate>{{#define:catgroup|{{#sub:{{{sortkey}}}|0|4}}}}{{#define:catlabel|{{#sortable:{{PAGENAME0}}}}}}</catpagetemplate>
    • The displayed text would no longer include the namespace, and the text would be in "sortable" format
    • The first four letters of the articles would be used to create the sections on the category page, instead of just the first letter
  • <catpagetemplate>{{#load:{{{pagename}}}|catgroup|catlabel|catanchor}}</catpagetemplate>
    • #save'd data would be loaded to get the previously-determined values for catgroup, catlabel, and catanchor (catanchor would allow the category to link directly to the appropriate section of the page).

To limit wiki processing requirements, complex processing would not be appropriate in a catpagetemplate tag -- especially since category pages are never cached. One possibility is to use a #load statement to fill in the variables, allowing any work to be done on the individual pages, instead of within the category.

Parameters with special meaning when #define'd within catpagetemplate:

  • Several parameters combine to create the text displayed for a given article; the result is equivalent to:
    • {{{cattextpre}}} [[{{{catpage}}}#{{{catanchor}}}|{{{catlabel}}}]] {{{cattextpost}}}
    • catlabel: the text to use for the article's label; overrides the output text of the tag. The advantage of using catlabel is that the contents of the catpagetemplate tag can then contain extra line breaks or even comments. The disadvantage of using catlabel is that any error messages produced when the template is parsed are lost, making debugging more difficult.
    • cattextpre: text to display before the link to the article; default . A space is automatically added in between the text and the link.
    • cattextpost: text to display after the link to the article; default . A space is automatically added in between the text and the link.
    • catpage: the full name of the article; defaults to {{{pagename}}}. In most cases, there should be no reason to set this parameter. If it is reset, there could be some minor anomalies because the code does not update various secondary settings based on a new value of catpage (e.g., the code does not check whether the page is valid, so the resulting link will be a standard blue link even if the page is non-existent; if a redirect is replaced with a non-redirect, the link will still be italicized, unless catredirect is also set).
    • catanchor: the section of the article to which the link should point; default .
  • catgroup: the group within which the article should be placed; defaults to the first letter of the sort key. catgroup can be a full word, or multiple words; there is no one-letter limitation. Note, however, that the articles are not re-sorted according to their group, therefore catgroup must be some logical derivative of the category sort key.
  • catredirect: is the link a redirect? This can be used to override the italicization of redirects, although normally it probably makes no sense to override the default values.
  • Note, there is no catsortkey parameter; no mechanism has been added for changing the order in which articles appear on the category. The problem is that implementing such a parameter would be incredibly inefficient on large categories (ones with more than 200 articles), because all the articles would have to be read, processed, resorted, and then refiltered to the correct subset. It's better to find a way to correctly set the original sortkey.

Input values to catpagetemplate:

  • {{{pagename}}} is the article's full name
  • {{{sortkey}}} is the article's sort key
  • In addition, the article is added to the current template stack. This means that {{PAGENAME0}}, {{FULLPAGENAME0}}, and {{NAMESPACE0}} provide information about the article (unless catpagetemplate calls subtemplates, in which case {{PAGENAMEx}} and the equivalents could be used).

Other points:

  • This feature has been set up so it doesn't eliminate the category changes introduced by the CategoryTree extension. However (at the moment at least), catpagetemplate cannot be used to customize category trees. The options for displaying subcategories on the page are
    • without a catpagetemplate, CategoryTree will generate the subcategories
    • using <catpagetemplate stdsubcat=1>, CategoryTree will generate the subcategories
    • otherwise, catpagetemplate will generate the subcategories
  • When #load is used within catpagetemplate, the code will check to see whether there are multiple subsets for the page, and if so, will try generating a category page entry for each of the subsets. For example, if several sections of the page should each be listed separately in a category, this provides a mechanism for doing so. [Except this is where catsortkey might be needed... I'm still pondering the details of this feature]



  • A NESTLEVEL magic word that tells you whether or not the template is being called directly.
    • This is a somewhat more obscure feature, but since I'd been using nestlevel behind the scenes for some of these features, I figured I'd make it available in case anyone else has uses for it. When generating the template itself, {{NESTLEVEL}} returns 0. When the template is being called directly from an article, NESTLEVEL is 1. If the template is being called from another template, that is being called from an article, NESTLEVEL is 2, etc.
    • If a variable named nestlevel exists, then the NESTLEVEL magic word will simply return the value of nestlevel. This feature was implemented so that templates whose behavior varies depending on NESTLEVEL can be tested/previewed.

Namespace and Pagename[edit]

  • NAMESPACEx, PAGENAMEx, and FULLPAGENAMEx variables (technically these are parser functions, but they are set up to be called as {{PAGENAMEx:arg}} instead of {{#PAGENAMEx:arg}} so that they are as similar as possible to the base variables)

Special Pages[edit]


This special page allows you to see the #saved variables on a page. Simply enter the full page name and, if desired, the maximum number of items on any given page, and the results will be displayed.

Caution: When paging through data, a full subset will always be returned as the first result. This will be the same subset as the last item on the previous page, even if the subset was shown there in its entirety. On pages with large enough main sets or subsets, you could end up simply paging through the same data repeatedly and will have to increase the number of results returned in order to continue. The default value of 50 is large enough for all but the largest pages.


This special page lists all pages containing a specific variable and/or subset. Either the subset name or a variable name must be provided. If you want to look for a variable within a specific subset, both can be entered. You cannot leave the subset at "Any" while the variable name is blank.

For the variable drop-down box, the 25 most common variables will be listed. For the namespace drop-down box, only namespaces that have actual #saved data in them are listed. Note: This data is updated every time the special page is viewed, which is not optimal on a large wiki with many users, but should pose no problems for UESP.


MetaTemplate includes an API property module which allows you to query pages for all #saved values, or for specific subsets or values. The module can be requested using a standard prop parameter, using the name metavars. Parameters are listed below. Maximum values are listed as <value for most users>/<value for high-limits users>. On UESP, bots and admins both have the high-limits right.

Parameter Name Generator Parameter Name Purpose
mvcontinue gmvcontinue When more results are available, use this to continue. Normally only emitted by the generator, but can be emitted by the standard module if result set is very large.
gmvlimit The maximum number of pages to return (default: 10; max: 500/5000). Note that this parameter is enabled only in generator mode. Limiting standard results is possible, but not particularly useful in most instances. Subsets and variable names provide more meaningful limitations. This also follows the behaviour of the similar prop=pageprops module, which doesn't support limits either.
mvsubset gmvsubset Only subsets with the names specified will be returned. In generator mode, only pages with the subsets specified will be returned. Supports multiple values (max: 50/500) using standard pipe separation. To get the main set of variables, use an empty value. To get only the main set, use mvsubset=| (the trailing pipe will be ignored).
mvvar gmvvar Only variables with the names specified will be returned. In generator mode, only pages with the variables specified will be returned. Supports multiple values (max: 50/500) using standard pipe separation.


Query parameters shown are limited to those that matter and separated for readability. Note: the queries below include &format=jsonfm to make them more human-readable; &format=json is the preferred format for programmatic use, and is the default in MediaWiki 1.25 and above.

Moved to Riven[edit]

The following features have all been ported to Riven. See that page for the current version of these functions.



#explodeargs is derivative of #splitargs that can be used to repeatedly call a subtemplate based on the elements of an exploded string. The return value of the parser function is the text generated by all of the subtemplate calls.

The first four arguments to explodeargs must be:

  1. The string to explode. Spaces surrounding the delimiter in the string are effectively ignored, since they're passed on verbatim to the wiki parser, which ignores them.
  2. The delimiter to explode on. Spaces in the delimiter itself are taken into consideration when exploding the string.
  3. The name of the subtemplate (the template which is to be called)
  4. The quantity of unnumbered arguments that should be passed to the subtemplate each time it is called. The quantity must be a positive integer or zero, otherwise the template will stop processing and return an empty string. If zero, every element of the exploded string will be passed as an argument to the template.

If additional arguments are provided, then the remaining arguments are used as the list of arguments which should be passed to the subtemplate. If no additional arguments are provided to #explodeargs, then the parent template's arguments are used as the list of arguments to be passed to the subtemplate. In other words, the template can be used in either of the following ways:

  • {{#explodeargs:<string>|<delimiter>|<subtemplate>|<nargs>|<arg1>|<arg2>}}
  • {{#explodeargs:<string>|<delimiter>|<subtemplate>|<nargs>}}
    • If this code appeared in a template called "Explodeargs", then {{Explodeargs|<string>|<delimiter>|<arg1>|<arg2>}} would produce the exact same result as the first case.

Any named arguments within the argument list will be passed, as named arguments, to the subtemplate each time it is called. The unnamed arguments are split into sets of <nargs> arguments; each set of arguments is then passed to the subtemplate in order. If no unnamed arguments are found, the subtemplate is not called (even if there are named arguments). The number of arguments which can be passed to #explodeargs is unlimited.

Example: Equivalent to:
{{#explodeargs:Here, There|,|Testexplode|1}} {{Testexplode|Here}} {{Testexplode|There}}
{{#explodeargs:Here,There|, |Testexplode|1}} {{Testexplode|Here,There}}
{{#explodeargs:{{{string}}}|,|Testexplode|2}} if {{{string}}}=Here, There, Everywhere, Nowhere {{Testexplode|Here|There}} {{Testexplode|Everywhere|Nowhere}}
{{#explodeargs:HereNowhereThere|Nowhere|Testexplode|1|param=extra|param2=more}} {{Testexplode|Here|param=extra|param2=more}} {{Testexplode|There|param=extra|param2=more}}
{{#explodeargs:Here.There.Everywhere|.|Testexplode|3|4=Nowhere|5=.}} {{Testexplode|Here|There|Everywhere}} {{Testexplode|Nowhere|.}}
  • In the previous examples, spaces have added occasionally between the individual calls to Testexplode purely to improve legibility; those spaces would not really appear in the code.
  • In the second example, there is a space in the delimiter which does not appear in the string, so the explode function sees only a single value, instead of two, and passes that value to Testexplode.
  • In the final example, '4=Nowhere' means that 'Nowhere' is assigned to be the 4th unnamed argument, becoming the first argument in the second call to Testexplode. '5=.' means that the next parameter will be a period; it will not be parsed as a delimiter, since the string is exploded before additional arguments are considered.
  • Technical Note: The parameters are, in fact, passed by number, not anonymously.


  • A #include parser function that allows subpage transclusion without invisible links.
    • For example, {{#include:Place Summary/Skyrim {{{type}}} }}, if type=Hold, would transclude the Place Summary/Skyrim Hold sub-template, but if type=Inn, nothing would be transcluded and no wanted template would be listed on Wanted Templates.



#pickfrom is a parser function that randomly selects npick entries from the subsequent list of arguments and prints those entries in a random order. Any number of arguments can be provided.

Required arguments:

  1. The number of entries to print
  2. The remaining arguments are the list of strings that are to be printed

Optional arguments:

  • seed: can be used to provide a seed for the shuffle function used to randomize the list of entries. By default, the current timestamp is used as the seed, which means a different result will be returned every second. The wiki's date and time variables can be used to create a seed that will make the result change less frequently. For example, |seed={{LOCALWEEK}}{{LOCALYEAR}} will make the list change once per week. The "LOCAL" time functions are preferable to the "CURRENT" functions, because they return the same value for all site readers.
  • separator: can be used to provide any characters that should be printed in between each of the selected entries. The default value for separator is '\n', i.e., a newline character. C-style escape sequences are recognized (e.g., '\n' for newline, '\t' for tab). To include spaces in the separator, it needs to be enclosed in quotes (single or double); otherwise the wiki processing will automatically strip any spaces from the end of the string. For example, |separator=' ' will add a single space in between all strings; the pair of quote marks is not considered part of the string.

If npick is larger than the number of entries provided to #pickfrom, then all of the entries will be printed in the original order -- when all of the entries are printed, they are not randomized.


  • A #trimlinks parser function. This one is really a parser function ;) It takes the provided text and strips out all links, leaving just the label.
    • The raison d'etre for this function is the place description pages. With this function, we can freely add links to any and all place descriptions -- then use #trimlinks on Oblivion:Places or other pages where the links are overwhelming. In fact, the links on Oblivion:Places can be turned off by adding a single line at the top of the page (we don't have to modify every place link template on the page), namely {{#define:trimlinks|true}}. To demonstrate how all of these ideas can start to work together, here's one possibility for what the corresponding Template:Place Link template would look like, fully revamped:

{{#define:place|{{{1}}} }}
{{#define:srcpage|{{NS_FULL:{{{game|}}}}{{{place}}} }}
{{#define:altname|{{#label:{{{place}}}}} }}


                                     |{{{description}}}}} }}
{{#define:maplink|{{#ifeq:{{{mapname|}}}|none| |{{Map Link|ns={{{game|{{NAMESPACE}}}}}|place={{{mapname}}}}}}} }}
</cleanspace>'''[[{{{srcpage}}}|{{{altname}}}]]'''{{#if:{{{desc|}}}| — {{{desc}}}}}{{#if:{{{maplink|}}}| {{{maplink}}}}}<noinclude>



#splitargs is a parser function that can be used to repeatedly call a subtemplate, using a different set of arguments each time the subtemplate is called. The return value of the parser function is the text generated by all of the subtemplate calls.

The first two arguments to splitargs must be:

  1. The name of the subtemplate (the template which is to be called).
  2. The quantity of unnamed/unnumbered arguments that should be passed to the subtemplate each time it is called.

If quantity is a positive integer, the unnamed arguments of either the call or the parent template (see below) are then split into sets of nargs arguments, with each set being passed to consecutive calls to {{subtemplate}}. Any named arguments within the argument list will be passed to the subtemplate verbatim each time it is called. If no unnamed arguments are found, the subtemplate is not called, even if there are named arguments. The number of arguments which can be passed to #splitargs is unlimited. If nargs is not a positive integer, the template will stop processing and return an empty string.

There are two ways in which the function can be used:

With Arguments[edit]

If additional arguments are provided as part of the #splitargs call, then those arguments are used as the list of arguments which should be passed to the subtemplate. For example:

{{#splitargs:Subtemplate|2|<arg1>|<arg2>|<arg3>|<arg4>}} would return:

Without Arguments[edit]

If no additional unnamed arguments are provided to #splitargs, then the parent template's unnamed arguments themselves are used as the list of arguments to be passed to the subtemplate. So, if a parent template contained the following code:

{{#splitargs:Subtemplate|2}}, then you could call that template as follows:
{{Parent Template|<arg1>|<arg2>|<arg3>|<arg4>}} to produce:
Example: Equivalent to:
{{#splitargs:Testsplit|1|a|b|c|d}} {{Testsplit|a}} {{Testsplit|b}} {{Testsplit|c}} {{Testsplit|d}}
{{#splitargs:Testsplit|2|a|b|c|d}} {{Testsplit|a|b}} {{Testsplit|c|d}}
{{#splitargs:Testsplit|3|a|b|c|d}} {{Testsplit|a|b|c}} {{Testsplit|d}}
{{#splitargs:Testsplit|1|a|b|c|param=extra|param2=more}} {{Testsplit|a|param=extra|param2=more}} {{Testsplit|b|param=extra|param2=more}} {{Testsplit|c|param=extra|param2=more}}
{{#splitargs:Testsplit|3|8=a|b|c|d|e}} {{Testsplit|b|c|d}} {{Testsplit|e}} {{Testsplit||a}}
  • In the previous examples, spaces have added occasionally between the individual calls to Testsplit purely to improve legibility; those spaces would not really appear in the code.
  • In the final example, '8=a' means that 'a' is assigned to be the 8th unnamed argument; 'b' therefore becomes the first unnamed argument. Arguments 5, 6, and 7 are missing and therefore treated as empty strings.
  • Technical Note: The parameters are, in fact, passed by number, not anonymously.

Tag Functions[edit]

  • A cleanspace tag function, which was already used in the above example. Within a pair of <cleanspace>...</cleanspace> tags, any white space in between tags is removed before the page is rendered.
    • Therefore, #define tags can each be placed on separate lines, #define tags can be separated from #load tags by a blank line, etc. -- all of which make the template more legible.
    • Another effect is that any categories or breadcrumb trails within the cleanspace tags are disabled on the template page (except in preview mode). In other words, the cleanspace tags can replace the includeonly tags that are often used on templates, preventing unnecessary redlinks and non-existent categories from being added to the template pages. However, one critical difference is that the categories and trails are displayed in preview mode -- allowing full debugging of those features, whereas right now mistakes generally don't become visible until after the template has been saved.
  • A cleantable tag function that cleans up any tables found within the <cleantable>...</cleantable> tags. Specifically, if a row is empty, the row is deleted from the table. If all of the rows in a table section are deleted, then the section header is also deleted.
    • This allows infobox templates to be significantly simplified. It is no longer necessary to embed entire sections of the table in #if statements, which in turn means that templates such as {{!}} no longer need to be used to build the table, and means that getting the spacing correct within templates is no longer such an ordeal.


Version 1.0[edit]

  • Jump to new major version number primarily in preparation for posting code on mediawiki and to reflect that code is relatively stable
  • New feature: catpagetemplate
  • Fixed various cleantable bugs (nested tables are now handled properly; HTML tags can be present in an "empty" cell; links deleted from the table should be fully vanished, i.e., no wantedpages entries)
  • Algorithms for writing #save data to database revamped to prevent deadlocks and generally optimize performance on a typical #save call (in which there is likely to be no or few changes to the #save data).