HFS: Template macros

From rejetto wiki
Jump to navigation Jump to search

Please note: Macros are not available in current version 2.2, but they will in 2.3, and you can experiment with them using beta versions.

How macros work

Differences with symbols

Macros are similar to symbols. They are something you type in the template and is substituted (we say expands to) with meaning content when the user watch the page.

While symbols are just a name, macros have a name and optionally parameters.

You can tell symbols by macros because symbols are surrounded by %percent% signs, macros are surrounded by {.dotted braces.}.

More on them

How the macro will work depends on the parameters. The same macro has a different effect when the parameter change.

The macro section will copy the content of a section of the template. But which section is specified by the parameters. So, you have one macro, section, but it will expand to any content as you change the name of the section (as parameter). Let's say {.section|style.} and it will copy the content of section [style].

Parameters

After the name of the macro you can put a character "|" (its name is pipe) and then a parameter: {.name|parameter.}. If the macro requires more parameters, like 2 or 3, you just add more pipes: {.name | parameter | another parameter | don't look at me.}.

Readability

Having macro in macro, nested, can be a mess to read. To increase readability you can

  • split the syntax over several lines, and indent.
  • add a final /macroname to know that you are closing just that macro, like {.load | something /load.}. The final /load is ignored by hfs, it's just for your convenience.

Quoting

Sometimes you don't want HFS to consider a text as part of the macros syntax. There's a way to tell HFS to leave untouched the text, and it is called quoting.

To quote a text you type {: then the text you want. To close the quoting just type :} .

This capability is very useful with the macro set. You'll see later.

Full list

With letters we are denoting parameters, and their meaning is explained immediately after.

Logic and flow

if | A | B | C
if A is true, then it expands to B, else it expands to C. When it expands to B, C is just discarded, and the vice versa. C is optional.
How you tell A is true? A is true if it is not empty and not just a zero. So if A is "hi there!", then it is true.
Remember to use quoting if you use macros in the body.
if not | A | B | C
same as if, but the logic is inverted. When A is true it expands to C and vice versa.
not | A
true if A is not true
and | A | B
true if both A and B are true. In details: it works as the pythonic and, that is, in the true case it returns B (or the last parameter, in case you specify more than two parameters).
or | A | B
true if any of A or B are true. In details: it works as the pythonic or, that is, it returns the first true parameter (yes, it supports any number of parameters). If none of the parameters is true, then it return false.
xor | A | B
true if A is true while B is false, or the opposite
= | A | B
true if A is the same as B. It's case insensitive. You can use the opposite macro, true if they are different, and it's name is != or <> (use the one you like most, there's no difference).
> | A | B
true if A is greater than B. Of course you can also use the related macro <.
>= | A | B
true if A is equal or greater than B. Of course you can also use the related macro <=.
switch | A | B | C | D | E | F ...
this is rather complex for non-programmers. You can learn how a switch statement is supposed to work from Wikipedia. When you know how a switch works, then you will understand this schema that will make you understand how the parameters of this macro work
switch(A) {
  case C: return D;
  case E: return F;
  ...
}
The B is a separator string for C. Let's be clearer: if you want to specify several cases in C, all of them returning D, then you can specify them separating with B.
As you can see C and D are paired, as E and F, eventually G and H, and so on. If at the end of this list of pairs, a single unpaired parameter is found, then it is used as the else/default case, when all other pairs have failed.
Let me translated the first example of Wikipedia using the macro
{.switch|%n%|,|
  0|you typed zero|
  3,5,7|%n% is a prime number|
  4,6,8|%n% is an even number|
  2|%n% is a prime and even number|
  1,9|%n% is a perfect square|
  Only single-digit numbers are allowed
.}
for | A | B | C | D | E
repeats D by replacing variable {.^A.} with every value between B and C (they are meant to be numbers).
Optionally you can specify a step for the numbers: that is, instead of going 1-by-1, they can go 2-by-2. The optional step is specified at position D; in such case the body is moved from position D to position E.
Remember to use quoting for the body.
for each | A | B | C ... | Z
a (sometimes) handier version of for, that will repeat Z but replacing variable {.^A.} with value B the first time, value C the second time, D etc etc. Remember to quote the body.
example: {.for each|i|10|100|1000|{:now it's {.^i.} :}.}
after the list | A
quoted text A is dequoted and executed, but only if the file listing is completed.
dequote | A
if A is {:quoted:} then quoting markers are removed (unleashing macro execution of the previously quoted text).

Data getting

section | A
A is the name of the [section] you want to include. Note: till build #160 it was case sensitive.
section | A | B
A is the name of the [section] you want to include from the external file B
get | A
where A is the name of a value you are interested in, like to know if an option is enabled or not.
This is a list of supported values for A:
can upload
true if in the current folder the user is allowed to upload files
can recur
true if the option Recursive listing is enabled
can archive
true if the current item in list is archivable. outside the list it refers to the current folder.
can access
true if the current item in list is accessible by the user
is new
true if the current item in list is flagged as "new"
urlvar | A
url variable. Let's say in the url you see ?sort=s and you want to know that s, just use the macro urlvar|sort.
postvar | A
just as urlvar but reads the value from the POST part of the request instead of the URL.
header | A
get any value from the HTTP request header. A is the name of the header field. This is something mostly for techies. Don't fret if you don't even know what the header is.
time | A
returns current time information. The format is specified by optional parameter A. Refer to this page for format syntax. If A is not supplied, value "c" will be used.

Data manipulation

substring | A | B | C
cut C from A to B, with B not included. Example: {.substring|(|belong|you (are) belong to us.} will be (are).
If A is not specified, C is taken from the start. If B is not specified, C is taken to the end.
cut | A | B | C
cut C from position A, for B characters. The difference with macro substring is that here you specify where to cut by numeric positions. Example: with A=2 and B=3 and C=abcdef you will get bcd because it is from character 2 and for a length of 3.
If you don't specify A, it defaults to 1. If you don't specify B, it defaults to the length of C.
If A is negative, then you are counting backward from the end of C. If B is negative, then you are considering the length of C minus the amount of B.
repeat | A | B
repeat B for A times. Example: for A=5 and B=+ the result is +++++
upper | A
returns A with all letters uppercased. Example: for A=Hello the result is HELLO
lower | A
returns A with all letters lowercased. Example: for A=Hello the result is hello
trim | A
returns A with any leading and trailing space removed
replace | A | B | C
returns C with every A replaced with B
calc | A
return the result of the mathematical expression A. Example: for 1+(5/2) the result is 3.5
supported operators are + - * / % (as modulus)
round | A | B
returns rounded value of A, where A is a non-integer number, and B is the number of digits after the decimal point. B is zero by default if not specified.
add | A | B
returns A+B where A and B are numbers
sub | A | B
returns A-B where A and B are numbers
mul | A | B
returns A*B where A and B are numbers
div | A | B
returns A/B where A and B are numbers
mod | A | B
returns A modulus B where A and B are numbers
min | A | B ...
returns the minimum over all parameters. You can have more than 2 parameters.
max | A | B ...
returns the maximum over all parameters. You can have more than 2 parameters.
count substring | A | B
tells the number of times A is present in B (case sensitive)
encodeuri | A
all characters are encoded in the %XX form used for URLs, expect alphanumerics and #/,&?:$@=+
decodeuri | A
all encoded characters in the form %XX are decoded.
convert | A | B | C
converts C from charset A to charset B. At the moment only supported charsets are ansi and utf-8.
filename | A
returns only the filename part of A, removing eventual path information.
force ansi | A
if template is using UTF-8 charset, then A is converted to ANSI. Otherwise A is left unchanged.

Variables and functions

set | A | B
what you actually do is to bind B to the name A. You can later recall B by passing A to the macro call.
If you are used to programming, you may know what variables and functions are. In such case, consider that set|x|1 is similar to a variable x with value 1. By binding to x something more complex, that uses parameters, you are creating a function. The first parameter is $1, the second $2, and so on, that you can specify inside the body B.
Remember to use quoting if you use macros in the body (B).
set append | A | B
just like set, but B is appended to current A value instead of overwriting.
call | A | B | C | ...
this macro (shortcut by ^) expands to the value bound to A. Such value can be a number or a text, doesn't matter. If the value contains symbols $1 and $2, then they are replaced with the parameters B and C (and so on).

You can find some examples for set and call on the forum.

inc | A | B
if A is a name which have been bound to a numeric value, then it is rebound to a new value, that is the old one incremented by the number B
dec | A | B
just as the inc above, but the value is decremented
count | A
each call returns and increments A, so the sequence will be 0 1 2 3...
variable A is not actually a variable, you can't access it throught {.^A.} and its value can be retrieved only through another call to count.
from table | A | B
A is the table and B is the key. The table is a variable with this form
key1=value1
key2=value2
key3=value3
Let's say such text is the content of variable A, then {.from table|A|key2.} will return value2.

Localization

Here goes a macro useful for template localization.

Inside the template you can use identifiers (IDs) instead of real text. So you can gather all the text in one place, and ease the work of whom wants to translate to another language.

Lets say that you have a link <a href ='..'>Go up one level</a>. You can replace with <a href ='..'>{.!up.}</a>, moving the text to section [special:strings].

[special:strings]
up=Go up one level

You can optionally provide a parameter (like {.!up|Go to parent folder.}) that will be the default text in case up is not found in [special:strings].


If no parameter is provided, then the ID itself will be used as default text.

File manipulation

load | A
you'll love this one. You can specify A as a file name, or as a URL. It will load and expand to it. The file or URL you specify must be accessible from the server machine. A can be C:\windows\win.ini or also http://another_hfs_in_lan/the_file_i_want.txt.
filesize | A
tells you the size in bytes of the file A. At the moment URL are not supported, only local filenames.
save | A | B
stores B to file path A. The path can be absolute, URI based or relative to HFS folder.
delete | A
moves the file A to the recycle bin.
rename | A | B
renames the file A to B.
md5 file | A
computes and expands to the MD5 hash for file A.

Others

comment | A
this is just discarded. It's for your convenience, to put a comment in the template. HTML comments are readable by visitors if they use show html source in their browser. Using this macro you will have comments that will never leave your server.
length | A
if A is 5 characters length, it expands to 5.
match | A | B
it is true if the content B is matched by the mask A.
match address | A | B
just like the macro match, but the mask supports ip ranges like 10.0.0.1-10.0.0.25
random | A | B | C | ...
it expands to any of the parameters, randomly
random number | A | B
if only A is specified then a number between 0 and A is generated. If also B is specified then the number will be between A and B (included).
is substring | A | B
it is true if A is contained in B (case insensitive)
append | A | B
add B at the end of file A. This may is also useful to keep special logs.
breadcrumbs | A
builds a breadcrumbs based on the current folder path. The A is a little html template that is repeated for every piece of the path, for which 2 symbols are provided: %bread-url% and %bread-name%. The simpler example for A is: <a href ="%bread-url%">%bread-name%/</a>.
Remember to use quoting if you use macros in the parameter.
dialog | A | B | C
display a server-side message in a dialog box. A is the message itself, and only required parameter. B are options separated by spaces, and C a title for the dialog.
Possible options are
okcancel
offers buttons OK and CANCEL
yesno
offers buttons YES and NO
yesnocancel
offers buttons YES, NO and CANCEL
error
this is an error reporting dialog
question
this is a questioning dialog
warning
this is a warning dialog
information
this is an informational dialog
mime | A
the mime-type describing the content sent to the browser. For techies, it's the content of the header field "Content-type".
any macro marker | A
it is true if any macro marker is present in A. Macro markers are {.delimiters.} , {:quotings:} and the parameters separator (the pipe, "|").
play system event | A
specify the name of the event bound to a sound that you want to play. SystemStart and MailBeep are just examples, for a full list you can open your registry at HKEY_CURRENT_USER\AppEvents\EventLabels .
add folder | A | B | C
adds a folder to the virtual file system. A specifies real or virtual, while B is the name of the folder.
For real folders B should be the path on disk, but if published name is different than the real one, you can specify the path in parameter C, and the name in B.
set item | A | B
modifies the item A in the virtual file system as specified by B. Supported values for B are
hide
as clicking hide in the context menu.
hide tree
as clicking hide tree in the context menu.
no log
as clicking don't log in the context menu.
not as download
as clicking don't consider as a download in the context menu. For folders, it sets the mask to *, but you can specify a different one as an additional parameter.
exec | A | B
ask system to run file A, eventually with parameters B. If you want to use the pipe inside of B, just use macro quoting.
chdir | A
changes the current working directory of the HFS process.

Shortcuts

$A
is a shortcut for section | A
?A
is a shortcut for urlvar | A
^A
is a shortcut for call | A
A = B
is a shortcut for =|A|B
?name = X
is a shortcut for {.?name.} = X .
It works also for <> != < <= > >= .

Execution order

The current execution order of macros is: from inner to outer, from left to right.

An example: {.A {.B.} {.C.}.} {.D.}. In this scenario, the order is B, C, A, D. Because B and C are inner, but on the same level, so B is expanded before C.

Problem

All macros, except quoted ones, are expanded. Even if you use the macro if|A|B|C, independently by the value of A, both B and C are expanded. If A is true, C is discarded, and vice versa. This is an important point, because if you was using append inside C, the file will be written anyway.

Workaround to the problem

Since having C not executed when A is true (from the previous example) is a very worth having feature, there's a workaround. By quoting the bodies of if, you avoid them to be executed. After the if choice between B (then) and C (else), it removes the surrounding quoting markers, if any. Let's see with an example

{.if| %user% | {.append|file.txt|someone is in!.} /if.}

As we stated before, this won't do what it seems to do. Append will always be executed, because every macro is executed. The only way to stop this is to surround with quoting markers

{.if| %user% | {: {.append|file.txt|someone is in!.} :} /if.}

By having quoted the append, it will only be executed if %user% is not void. This is because the if removes the quoting markers.

This is a special behaviour by only few macros, others will just leave the quoting because they are supposed to stay. The special macros with such behaviour are: if, set, for, switch and breadcrumbs.