Template Tags as a Programming Language

From MovableType

Contents

Description

MT Template markup language aka MTML has not really been a markup language. It is a domain specific language or DSL, implmented on top of both Perl and PHP. Markup languages does not have computational amount while MTML does (for example, MTAuthorName might return result in O(1) while MTCommentCount might not without memcached). Now is the time to let the world know that MT has a powerful scripting feature embedded in its core, by providing a simple set of generic construct and computational operators.

Functional Specification

MT tags fall into the category

Loop - hash support added in Boomer
sequential loop of an array or a hash
For - new in Boomer
incremental loop
If - ElseIf - Else - IF and ELSE existing since 4.0, ELSEIF, math and hash/array support new in Boomer
conditionally executed block
Array and Hash support in GetVar and SetVar - new in Boomer
Create an array/hash variable or set a value to existing array/hash variable
SetHashVar - new in Boomer
Create a hash variable in a block.
op attribute - new in Boomer
provide simple math operation (add, sub, mul, div, mod) to SetVar, and GetVar

Loop

Loop has been useful to iterate through an array. This release introduces hash support to the tag. By simply specifying the name of the hash variable in the "name" attribute, you can iterate through all of the keys and associated values in the loop. Each iteration sets special "__key__" and "__value__" variables which has the key and the value of the iteration. If the variable is of array type, "__value__" variable is set.

Attributes

