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.
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!
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).
There are two styles of supplying attributes to a tag: named and positional.
In the named style, you supply attributes using
pairs, just as with HTML:
key
=value
[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).
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 [
and tagname
...
]<!--[
are equivalent.
tagname
...
]-->
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:
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.
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 | |
---|---|
As you can implicitly see from the above example, attribute values
are normally interpolated (in other words,
However, when the attributes contain a dot (such as
|
Interchange parse content like pages, components and profiles in a few consecutive passes instead of once from the top to the bottom:
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.
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]
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 | |
---|---|
It is only possible to use the backticks notation with named parameters! |
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
,
while the second line would log pretty useless "[value name] was here".
CATROOT
/etc/log
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.
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.
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.
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 tag | Default prefix | Example 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 | |
---|---|
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' }, ]