Special text strings expand on the fly to display information, or trigger a function.
Macros are text strings in one of three basic forms:
%MACRONAME% %MACRONAME{ parameter="value" }% %MACRONAME{ param1="value " +"& more " param2="whatever" param1+="and even more" }%The third form is a new feature in Foswiki 2.1 to significantly improve readability with complex macros, see Readable Macros for details. These usually expand into content when a topic is rendered for viewing. There are two types of macros:
%CALC{}%
macro)
%T%
to get (a predefined preference setting)
%TOPIC%
to get Macros
(a predefined macro)
%CALC{ "$UPPER(Text)" }%
to get TEXT
(CALC is a macro defined by SpreadSheetPlugin)
!%TOPIC%
to get %TOPIC%
<nop>
anywhere in the macro, Eg. %<nop>TOPIC%
%ALLVARIABLES%
to get a full listing of all macros defined for a particular topic
default
parameter, in which case the value of the default
parameter will replace the macro call in the output. For example, %UNDEFINED{default="blank"}%
will expand to blank
.
commonTagsHandler()
)
%MACRO1{ something="%MACRO2{ somethingelse="%MACRO3%, %MACRO4%" }%" }%The macros are expanded in this order: MACRO3, MACRO4, MACRO2, MACRO1.
%
, and escaping any "
character it uses (becomes \"
)
$percent/$percnt
format tokens. Generally only output parameters like header
, format
and footer
support format tokens.
%MACRO1{ format="$percentMACRO2{ format=\"%MACRO3%, %MACRO4%\" }$percent" }%The macros are expanded in this order: MACRO3, MACRO4, MACRO1, MACRO2.
%MYVAR%
, %MyVar%
, %My2ndVar%
, and %My_Var%
are all separate, valid macro names (macros are case sensitive - %MyVAR%
and %MYVAR%
are not the same).
By convention all settings, predefined macros and macros registered by plugins are always UPPER-CASE.
[multiple of 3 spaces] * [space] Set [space] MACRONAME [space] = [space] value
Example:
* Set WEBBGCOLOR = #FFFFC0Macros defined using preference settings are expanded by enclosing their name in percent signs. So when you write
%WEBBGCOLOR%
, it gets expanded to #B9DAFF
A preference macro is always taken from the most current topic revision, even when accessing previous revisions of a topic.
Preferences can be defined in a number of places:Set
statements which occur at numerically higher locations override macros of the same name defined at lower numbered levels, unless the macro was listed in a finalpreferences setting (finalised) at a lower-numbered level. in this case, the macro is locked to the value at that level; set
statements at higher-numbered levels are ignored.
3-spaces,asterisk,equals,value
* Set MYSETTING = My setting value
When using the Wysiwyg editor, click the "Bullet" button and write the setting as a simple bullet. Don't include the asterisk.
Spaces between the = sign and the value will be ignored. You can split a value over several lines by indenting following lines with spaces - as long as you don't try to use * as the first character on the following line.
Example:* Set MACRONAME = value starts here and continues here
Whatever you include in your macro will be expanded on display, exactly as if it had been entered directly (though see Parameters, below).
Example: Create a custom logo macro%MYLOGO%
, define the preference settings in the web's WebPreferences topic, and upload a logo file, ex: mylogo.gif
. You can upload by attaching the file to WebPreferences, or, to avoid clutter, to any other topic in the same web, e.g. LogoTopic
. Sample preference setting in WebPreferences:
* Set MYLOGO = %PUBURL%/%WEB%/LogoTopic/mylogo.gifPreference settings are case sensitive. (Foswiki by convention always writes settings in upper case.)
* Set lower = This is LOWER * Set LOWER = This is UPPER * Set LoWeR = This is MIXED Expand %lower%, %LOWER% and %LoWeR%Expand %lower%, %LOWER% and %LoWeR%. preference settings can easily be disabled with a # sign. Example:
* #Set DENYWEBCHANGE = %USERSWEB%.UnknownUser
<!-- * Set HIDDEN = This will be invisible in the output -->You can also set preference settings in a topic by clicking the link
Edit topic preference settings
under More topic actions
. Preferences set in this manner are known as 'meta' preferences and are not visible in the topic text, but take effect nevertheless.
Preview
will show the wrong thing, and you must Save
the topic to see it correctly.
Foswiki always reads the settings from the most current topic revision, so viewing older revisions of a topic can show unexpected results.
And especially important, preference settings are never overridden or set in "%INCLUDE{" topics. in the below example about weather conditions, note the difference in the CONDITIONS expansion* Set CONDITIONS = According to [[%BASETOPIC%]] the %WHAT% is %STATE% today (Set in ...).You can call this macro passing in values for
WHAT
and STATE
. For example: %CONDITIONS{WHAT="sea" STATE="choppy"}%
DEFAULT
gets the value of any unnamed parameter in the macro call.
default
parameter so that they expand to something even when a value isn't passed for them in the call.
* Set WEATHER = It's %DEFAULT{default="raining"}%.
%WEATHER%
expands to It's raining.
%WEATHER{"sunny"}%
expands to It's sunny.
Local
in place of Set
in the macro definition. For example, if the user sets the following in their home topic:
* Set EDITBOXHEIGHT = 10 * Local EDITBOXHEIGHT = 20Then, when they are editing any other topic, they will get a 10 high edit box. However, when they are editing their home topic they will get a 20 high edit box.
Local
can be used wherever a preference needs to take a different value depending on where the current operation is being performed.
Use this powerful feature with great care! %ALLVARIABLES%
can be used to get a listing of the values of all macros in their evaluation order, so you can see macro scope if you get confused.
%SEARCH%
, are powerful and general tools.
%BASETOPIC%
, %INCLUDE%
, and the mighty %SEARCH%
.
\
character. This is generally useful but with macros you need precise control of spaces which \
does not give you.
From Foswiki 2.1 it's now possible to use the following in macros to make them much more readable: parameter="value" +"more" +"and more"
param1+="value1" param2+="value2" param1+="more1" param2+="more2"
type="query" nonoise="on" web="Batch" header="|*Batch Code*|*Batch Value*|*Currencies*|*Sizes*|*Count*|*To Queue*|*Killed*|*Ignored*|*Errors*|*Notes*|*History*|*Flow*|" format="| [[$topic][$formfield(BatchCode) $formfield(BatchDate) $formfield(BatchNumber) $formfield(BatchLocation)]]"}$percnt | $formfield(BatchValue) | $formfield(BatchCurrencies) | $formfield(BatchSizes) | $formfield(BatchCount) | $formfield(BatchToQueue) | $formfield(BatchKilled) | $formfield(BatchIgnored) | <div style='background-color:#$percntCALC{$SET(dat,$formfield(BatchDate))$SET(pdat,$formfield(BatchProcessDate))$SET(age,$EVAL($percnt$formfield(BatchCode)_days$percnt))$IF($EXACT($percntWORKFLOWSTATE{\"$topic\"}$percnt,CHECKED),FAF0D4,$IF($GET(age)>=0,ff3300,$IF($GET(age)>=-2,ff9933,66cc33)))}$percnt'>$formfield(BatchErrors) </div> | $formfield(BatchOperators) | $formfield(BatchActivities) | <form name=\"Edit\" action=\"%SCRIPTURLPATH{edit}%/%WEB%/\"> <input type=\"hidden\" name=\"action\" value=\"form\"/> <input type=\"hidden\" name=\"topic\" value=\"$topic\"/> <input type=\"hidden\" name=\"redirectto\" value=\"%TOPIC%?%QUERYSTRING{encode="url"}%\"/> <input type=\"image\" src=\"%ICONURL{pencil}%\" alt=\"Edit Form\" /> </form> $formfield(BatchNotes) | $percntWORKFLOWHISTORY{\"$topic\"}$percnt | $percntWORKFLOWTRANSITION{\"$topic\" redirectto=\"%TOPIC%?%QUERYSTRING{encode="url"}%\"}$percnt |"That is very hard to read and debug, and even harder to maintain months or years later and for readability this example excludes the complex and many line
search
parameter.
Now, we can improve that by splitting each parameter onto a separate line:
type="query" nonoise="on" web="Batch" header="|*Batch Code*|*Batch Value*|*Currencies*|*Sizes*|*Count*|*To Queue*|*Killed*|*Ignored*|*Errors*|*Notes*|*History*|*Flow*|" format="| [[$topic][$formfield(BatchCode) $formfield(BatchDate) $formfield(BatchNumber) $formfield(BatchLocation)]] | $formfield(BatchValue) | $formfield(BatchCurrencies) | $formfield(BatchSizes) | $formfield(BatchCount) | $formfield(BatchToQueue) | $formfield(BatchKilled) | $formfield(BatchIgnored) | <div style='background-color:#$percntCALC{$SET(dat,$formfield(BatchDate))$SET(pdat,$formfield(BatchProcessDate))$SET(age,$EVAL($percnt$formfield(BatchCode)_days$percnt))$IF($EXACT($percntWORKFLOWSTATE{\"$topic\"}$percnt,CHECKED),FAF0D4,$IF($GET(age)>=0,ff3300,$IF($GET(age)>=-2,ff9933,66cc33)))}$percnt'>$formfield(BatchErrors) </div> | $formfield(BatchOperators) | $formfield(BatchActivities) | <form name=\"Edit\" action=\"%SCRIPTURLPATH{edit}%/%WEB%/\"> <input type=\"hidden\" name=\"action\" value=\"form\"/> <input type=\"hidden\" name=\"topic\" value=\"$topic\"/> <input type=\"hidden\" name=\"redirectto\" value=\"%TOPIC%?%QUERYSTRING{encode="url"}%\"/> <input type=\"image\" src=\"%ICONURL{pencil}%\" alt=\"Edit Form\" /> </form> $formfield(BatchNotes) | $percntWORKFLOWHISTORY{\"$topic\"}$percnt | $percntWORKFLOWTRANSITION{\"$topic\" redirectto=\"%TOPIC%?%QUERYSTRING{encode="url"}%\"}$percnt |"That helps a little, but the
format
parameter still covers multiple lines and is still hard to follow.
Now we could use the standard Foswiki line continuation, but you do not get full control of spacing and you cannot achieve the result you want. In the example above, which creates a table, one possible problem is having cell values centred instead of right justified.
This is when the ability to cleanly separate comes into its own. This is achieved by using the+"more"
option to break a parameter into appropriate pieces, we can now transform the above to:
type="query" nonoise="on" web="Batch" header="" +"|*Batch Code*" +"|*Batch Value*" +"|*Currencies*" +"|*Sizes*" +"|*Count*" +"|*To Queue*" +"|*Killed*" +"|*Ignored*" +"|*Errors*" +"|*Notes*" +"|*History*" +"|*Flow*" +"|" format="" +"| [[$topic][$formfield(BatchCode) $formfield(BatchDate) $formfield(BatchNumber) $formfield(BatchLocation)]]" +"| $formfield(BatchValue) " +"| $formfield(BatchCurrencies) " +"| $formfield(BatchSizes) " +"| $formfield(BatchCount) " +"| $formfield(BatchToQueue) " +"| $formfield(BatchKilled) " +"| $formfield(BatchIgnored) " +"| <div style='background-color:#" +"$percntCALC{" +"$SET(dat,$formfield(BatchDate))" +"$SET(pdat,$formfield(BatchProcessDate))" +"$SET(age,$EVAL($percnt$formfield(BatchCode)_days$percnt))" +"$IF($EXACT($percntWORKFLOWSTATE{\"$topic\"}$percnt,CHECKED),FAF0D4," +"$IF($GET(age)>=0,ff3300,$IF($GET(age)>=-2,ff9933,66cc33)))}" +"$percnt'>$formfield(BatchErrors) </div> " +"| $formfield(BatchOperators) " +"| $formfield(BatchActivities) " +"| <form name=\"Edit\" action=\"%SCRIPTURLPATH{edit}%/%WEB%/\">" +"<input type=\"hidden\" name=\"action\" value=\"form\"/>" +"<input type=\"hidden\" name=\"topic\" value=\"$topic\"/>" +"<input type=\"hidden\" name=\"redirectto\" value=\"%TOPIC%?%QUERYSTRING{encode="url"}%\"/>" +"<input type=\"image\" src=\"%ICONURL{pencil}%\" alt=\"Edit Form\" /> </form> $formfield(BatchNotes) " +"| $percntWORKFLOWHISTORY{\"$topic\"}$percnt " +"| $percntWORKFLOWTRANSITION{\"$topic\" redirectto=\"%TOPIC%?%QUERYSTRING{encode="url"}%\"}$percnt " +"|"Note the use of
+
to concatenate all the pieces of the format
and header
parameters together. Also note that each piece is of the form +"more"
and as that is quoted you have full control of the contents. This is especially true for those all important spaces — you'll see some pieces have a trailing space and some do not.
header
and format
parameters above start each table cell on a separate line. This makes it a little easier to scan the topic text the match the cell positions and check they line up (i.e. the Header cell #n will match the contents from Format cell #n).
Another trick used above for easier maintenance is to initialise a parameter to ""
and then add the pieces on separate lines. (In the example we are dealing with table cells hence "|"
, on a separate line, is used as an end of row marker.) This helps by allowing you to cut and paste whole lines, representing cells, into different column positions without concern as to whether or not it's the 1st column, last column or one in the middle because they have an identical structure.
Nonetheless, keeping the header
of a table column separate from the format
of each cell in the SEARCH can still be troublesome especially as the number of columns and/or complexity of header
and footer
cells grow.
To remedy that the param1+="value1" param2="value2" param1+="more1" param2+="more2"
can be used. Not that the pieces of different parameters can now be intermingled in whatever way you need to make the macro easier to read. For example we can transform the above to:
type="query" nonoise="on" web="Batch" header+="|*Control record*" format+="| <span style=\"display:none;\">" +"$formfield(BatchNumber) $formfield(BatchLocation) " +"$percntCALC{$SUBSTRING($topic,13,3)}$percnt $formfield(BatchDate) " +"!$formfield(BatchCode)" +"</span>" +"<a id=\"$topic\"/>[[$topic]]" header+="|*Batch Value*" format+="| $formfield(BatchValue) " header+="|*Batch Curr- encies*" format+="| $formfield(BatchCurrencies) " header+="|*Success*" format+="| $formfield(BatchCount) " header+="|*To Queue*" format+="| $formfield(BatchToQueue) " header+="|*Killed*" format+="| $formfield(BatchKilled) " header+="|*Ignored*" format+="| $formfield(BatchIgnored) " header+="|*Batch Size*" format+="| $formfield(BatchSizes) " header+="|*Batch Errors*" format+="| <div style='background-color:#" +"$percntCALC{$SET(dat,$formfield(BatchDate))" +"$SET(pdat,$formfield(BatchProcessDate))" +"$SET(age,$EVAL($percnt$formfield(BatchCode)_days$percnt))" +"$IF($EXACT($percntWORKFLOWSTATE{\"$topic\"}$percnt,CHECKED),FAF0D4," +"$IF($GET(age)>=0,ff3300,$IF($GET(age)>=-2,ff9933,66cc33)))}$percnt'>" +"$formfield(BatchErrors) </div> " header+="|*Batch Oper- ators*" format+="| $formfield(BatchOperators) " header+="|*Batch Acti- vities*" format+="| $formfield(BatchActivities) " header+="|*Batch Notes*" format+="| <form name=\"Edit\" action=\"%SCRIPTURLPATH{edit}%/%WEB%/\">" +"<input type=\"hidden\" name=\"action\" value=\"form\"/>" +"<input type=\"hidden\" name=\"topic\" value=\"$topic\"/>" +"<input type=\"hidden\" name=\"redirectto\" value=\"%TOPIC%?%QUERYSTRING%#$topic\"/>" +"<input type=\"image\" src=\"%ICONURL{pencil}%\" alt=\"Edit Form\" /> </form> $formfield(BatchNotes)" header+="|*Status History*" format+="| $percntWORKFLOWHISTORY{\"$topic\"}$percnt " header+="|*Change Status*" format+="| $percntWORKFLOWTRANSITION{\"$topic\" redirectto=\"%TOPIC%?%QUERYSTRING%#$topic\"}$percnt " header+="|" format+="|"There is no guesswork (or cell counting) required to work out which cell
header
matches which format
cell. Also note how this enables you to better manage table layouts by keeping the coding for each column together and in turn the easy ability to move a column into other positions by cut and paste.
The 1st parameter in a macro does not have to be named, e.g %MACRO{"I'm the first"}%
. How can you extend such a value with name+="more"
when it has no name? Well actually it does and it's name is _DEFAULT
so you can have %MACRO{"I'm the first" type="X" _DEFAULT+=", with seconds"}%
. Some macros may give you an alternative name to use, e.g. %SEARCH
allows search
i.e. %SEARCH{search="find him" type="literal" search+=" and her"}%
. In this situation it is better to use one or the other and not both as that could lead to confusion. In the case of %SEARCH
if you use both then this macro will ignore search
and use _DEFAULT
, so take care.
The following is a working example using the Foswiki FAQ topics follows. You can use this to create yourself a Sandbox topic to try out the +
and +=
options to experiment with ways to make macros easier to read.
%SEARCH{"form.name ~ 'FAQForm'" type="query" web="System" nonoise="on" header+="|*Title*" format+="|$formfield(TopicTitle)" header+="|*Topic*" format+="|$web.$topic" header+="|*Summary*" format+="|$formfield(TopicSummary)" header+="|*Related Topics*" format+="|$formfield(RelatedTopics)" header+="|" format+="|" }%This gives:
Title | Topic | Summary | Related Topics |
---|---|---|---|
Creating applications with DataForms | Creating applications with DataForms | How can I create a simple data form based application? | UserDocumentationCategory |
Deleting and renaming topics | Deleting and renaming topics | How do I delete or rename a topic? | UserDocumentationCategory |
Deleting and renaming attachments | Deleting and renaming attachments | How do I delete or rename a file attachment? | UserDocumentationCategory |
Get Foswiki | Get Foswiki | I would like to install Foswiki on my server. Can I get the source? | AdminDocumentationCategory |
Explanation of Foswiki's repRev feature |
Explanation of Foswiki's =repRev= feature | Why does the topic revision not increase when I edit a topic? | UserDocumentationCategory |
Foswiki software license | Foswiki software license | Foswiki has a GPL (GNU General Public License). What is GPL? | UserDocumentationCategory |
HiddenUsersAndGroups | HiddenUsersAndGroups | How to hide groups and users from common view | VarGROUPINFO, AccessControl |
Rebuilding WikiUsers topic | Rebuilding %WIKIUSERSTOPIC% topic | How can I re-build my WikiUsers topic? | Foswiki:Support.Faq27, TopicUserMappingContrib, WikiUsers, AdminDocumentationCategory |
Fixing broken %SEARCH caused by faulty configuration. |
Fixing broken =%SEARCH= caused by faulty configuration. | I've problems with the WebSearch. There is no Search Result on any inquiry. By clicking the Index topic it's the same problem. | AdminDocumentationCategory |
How simultaneous edits are handled in Foswiki | How simultaneous edits are handled in Foswiki | What happens if two or more users try to edit the same topic simultaneously? | UserDocumentationCategory |
System.FAQTemplate | |||
Troubleshooting Extensions | Troubleshooting Extensions | A plugin I installed is not working. How can I troubleshoot it? | Foswiki:Support.Faq31, InstalledPlugins, AdminDocumentationCategory |
Definition of "WikiWiki" | Definition of "WikiWiki" | So what is this WikiWiki thing exactly? | WelcomeGuest, GoodStyle, FrequentlyAskedQuestions, WikiCulture |