sort_by
'key' specifies sorting by hash key (for array, it's index and meaningless). 'value' specifies sorting by hash or array value.
Both 'key' and 'value' can have sub modifier 'reverse', which specifies the reverse order. 'value' can have another sub modifier 'numeric' which compares values in numerical view during sorting.

Example

foreach my $__key__ ( keys %MTVersions ) {
  print "$__key__ - $__value__\n";
}
<MTLoop name="MTVersions">
  <MTVar name="__key__"> - <MTVar name="__value__">
</MTLoop>
join(',', @offices);
<MTLoop name="Offices" glue=","><MTVar name="__value__"></MTLoop>
<MTLoop name="Offices" glue="," sort_by="value numeric reverse"><MTVar name="__value__"></MTLoop>

For

Byrne has already documented the feature. See here.

If-ELSEIF-Else

MTIf tag was introduced in 4.0 and documented in [here|http://www.movabletype.org/documentation/appendices/tags/if.html].

What's new in Boomer

"ElseIf" functionallity
MTElse can now have "name" and other attributes specified in its corresponding MTIf tag, to realize "elsif" or "else if" function in the MT template.
New Attributes
op, index and key support simple math and hash/array support.

New Attributes

  • index (integer)

specifies the index of the item of the array, which will have the specified value. No default value.

If the value is not numeric, it is an error.

  • key (string)

specifies the name of the key of the hash which will have the specified value. No default value.

Note: "function" attribute is not supported as MTGetVar does.

  • test (expression string)

consult Brad to know how to specify expression.

Syntax

index and key attributes can be specified in two ways. You can specify them like other attributes, but you can also specify them in more like a program. See the example below.

Example

 <MTVar name="offices" index="0" value="San Francisco">
 <MTVar name="offices" index="1" value="Paris">
 <MTVar name="offices" index="2" value="Tokyo">
 <MTIf name="office" index="0" eq="Los Angeles">
   Lakers
 <MTElse name="office" index="0" eq="San Francisco">
   Warriors
 <MTElse>
   Where did Sonics go?
 </MTIf>
 <MTVar name="MTVersions" key="3.2" value="Spam Fighter">
 <MTVar name="MTVersions{3.3}" value="Tinsel">
 <MTVar name="MTVersions{3.5}" value="Wheeljack">
 <MTIf name="MTVersions{3.5}">
   Version 3.5 has not been released.
 <MTIf>
 <MTIf name="MTVersions{3.3}" eq="Tinsel">
   Tinsel has been developed with a sister product, Tribble.
 </MTIf>
 <MTVar name="num" value="1">
 # if ( 2 == num + 1 )
 <MTIf name="num" op="add" value="1" eq="2">This is true.</MTIf>
 # if ( 2 == num * 10 )
 <MTIf name="num" op="*" value="10" eq="2">???</MTElse>This is false.</MTIf>

SetHashVar

Template:Bug

Features added to SetVar

SetVar can now create new array or hash variable, add new item to the existing array, add new key - valur pair to the existing hash, and replace the value for the exiting key of the existing hash or the existing array item.

New Attributes

  • index (integer)

specifies the index of the item of the array, which will have the specified value. No default value.

If the array variable does not exist, this will create a new array variable with the name specified. Specifying index other than 0 when creating a new array will create a sparse array.

If the value is not numeric, it is an error.

  • key (string)

specifies the name of the key of the hash which will have the specified value. No default value.

If the hash variable does not exist, this will create a new hash variable with the name specified and the key - value pair.

  • function (string)

specifies the function when to getting value from the array variable. No default value.

push
push, or set at the top (or at the last) the specified value to the array variable. Does nothing if the variable is not array type. Creates a new array variable and stores the value as the first item if the variable does not exist.
unshift
the same behavior as push does, except it unshifts, or set at the bottom (or at the first) the value to the array.
undef
clear array or hash variable.
delete
If the variable is of hash type and "key" is also specified, delete the key-value pair.

Syntax

index, key and function attributes can be specified in two ways. You can specify them like other attributes, but you can also specify them in more like a program. See the example below.

NOTE

Calls to SetVar without these attributes behaves the same as in MT 4.0. Therefore, if you call SetVar without these attributes to the existing value whose type is either array or hash, it overwrites the variable to have scalar value.

Calls to SetVar with these attributes for the existing variable of scalar type, the tag works as if the attribute were not specified.

Example

my @offices = ( 'Tokyo', 'San Francisco', 'Paris' );
<MTSetVar name="offices" value="San Francisco" index="0">
<MTSetVar name="offices" value="Tokyo" function="unshift">
<MTSetVarBlock name="offices" index="2">Paris</MTSetArrayBarBlock>
-- or --
<MTSetVar name="offices[0]" value="San Francisco">
<MTSetVar name="unshift(offices)" value="Tokyo">
<MTSetVarBlock name="offices[2]">Paris</MTSetArrayBarBlock>
my %MTVersions = (
   '4.0' => 'Athena'
   '4.01' => 'Enterprise Solution'
   '4.1' => 'Boomer' # whitespaces omitted for clarity
   '4.2' => 'Cal'
);
<MTSetVar name="MTVersions" key="4.0" value="Athena">
<MTSetVarBlock name="MTVersions" key="4.01">Enterprise Solution</MTSetHashVarBlock>
<MTSetVarBlock name="MTVersions" key="4.1">
   Boomer
   <MTSetVar name="4.2" value="Cal">
</MTSetHashVarBlock>
-- or --
<MTSetVar name="MTVersions{4.0}" value="Athena">
<MTSetVarBlock name="MTVersions{4.01}">Enterprise Solution</MTSetHashVarBlock>
<MTSetVarBlock name="MTVersions{4.1}">
   Boomer
   <MTSetVar name="4.2" value="Cal">
</MTSetHashVarBlock>
<MTVar name="object1" key="name" value="foo">
<MTVar name="object1" key="price" value="1.00">
<MTVar name="object2" key="name" value="bar">
<MTVar name="object2" key="price" value="1.13">
<MTSetVar name="array1" function="push" value="$object1">
<MTSetVar name="array1" function="push" value="$object2">
<MTLoop name="array1">
   <MTVar name="name">(<MTVar name="price">)<br />
</MTLoop>
  ... should return
  foo(1.00)<br />bar(1.13)

Features added to GetVar

GetVar can now return value of an item from either an array or a hash variable.

Attributes

  • index (integer)

specifies the index of the item of the array, which will have the specified value. No default value.

Returns empty string if the array variable does not exist or the specicfied index does not exist.

If the value is not numeric, it is an error.

  • key (string)

specifies the name of the key of the hash which will have the specified value. No default value.

Returns empty string if the hash variable does not exist or the specified key does not exist in the hash.

  • function (string)

specifies the function when to adding value to the array or the hash variable. No default value.

pop
pop, or get from the top (or the last) the specified value from the array variable. Does nothing if the variable is not array type. The item will be removed from the array variable.
shift
the same behavior as pop does, except it shifts, or get from the bottom (or the first) the value from the array.
count
returns the number of items in the array variable, or number of keys in the hash variable.

If the existing variable is of hash type, pop and shift are ignored.

Syntax

index, key and function attributes can be specified in two ways. You can specify them like other attributes, but you can also specify them in more like a program. See the example below.

Example

<MTGetVar name="offices" function="count"> -- returns 3
<MTGetVar name="offices" index="1"> -- returns San Francisco
<MTGetVar name="offices" function="shift"> -- returns Tokyo
<MTGetVar name="offices" function="count"> -- returns 2 (because Tokyo was removed)
<MTGetVar name="offices" index="1"> -- returns Paris (because Tokyo was removed)
-- or --
<MTGetVar name="count(offices)"> -- returns 3
<MTGetVar name="offices[1]"> -- returns San Francisco
<MTGetVar name="shift(offices)"> -- returns Tokyo
<MTGetVar name="count(offices)"> -- returns 2 (because Tokyo was removed)
<MTGetVar name="offices[1]"> -- returns Paris (because Tokyo was removed)
<MTSetVarBlock name="count"><MTGetVar name="offices" function="count" op="sub" value="1"></MTSetVarBlock>
<MTFor from="0" to="$count" step="1">
  <MTGetVar name="offices" index="$__index__">
</MTFor>
 -- Please also see example of Loop for easier iteration of an array

NOTE

Each iteration sets __key__ and __value__ variables which have the key and the value respectively for each iteration. If the specified variable is an array, __key__ has the index.

op

"op" is a new optional attribute added both to SetVar and GetVar. "op" is to perform simple mathematical calculation upon setting or receiving a numeric value to and from existing variable in context.

Behavior change of value attribute

Before this release, MTVar with "value" attribute redirects to MTSetVar. So for example, while <MTVar name="foo"> is equivalent to <MTGetVar name="foo">, <MTVar name="bar" value="quux"> is equivalent to <MTSetVar name="bar" value="quux">.

With this release introduces "op" attribute, MTVar with value attribute will not redirect to MTSetVar if "op" attribute is also specified. So for example, <MTVar name="foo" value="1" op="+"> is equivalent to <MTGetVar name="foo" value="1" op="+">. The value of the "value" attribute will not be stored in the variable named "foo".

Operations

add or +
Addition
sub or -
Subtraction
mul or *
multiplication
div or /
Division
mod or %
Modulus
inc or ++
Increment
dec or --
Decrement

NOTE

If a variable specified in name attribute does not exist, "op" of SetVar does nothing. SetVar would just have the value. On the other hand, if the variable exists, specifying "op" attribute change the behavior of SetVar because it does not overwrite the value but instead stores the calculated result.

If the value in the existing variable is not numeric or the value specified is not numeric, "op" is simply ignored.

Example

$num = 99;
<MTSetVar name="num" op="add" value="99"> -- num now has 99
$num = 1;
<MTSetVar name="num" value="1"> -- num now has 1 (overwritten)
$num += 2;
<MTSetVar name="num" value="2" op="add"> -- num now has 3
$num *= 3;
<MTSetVar name="num" value="3" op="mul"> -- num now has 9
$num /= 2;
<MTSetVar name="num" op="div" value="2"> -- num now has 4
$num %= 6;
<MTSetVar name="num" op="mod" value="6"> -- num now has 2
$num = 1;
<MTSetVar name="num" value="1"> -- num now has 1
print $num + 2;
<MTGetVar name="num" op="add" value="2"> -- returns 3 but num still has 1
print $num * 3;
<MTGetVar name="num" op="mul" value="3"> -- returns 3 but num still has 1
print $num - 4;
<MTGetVar name="num" op="sub" value="4"> -- returns -3 but num still has 1
print $num / 5;
<MTGetVar name="num" op="div" value="5"> -- returns 0 but num still has 1
print $num % 6;
<MTGetVar name="num" op="mod" value="6"> -- returns 1 but num still has 1