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%
%SHOWPREFERENCE%
to get a full listing of all macros defined for a particular topic, or %SHOWPREFERENCE{"foo"}%
to see an individual setting.
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.
A preference setting looks like this:
[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
Preferences can also be set dynamically by using the %SET{"setting" value="value"}%
Macro. With the exception of these dynamic preference settings, preferences are 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. When a preference is finalized, the macro is locked to the value at that level; SET
statements at higher-numbered levels are ignored. Looking at it graphically:
Access Control rules (ACLs) are also written as preference settings. ACLs cannot be dynamically modified by the %SET{}%
macro.
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 or the leading spaces.
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. (Not when using the Wysiwyg editor.)
Example:* Set MACRONAME = value starts here and continues herepreference settings can easily be disabled with a # sign. Example:
* #Set DENYWEBCHANGE = %USERSWEB%.UnknownUserWhatever you include in your bullet style setting will be expanded on display, exactly as if it had been entered directly (though see Parameters, below). (%SET{}% settings are expanded during the set process. See VarSET for further information. 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%.
<!-- * 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.
If the same setting appears as both an inline setting, and in topic meta settings, the meta setting will override the inline setting! There is no warning when the setting is duplicate. This should be avoided to prevent confusion.
Preview
will show the wrong thing, and you must Save
the topic to see it correctly. (%SET{}%
style settings are assigned during the topic rendering, and their effect will be visible in the preview.)
Foswiki always reads the bullet style settings from the most current topic revision, so viewing older revisions of a topic can show unexpected results.
And especially important, bullet style preference settings are never overridden or set in when a topic content is obtained by "%INCLUDE{". However %SET{ style settings can be overidden when an INCLUDE is expanded. in the below example about weather conditions, note the difference in the CONDITIONS expansionMacros defined using preference settings can take parameters. These are symbols passed in the call to the macro to define local macros that will be expanded in the output. For example, Both Macros and PreferenceSettings have a Set statement that defines theNote that %CONDITIONS% expands differently when this example is viewed in PreferenceSettings. This is because Set statement are not active in included topics. The including topic's set statements are used.%CONDITIONS%
macro as shown here:* Set CONDITIONS = According to [[%TOPIC%]] the %WHAT% is %STATE% today (Set in ...).The%TOPIC%
shows where the CONDITIONS macro is expanded, and the … shows where the Set statement was actually defined. You can call this macro passing in values forWHAT
andSTATE
. For example:
%CONDITIONS{WHAT="sea" STATE="choppy"}%
- expands to
According to PreferenceSettings, The sea is choppy today. (Set in Macros).
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.
%SET{}%
macro.
Local
in place of Set
in the macro definition. For example, if the user sets the following in their home topic:
* Local NOWYSIWYG = 1Then, when they are editing any other topic, they will get the Wysiwyg editor. However, when they are editing their home topic they will get the wikitext editor.
Local
can be used wherever a preference needs to take a different value depending on where the current operation is being performed.
%SHOWPREFERENCE%
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.
%SHOWPREFERENCE{"CONDITIONS"}%expands into:
%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 |