ITL

Interchange functions are accessed via the Interchange Tag Language (ITL). The pages in a catalog may be mostly HTML, but they will use ITL tags to provide dynamic content and access Interchange functions in general. ITL is a superset of MML, or Minivend Markup Language.

ITL tags perform all kinds of operations. There's more than 200 standard predefined tags, and the UserTag facility allows you to create your own custom tags, indistinguishable from the ones "built-in". To get started with custom tags, see usertag glossary entry.

Basics

ITL tags are similar to HTML, in that they accept attributes and that there are both standalone (non-container) and container tags.

A standalone tag has no ending element, such as [value]:

[value name]

The above example will insert user's name.

A container tag has both beginning and ending elements, such as tag if:

[if value name]
  You have provided us with your name. It is [value name].
[/if]

In the above example, you see that container tags are in general useful only when content is provided in their body (the place between the opening and corresponding ending tag).

There must be no whitespace between the left bracket ([) and the tag name; [forum] is valid, [ forum] is not!

Standard Syntax

We've covered the most basic syntax above. If you need to pass attributes to the tag, you do that in the opening section. If the tag is a container, then body text can additionally be specified between the opening and closing marker:

[tagname parameters_or_attributes]
  Body Text
[/tagname]

Note that some Interchange macros (drop-in text replacements) may look like tags or end tags. For example, [/page] used to be a macro that expanded to </a>, but [page] itself was not a container tag and [/page] wasn't its closing element. Likewise, some tags perform special subtags processing on their own, and the subtags are not considered real tags — they only exist in the context of the surrounding tag and usually support only limited options. One such example is tag [row] with its subtag .

It is important to mention that if a tag or argument name includes an underscore or dash (such as in [item-list]), then you can always choose between the dash and underscore! The two forms are interchangeable, except that you must be consistent with tag markers; an ending tag must match the opening tag. Both [item-list]...[/item-list] or [item_list]...[/item_list] are fine, but [item-list]...[/item_list] is not.

This is a very convenient feature. It is a common standard to use dashes (-) in ITL, and underscores (_) in Perl code (because in Perl, dashes would be interpreted as minus operators, which is not what you'd expect).

Named and Positional Parameters

There are two styles of supplying attributes to a tag: named and positional.

In the named style, you supply attributes using key=value pairs, just as with HTML:

[value name=variable_name]

The positional-style tag that accomplishes the same thing, but is more compact:

[value variable_name]

Positional syntax, however, requires support by each tag individually. That is, set of arguments that can be specified in positional notation (as well as their position in the set) are fixed and determined in advance. You can see a list of accepted positional arguments (and their implicit order) in each tag's SYNOPSIS reference section; try [value] for example.

You cannot mix named and positional attributes in the same tag; use either all named, or all positional.

Positional syntax is appropriate for simpler tags and Interchange interprets positional arguments slightly faster, but don't let premature optimization kick in — as Edsger W. Dykstra once said, "Premature optimization is the root of all programming evil".

In most cases, positional attributes (called parameters) work the same as named attributes. However, there are a few situations where you must use named attributes:

  • When you want to specify a parameter that, positionally, comes after a parameter that you want to omit, e.g. omit the first parameter but specify the second. The parser would have no way of knowing which is which, so you must revert to named syntax. This rarely happens, though, because the first positional parameter is usually required for a given tag anyway, and can't be omitted.

  • When there is some ambiguity as to which parameter is which, usually due to whitespace. If the argument value contains any whitespace (even if it is enclosed in quotes!), you must use named syntax.

  • When you need to interpolate the value of an attribute, such as of default= in [value name=time default="[time]"].

  • When you are using looping subtags (Loop tags and subtags are explained below).

HTML Comments

ITL also supports a special notation where you use HTML comment markers with ITL inside, and where the comments are removed by Interchange so that the tag results show up in the generated HTML page.

That means [tagname ...] and <!--[tagname ...]--> are equivalent.

This allows you to make your raw ITL tags appear as comments in HTML browsers, and especially in HTML editors which might not like non-HTML markup at random positions. You might want to use this if your editor has trouble with ITL tags when editing Interchange page templates, or alternatively, if you want to use one .html file both as an Interchange page and as a static page delivered directly by your web server where no Interchange processing is performed.

When you really wish to treat HTML comments as plain comments and not remove them, then you must include a whitespace between the HTML comment delimiters and the ITL square brackets:

<!--[value name]-->       becomes  Bill
<!-- [value name] -->     becomes  <!-- Bill -->

ITL is interpolated in both cases however. To prevent ITL interpolation within HTML comments, you either need to see the no_html_comment_embed pragma, or enclose the block in a special ITL [comment] tag instead of in HTML comment:

[comment] [value name] [/comment]

Besides not being interpolated at all, [comment] blocks do not appear in the final HTML sent to the client either, so you can be completely safe regarding any unintentional code or information leakage.

While <!--[ and [ are completely interchangeable, the Interchange parser does not replace ]--> with ] unless it sees <!--[ at least once somewhere on the page. This is a small parsing speed optimization and shouldn't cause you any trouble as you're supposed to be consistent in your syntax anyway.

Argument Interpolation

When ITL tags are expanded, for example when [value name] yields the actual user's name, we say the tags are interpolated.

If you want to use one tag's output as another tag's input, you must use named syntax and you must double-quote the argument. For example, this WILL NOT work:

[page scan se=[scratch variable_name]]

The above code is in really bad shape. To make it work, we need to do two things: switch to named syntax, and properly quote arguments containing whitespace:

[page href=scan arg="se=[scratch variable_name]"]

Note that the href argument did not need to be quoted; arg did, because it contained a whitespace and it contained a tag intended for interpolation ([scratch variable_name]).

[Note]Note

As you can implicitly see from the above example, attribute values are normally interpolated (in other words, se=[scratch variable_name] was expanded to se=VARIABLE_VALUE before being passed as value to arg=.

However, when the attributes contain a dot (such as name.first= or name.last=), their values are not interpolated by default. Their interpolation is controlled by the interpolate_itl_references pragma, and more about this whole concept can be learned below in the section called “Attribute Arrays and Hashes”.

Parsing Order

Interchange parse content like pages, components and profiles in a few consecutive passes instead of once from the top to the bottom:

  1. pragmas are set from [pragma] in the content.

  2. Variables are replaced (__NAME__, @@NAME@@, @_TITLE_@) with their respective values.

  3. Comments enclosed by [comment] are removed.

  4. Tags are expanded.

Deeper Look at Argument Quoting

The question is — exactly when can you omit the quotes around argument values? First answer is trivial; never omit the quotes and you'll never run into trouble.

The other answer says, "omitting quotes earns you a bonus for style and achieves small parser speed improvement". However, if the value contains a whitespace, you must quote it. To quote values, you can use double quotes ("), single quotes (') and pipes (|) interchangeably. Pipes have the additional functionality of removing leading and trailing whitespace, but generally you can use all types in combination to do three levels of quoting with ITL; for more deeply nested constructs, use direct Perl code.

Additionally, container tags can be closed using "XML-style" syntax. The following two lines are identical in effect:

  [forum top="[item-code]" display_page=forum_display  ][/forum]
  [forum top="[item-code]" display_page=forum_display /]

Note, however, that you can use "/]" only with tags for which you provide no attributes, or the attributes are named and quoted. Positional attributes (parameters) "absorb" everything (including the "/") up to the closing bracket, and therefore using "/]" is not possible.

When using "/]" notation, it is still possible to provide body for the tag by specifying body= attribute.

The above covers a good deal of quoting, but it's still not all there is to it. Loop subtags, which we explain some lines below, are an exception to the rule and do not need quotes (even though they sometimes contain whitespace), because they are parsed earlier in a separate pass, before the general parser takes on.

Here's an example with loop subtags that would work properly even with quotes omitted. (Pay attention to [item-field url] which, at first sight, looks like it is invalid because it contains a space and is not quoted nor named.)

[item-list]
[page [item-field url]]detailed info[/page] on [item-description]
[/item-list]

You might wonder why unquoted tags are allowed. The answer is performance. If you have large lists of tags you can achieve significant speed-ups by using positional attributes. Parsing and disassembling named attributes takes some more CPU cycles.

Accessing Perl from ITL

Perl-related ITL tags

Among ITL tags, [perl], [calc], [calcn] and [mvasp] are used obtain direct access to the Perl language in Interchange pages. In fact, all the ITL tags are only extensions (and convenience features) built on top of Interchange's Perl core, so all ITL tags boil down to Perl code anyway. Using Perl directly could have the benefits of:

  • Increasing page parsing speed (Perl blocks are evaluated directly)

  • Making complicated ITL constructs much easier, or even trivial

  • Enabling direct access to Interchange data structures (and code, and everything)

  • Unifying coding style

Let's say we wanted to display user's real name in a page. Here are three pieces of code, all achieving this effect, but using different approaches to produce the result:

Displaying user's real name using an ITL layer:

[if value name]
  Your name is [value name], in case you forgot.

[else]
  I don't know your name.

[/else]
[/if]

Displaying user's real name by calling ITL tags from Perl:

[calc]
  my $out;
  my $name = $Tag->value('name');

  if ($name) {
    $out = "Your name is $name, in case you forgot.";

  } else {
    $out = "I don't know your name.";

  }

  return $out;                                              
[/calc]                 

Displaying user's real name by accessing Interchange's data structures:

[calc]
  my $out;
  my $name = $Values->{name};

  if ($name) {
    $out = "Your name is $name, in case you forgot.";

  } else {
    $out = "I don't know your name.";

  }

  return $out;                                              
[/calc]                 

Perl-related ITL syntax

ITL also supports a special type of quoting, using backticks (`), where the content is evaluated by Perl and then passed as the usual argument value.

Using this backtick notation, you can conveniently perform quick, in-place Perl evaluation of an argument, or pass complex data structures as attribute values.

Performing quick, in-place argument interpolation:

[cgi name=Magic_Number hide=1 set=`2 + 3 + $CGI->{magic}`]

The magic number is: [cgi Magic_Number]

Passing complex data structures as argument values:

[cgi name=Magic_Structure hide=1 set=`{ key1 => 'val1', key2 => 'val2' }`]

As you see, this notation (and implied functionality) is completely valid and possible. It is then just up to the called tag to handle attribute value appropriately.

[Note]Note

It is only possible to use the backticks notation with named parameters!

Universal Attributes

Universal attributes apply to all tags, although tags might specify their own defaults for the attribute. The code implementing universal attributes is independent of the routines that implement specific tags.

We will explain interpolate and reparse attributes here. It is important to remember that the behavior of the interpolate attribute (unfortunately) differs, based on whether a tag is stand-alone or a container. In addition, the reparse attribute is only used with container tags.

With standalone tags, the interpolate attribute specifies whether the output of the tag will be interpolated.
Output of most of the stand-alone tags is not interpolated by default. Exceptions to this rule would include, for example, the [include] tag.

With container tags, the interpolate attribute specifies whether the tag body will be interpolated before being passed to the tag.
The reparse attribute specifies whether output will be interpolated.

For an interpolation example with container tags, let's assume the user's name is Kilroy:

[log interpolate=1][value name] was here[/log]

[log interpolate=0][value name] was here[/log]

The first line would log "Kilroy was here" to CATROOT/etc/log, while the second line would log pretty useless "[value name] was here".

For an interpolation example with stand-alone tags, consider the following:

[set name=now interpolate=0][time]%A, %B %d, %Y[/time][/set]

We've set the scratch variable new to not contain the actual date, but the ITL code to calculate and display the date each time it's called. Let's assume today is Monday, January 1, 2001, and we have the following code:

[scratch name=now interpolate=1]

[scratch name=now interpolate=0]

The first line would then produce the expected Monday, January 1, 2001, while the second would leave us with unusable [time]%A, %B %d, %Y[/time].

The example above serves to show interpolate used with a stand-alone tag. This behavior can only be achieved by using reparse attribute if you're having a container instead of the stand-alone tag.



The reparse attribute is only used with container tags, and specifies whether the tag output will be interpolated. This is basically the same as the interpolate attribute for stand-alone tags, but a new name had to be invented because interpolate already performed a different function with container tags, when this functionality was to be added.

Most container tags will have their output re-parsed for more Interchange tags by default. If you wish to prevent this behavior, you must explicitly set the reparse attribute to a false value. Note, however, that you will almost always want the default action. Probably the only ITL tag that doesn't reparse by default is [mvasp].

Here's an example. Again, assuming the user's name is Kilroy,

[perl reparse=1]
   my $tagname = 'value';
   return "[$tagname name] was here\n"
[/perl]

[perl reparse=0]
   my $tagname = 'value';
   return "[$tagname name] was here\n"
[/perl]

expands to

Kilroy was here

[value name] was here

In Interchange 5.3.1, the no_default_reparse pragma was added, which disables the default reparsing of container tags' output. It is therefore advisable to always specify the reparse=1 attribute at places where you want reparsing to really occur.



The third attribute, send , was deprecated long ago and will only be described for historical reference.

Tag-specific Attributes

Each tag may accept additional arguments which vary from tag to tag. For each tag individually, you need to consult the appropriate reference page to learn tag-specific parameters and attributes. Note, however, that the tag arguments do not necessarily have to be announced in the tag definition header; sometimes they're used pretty much ad-hoc, but we are making sure to keep the documentation up to date. We kindly ask you to inform us of any errors or omissions in the documentation.

Attribute Arrays and Hashes

Instead of just plain values, for some tags it would be particularly convenient if they accepted arrays or hashes of argument values, much like you could do if you used Perl directly. Fortunately, ITL supports that too! For an ordinary tag, the syntax is as follows:

attribute.ARRAY_INDEX=value

attribute.HASH_KEY=value

ARRAY_INDEX is an integer array index starting from 0 (this is due to standard programming practice and Perl behavior). Note that you cannot have both an array and a hash attribute of the same name (even though that would be possible in pure Perl).

Here is an example of an array-based attribute:

search.0="se=hammer
          fi=products
          sf=description"

search.1="se=plutonium
          fi=products
          sf=comment"

It is up to each individual tag to handle an array or hash value properly. See the documentation for the specific tag before passing it an attribute array or hash value. The [page] tag, for example, would treat a search specification array (the structure we've shown above) as a joined search (one of different search types we support).

On the Perl level, before passing attributes to a tag, Interchange parser would convert attributes to an anonymous array reference.

If you were passing the above example directly to a tag named ROUTINE within a [perl] block or your custom tag, you would actually pass an anonymous array as the value of the attribute:

my $arrayref = [ "se=hammer/fi=products/sf=description",
                 "se=plutonium/fi=products/sf=description", ];

$Tag->ROUTINE( { search => $arrayref } );

Similarly, to use a hash reference in some other occasion:

my $hashref = { name   => "required",
                date   => 'default="%B %d, %Y"', };

$Tag->ROUTINE( { entry => $hashref } );

Pay attention to the fact that with array- or hash-based attributes, their values are not interpolated. It means that, at places where scalar options are interpolated (such as [tag option='[time]'], resulting in [time] expanding to the current time), reference-based attributes are not (such as [tag my.option='[time]'], which would result in literal "[time]" being passed to the attribute "my.option". Interpolation of reference-based attributes can be enabled by setting the interpolate_itl_references pragma.

Looping Tags and Sub-tags

Certain tags can only appear in a special context, usually nested within other, parent tags. Those include, for example, the ones interpreted as part of a surrounding loop tags, such as [loop], [item-list], [query] or [search-region].

Some of those subtags are PREFIX-accessories, PREFIX-alternate, PREFIX-calc, PREFIX-change, PREFIX-code, PREFIX-data, PREFIX-description, PREFIX-discount, PREFIX-discount-subtotal, PREFIX-field, PREFIX-increment, PREFIX-last, PREFIX-line, PREFIX-modifier, PREFIX-next, PREFIX-param, PREFIX-pos, PREFIX-price, PREFIX-quantity, PREFIX-subtotal, if-PREFIX-data, if-PREFIX-field, if-PREFIX-param, if-PREFIX-pos, PREFIX-modifier-name and PREFIX-quantity-name.

In each of the above, PREFIX represents an arbitrary prefix that is used in that looping tag; this is needed to be able to support nesting in arbitrary order and to arbitrary level. All of the subtags are only interpreted within their container tags (they can only appear inside their parent tags' bodies) and they only accept positional parameters. The default prefixes follow:

Parent tagDefault prefixExample sub-tag
[loop] loop [loop-code]
[loop-field price]
[loop-increment]
[item-list] item [item-code]
[item-field price]
[item-increment]
[search-region] item [item-code]
[item-field price]
[item-increment]
[query] sql [sql-code]
[sql-field price]
[sql-increment]
[tree] item [item-code]
[item-field price]
[item-increment]

Sub-tag behavior is consistent among the looping tags.

[Important]Important

As we've said already, subtags are parsed before the standard parsing routine takes on; that means before any regular tags within the loop. This has a subtle effect; it can, for example, prevent the usual tags from accepting looping tags' values as attributes. In such cases, look for "subtag" (prefixed) variants of the usual tags.

Speaking of loops and Perl, there are two types of records that Interchange can return with looping lists: ARRAY and HASH.

An array list is the normal output of the [query] and [loop] tags, or of a search operation. In those cases, fields specified in mv_return_fields (or those specified in SQL) are returned for each selected row. The two queries below are essentially identical:

[query sql="select foo, bar from products" /]

[loop search="
                ra=yes
                fi=products
                rf=foo,bar
"]

Both will return an array of arrays consisting of the foo and bar columns. The corresponding Perl data structure would look like this (familiarity with the output from Data::Dumper Perl module helps here):

[
    ['foo0', 'bar0'],
    ['foo1', 'bar1'],
    ['foo2', 'bar2'],
    ['fooN', 'barN'],
]

A hash list is the normal output of the [item-list] tag. It returns the value of all return fields in an array of hashes. A normal return might look like this:

[
    {
        code     => '99-102',
        quantity => 1,
        size     => 'XL',
        color    => 'blue',
        mv_ib    => 'products',
    },
    {
        code     => '00-341',
        quantity => 2,
        size     => undef,
        color    => undef,
        mv_ib    => 'products',
    },

]

However, you can explicitly request hash lists to be returned from queries:

[query sql="select foo, bar from products" type=hashref /]

The data structure returned would then be:

[
    { foo => 'foo0', bar => 'bar0' },
    { foo => 'foo1', bar => 'bar1' },
    { foo => 'foo2', bar => 'bar2' },
    { foo => 'fooN', bar => 'barN' },
]

DocBook! Interchange!