HFS: Template macros: Difference between revisions

From rejetto wiki
Jump to navigation Jump to search
(→‎Problem and Solution: username being "true" can be hard to understand at this stage)
 
(70 intermediate revisions by 5 users not shown)
Line 1: Line 1:
'''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.
Server-side scripting is possible through commands, also known as macros. These commands are mostly the same for templates and for hfs.events.
 
'''Please note:''' Macros are currently not available in 2.2f, but they will be in 2.3. You can experiment with them using [http://www.rejetto.com/forum/index.php/board,33.0.html beta versions].
 
Macros are very powerful and can be used to add new functionality to HFS.


== How macros work ==
== How macros work ==


=== Differences with symbols ===
=== 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.
You are supposed to already know [[HFS: Template#How_templates_work|how templates work]]. Macros are similar to symbols. They are something you type in the template that gets substituted (we say ''expands to'') with meaningful content when the user watch the page.


While symbols are just a name, macros have a name and optionally parameters.
While symbols are just a name, macros have a name and optionally parameters.  Additionally, while symbols only expand to text (such as %user% or %url%), macros can be used to perform server-side commands (such as {.save.} and {.load.} ).  


You can tell symbols by macros because symbols are surrounded by %percent% signs, macros are surrounded by <nowiki>{.dotted braces.}</nowiki>.
You can tell symbols from macros easily: symbols are surrounded by %percent% signs, while macros are surrounded by <nowiki>{.dotted braces.}</nowiki>.


=== More on them ===
=== More on them ===
How the macro will work depends on the parameters. The same macro has a different effect when the parameter change.
How the macro will work depends on the parameters. The same macro can have a variety of functions based on its parameters.


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 <tt><nowiki>{.section|style.}</nowiki></tt> and it will copy the content of section [style].
The macro ''section'' will copy the content of a [section] of the template. The parameter in the macro specifies which section. 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 <tt><nowiki>{.section|style.}</nowiki></tt> and it will copy the content of section [style].  A shortcut for this is {.$style.}.


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


=== Readability ===
=== Readability ===
Having macro in macro, nested, can be a mess to read. To increase readability you can
You can also put macros inside macros, or nest them.  However, this can often be a mess to read. To increase readability, you can:
* split the syntax over several lines, and [http://en.wikipedia.org/wiki/Indentation#Indentation_in_programming indent].
* split the syntax over several lines, and [http://en.wikipedia.org/wiki/Indentation#Indentation_in_programming indent].
* add a final <tt>/macroname</tt> to know that you are closing just that macro, like <tt><nowiki>{.load | something /load.}</nowiki></tt>. The final <tt>/load</tt> is ignored by hfs, it's just for your convenience.
* add a final <tt>/macroname</tt> to know that you are closing just that macro, like <tt><nowiki>{.load|{.section|stuff /section.} /load.}</nowiki></tt>. The final <tt>/load</tt> is ignored by hfs, it's just for your convenience.


=== Quoting ===
=== 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''.
Sometimes you don't want HFS to consider plain text as part of the macro syntax. There's a way to tell HFS to not process the text, and it is called ''quoting''.
 
To quote a text you type <tt><nowiki>{:</nowiki></tt> then the text you want. To close the quoting just type <tt><nowiki>:}</nowiki></tt> .
 
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 [http://en.wikipedia.org/wiki/Pythonic 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 [http://en.wikipedia.org/wiki/Pythonic 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 [http://en.wikipedia.org/wiki/Switch_statement#Examples 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
<pre><nowiki>
switch(A) {
  case C: return D;
  case E: return F;
  ...
}
</nowiki></pre>
: 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
<pre><nowiki>
{.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
.}
</nowiki></pre>
 
; for | A | B | C | D | E
: repeats D by replacing variable <tt>{.^A.}</tt> 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 <tt>{.^A.}</tt> with value B the first time, value C the second time, D etc etc. Remember to quote the body.
: example: <tt>{.for each|i|10|100|1000|{:now it's {.^i.} :}.}</tt>
 
; 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.
: An additional parameter will be the external file from where to load the section.
; 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 delete : true if the user is allowed to delete the current item
:; 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"
:; accounts : semicolon-separated list of username of all accounts.
:;: By specifying an additional parameter '''group''' you'll get only groups. Respectively, a parameter '''users''' will give you only non-group accounts.
:; stop spiders : true if ''stop spider'' option is enabled.
 
; urlvar | A : url variable. Let's say in the url you see <tt>?sort=s</tt> 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 [[HFS: Time format|this page]] for format syntax. If A is not supplied, value "c" will be used.
: Optional parameter ''when'' to [[HFS: Time format|specify a time]] other than current.
 
=== Data manipulation ===
; substring | A | B | C : cut C from A to B, with B not included.
: Example: <tt>substring|(|belong|you (are) belong to us.}</tt> will be <tt>(are)</tt>.
: If A is not specified, C is taken from the start. If B is not specified, C is taken to the end.
: Optional parameter ''case=1'' to turn on case sensitivity.
: Optional parameter ''include'' to say if A and/or B must be included. Values: 0 (none), 1 (only A), 2 (only B), 1+2 (both).
; 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 <tt>+ - * / %</tt> (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 <tt>#/,&?:$@=+</tt>
; 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'' [http://www.rejetto.com/forum/index.php?topic=5353.msg1031074#msg1031074 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 or a section with this form
: <tt>key1=value1
: key2=value2
: key3=value3</tt>
: Let's say such text is the content of variable ''foo'', then to get <tt>value2</tt> you should do <tt>{.from table|foo|key2.}</tt>. (yes, poetry)
: If it was the content of table [bar] instead, then you should do <tt>{.from table|$bar|key2.}</tt>.
 
; set table | A | B
 
=== 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 ''<nowiki>
{.!up|Go to parent folder.}</nowiki>'') 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 ===
Consider most of these macros work only with operating system oriented paths, not the ones of the virtual file system.
 
; 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 <tt>C:\windows\win.ini</tt> or also <tt><nowiki>http://another_hfs_in_lan/the_file_i_want.txt</nowiki></tt>.
: With an optional parameter ''var'' you can specify the name of a variable where the whole content of the file will be stored. In such case, the macro will not expand to the content of the file, it's just in the var.
: Example: {.load|document.txt|var=mytext.}
 
; 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.
 
; append | A | B : add B at the end of file A. This may is also useful to keep special logs.
 
; 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.
 
; copy | A | B : copies file A to B, where B can be a folder or a full path+filename.
; move | A | B : as ''copy'', but A is removed when the copy is complete.
 
; chdir | A : changes the current [http://en.wikipedia.org/wiki/Working_directory working directory] of the HFS process.
 
; mkdir | A : creates the specified folder.
 
=== Virtual File System manipulation ===
; 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 B, and the name in C.
: Where in place of a simple name, you specify a virtual path, it will be used to place the folder (otherwise it will be place on the root).
; 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.
:; access / delete / upload = account list
:;: you can set permissions on the item, by specifying a list of usernames.
:;: Example: <tt>set item|videos|upload=robert</tt> will give account ''robert'' the ability to upload files in ''/videos''.
:;: Moreover, you can prefix permission by '''add''' or '''remove'''. In the previous example you set the ''upload'' permission. If ''mike'' already had upload permission, it is now lost. To '''add''' ''robert'' instead of just overwriting, you should <tt>{.set item|videos|add upload=robert.}</tt>. Now ''mike'' is happy too. As well, if you change your mind, and want to revoke your grant, just <tt>set item|videos|remove upload=robert</tt>.
:;: You can specify more than one username this way: '''robert;mike;rupert'''.
:;: Special usernames are '''@anonymous @any account @anyone'''.
 
=== Connections control ===
; disconnect | A | B : with no parameters, it disconnects current connection. If A is supplied, it is considered an [[HFS: IP masks|IP mask]], and every connection matching it (instead of the current one) will be disconnected. If also B is supplied, it will select only connections with port number same as B.
; set speed limit for connection | A : set a speed limit A in KB/s for the current connection
; set speed limit for address | A | B : set a speed limit B in KB/s for all connections coming from address A (no masks allowed)
; disconnection reason | A
: this ''reason'' determines if a download must be aborted. Any value will abort the download, but if the value is empty the download goes on.
: This macro will always return the value of the ''reason'' just before its call. So with a single call you are reading and setting the value. If no parameter is specified, then you are only reading.
; add header | A : adds line A to the [http://en.wikipedia.org/wiki/HTTP_header HTTP header].
 
; current downloads by user | A
: number of current downloads for user A. If no user is specified, it is used the username of the connection where the macro is running on.
; current downloads of file
: number of current downloads of current file.
: If an unnamed optional parameter is specified, it is used as [[HFS: IP masks|IP mask]], and only matching connections will be counted.
: If the parameter is given name ''user'', only connections by such user will be counted.
 
=== Account manipulation ===
; set account | A | ...
: by specifying the username in A, you'll be able to specify all others settings in following parameters.
: Other parameters name must be named in the form <tt>set account|username|thing=value|thing2=value2</tt>.
: These are available parameters you can specify after the username
:;password : set a new password
:;newname : set a new username
:;redirect : set the homonymous field
:;no limits : set the homonymous field (true/false)
:;enabled : set the homonymous field (true/false)
:;is group : set the homonymous field (true/false)
:;member of : set the ''member of'' field (specify more than one group separating by semicolons ";" )
:;add member of : add more groups to the above
:;remove member of : remove some groups to the above


; new account | A | ...
To quote text you type <tt>{:</tt> then the text you want. To close the quoting just type <tt>:}</tt> .
: creates a new account with username A. After the username, you can also specify all the values available in the ''set account'' macro.


; member of | A
This capability is very useful with macros ''set'' and ''if''. You'll see later.
: true if the current user is member of group A (specify more than one group separating by semicolons ";").
: Optional parameter ''user'' to check a different user.
: Example <tt>member of|administrators|user=rejetto</tt> 


=== Others ===
Sometimes you may need to use the pipe in plain text. To tell HFS not to interpret it as a parameter, you can quote it.
; 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.
Example: <tt>{.add to log|print{:|:}pipes.}</tt> to have the pipe character in your log. Additionally, you can use the command {.no pipe||.}
; length | A : if A is 5 characters length, like <tt>abcde</tt>, it expands to 5.
; var length | A : it's like <tt>{.length|{.^A.}.}</tt>, but much faster.
; match | A | B : it is true if the content B is matched by [[HFS: File masks|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)
; pos | A | B : expands to the position of A in B. Zero if not found. Optional parameter ''ofs'' will start searching from specified position.
: Example <tt>pos|the|i'm the alpha and the omega|ofs=14</tt> expands to <tt>19</tt>
; breadcrumbs | A
: builds a [http://en.wikipedia.org/wiki/Breadcrumb_(navigation) 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'' .
; 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.
; add to log | A : add A to the log panel.
: Optional parameter ''color'' to [[HFS: color format|specify the color]].


=== Shortcuts ===
== Available commands ==
;$A : is a shortcut for '''section | A'''
Refer to [[HFS: scripting commands|the full list]] for available commands.
;?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 <tt>{.?name.} = X</tt> .
: It works also for '''<> != < <= > >=''' .


== Execution order ==
== Execution order ==
The current execution order of macros is: from inner to outer, from left to right.
The current execution order of macros is: from inner to outer, from left to right.  Macros are like XHTML in that they cannot overlap.


An example: <tt>{.A {.B.} {.C.}.} {.D.}</tt>. 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.
An example: <tt>{.A {.B.} {.C.}.} {.D.}</tt>. In this scenario, the order is B, C, A, D. HFS cannot process A until B and C are processed because it doesn't yet know what the macro {.A.} contains.


=== Problem ===
=== Problem and Solution ===
All macros, except [[#Quoting|quoted ones]], are expanded. 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 (after expanded), and vice versa.
All macros, except [[#Quoting|quoted ones]], are expanded, not matter what. For example, if you have the command {.if | A | B | C .}, then B and C will both be expanded, no matter what A is.
Many times you want C to be not even expanded, just because it's wasted CPU time, or because it will have undesired side-effects like when C contains the macro ''append'' (the file will be written anyway).


=== Workaround to the problem ===
Since it is important to be able to select the execution of B or C based on the value of A, there's a workaround.
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 B and C, you stop them from being executed. After the ''if'' command chooses between B (''then'') and C (''else'') based on A, it removes the surrounding quoting markers, if any, executing the proper command.  However, if B and C contain only text and not macros, they do not need to be quoted.
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.
Here's an example:
Let's see with an example
  <tt><nowiki>{.if| %user% | {.append|file.txt|someone is in!.} .}</nowiki></tt>
  <tt><nowiki>{.if| %user% | {.append|file.txt|someone is in!.} /if.}</nowiki></tt>
As we stated before, this is not going to choose whether or not to execute the append command. ''Append'' will always be executed, because every macro is executed. The only way to stop this is to surround with quoting markers:
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
  <tt><nowiki>{.if| %user% | {:{.append|file.txt|someone is in!.}:} .}</nowiki></tt>
  <tt><nowiki>{.if| %user% | {: {.append|file.txt|someone is in!.} :} /if.}</nowiki></tt>
By having quoted the ''append'', it will only be dequoted and then executed if ''%user%'' is not void. This is because the ''if'' automatically removes the appropriate quoting markers.
By having quoted the ''append'', it will only be executed if ''%user%'' is not void. This is because the ''if'' automatically removes the quoting markers.


Normally you would have to remove the markers yourself, by using macro ''dequote''. But few macros (like ''if'') have this special behavior, for your convenience. The special macros with such behaviour are: ''if'', ''set'', ''for'', ''switch'' and ''breadcrumbs''.
Normally you would have to remove the markers yourself, by using macro ''dequote''. But some macros (like ''if'') have this special behavior, for your convenience. The special macros with such behavior are: ''if'', ''set'', ''for'', ''for each'', ''switch'' and ''breadcrumbs''.  These allow you to use variables to change output.


=== From the beginning ===
=== From the beginning ===
Sometimes you need to be sure sometimes is done before every page is built and sent to the browser. In such case, you can put all your macros in the section '''[special:begin]'''.
Sometimes you need to be sure something gets done before every page is built and sent to the browser. In such case, you can put all your macros in the section '''[special:begin]'''.

Latest revision as of 12:18, 20 September 2011

Server-side scripting is possible through commands, also known as macros. These commands are mostly the same for templates and for hfs.events.

Please note: Macros are currently not available in 2.2f, but they will be in 2.3. You can experiment with them using beta versions.

Macros are very powerful and can be used to add new functionality to HFS.

How macros work

Differences with symbols

You are supposed to already know how templates work. Macros are similar to symbols. They are something you type in the template that gets substituted (we say expands to) with meaningful content when the user watch the page.

While symbols are just a name, macros have a name and optionally parameters. Additionally, while symbols only expand to text (such as %user% or %url%), macros can be used to perform server-side commands (such as {.save.} and {.load.} ).

You can tell symbols from macros easily: symbols are surrounded by %percent% signs, while macros are surrounded by {.dotted braces.}.

More on them

How the macro will work depends on the parameters. The same macro can have a variety of functions based on its parameters.

The macro section will copy the content of a [section] of the template. The parameter in the macro specifies which section. 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]. A shortcut for this is {.$style.}.

Parameters

After the macro name you specify parameters with "|" (pipes) and then the parameter: {.name|parameter.}. If the macro requires more parameters, you just add more pipes: {.name | parameter | another parameter | a third parameter.}.

Readability

You can also put macros inside macros, or nest them. However, this can often 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|{.section|stuff /section.} /load.}. The final /load is ignored by hfs, it's just for your convenience.

Quoting

Sometimes you don't want HFS to consider plain text as part of the macro syntax. There's a way to tell HFS to not process the text, and it is called quoting.

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

This capability is very useful with macros set and if. You'll see later.

Sometimes you may need to use the pipe in plain text. To tell HFS not to interpret it as a parameter, you can quote it. Example: {.add to log|print{:|:}pipes.} to have the pipe character in your log. Additionally, you can use the command {.no pipe||.}

Available commands

Refer to the full list for available commands.

Execution order

The current execution order of macros is: from inner to outer, from left to right. Macros are like XHTML in that they cannot overlap.

An example: {.A {.B.} {.C.}.} {.D.}. In this scenario, the order is B, C, A, D. HFS cannot process A until B and C are processed because it doesn't yet know what the macro {.A.} contains.

Problem and Solution

All macros, except quoted ones, are expanded, not matter what. For example, if you have the command {.if | A | B | C .}, then B and C will both be expanded, no matter what A is.

Since it is important to be able to select the execution of B or C based on the value of A, there's a workaround. By quoting B and C, you stop them from being executed. After the if command chooses between B (then) and C (else) based on A, it removes the surrounding quoting markers, if any, executing the proper command. However, if B and C contain only text and not macros, they do not need to be quoted. Here's an example:

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

As we stated before, this is not going to choose whether or not to execute the append command. 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!.}:} .}

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

Normally you would have to remove the markers yourself, by using macro dequote. But some macros (like if) have this special behavior, for your convenience. The special macros with such behavior are: if, set, for, for each, switch and breadcrumbs. These allow you to use variables to change output.

From the beginning

Sometimes you need to be sure something gets done before every page is built and sent to the browser. In such case, you can put all your macros in the section [special:begin].