This is a stripped-down version of a single section of Grok TiddlyWiki, optimized for fast loading and readability by search engines. Some features are missing.

For the full Grok TiddlyWiki experience, please visit the wiki version of this page.

Text Substitution

21st May 2021 at 3:28pm

We need to circle back around to answer a perplexing question from earlier. In an exercise in the Transclusions section, this didn't work:

{{ {{!!manager}}!!phone }}

Yet this did:

\define getPhone(person)
{{$person$!!phone}}
\end

<$macrocall$name="getPhone" person={{!!manager}}/>

What's up with that? At first glance, these snippets appear to be doing exactly the same thing.

This is one of the most confusing aspects of TiddlyWiki (some might say the most confusing), and it can sometimes trip up even veteran users. Fortunately, there is a reasonable explanation, albeit an obscure one, and once you understand the explanation you'll be able to tell what will work and what won't without having to try it.

Text substitution

It turns out that there's something entirely different about macros as compared to any other part of wikitext: Macros have their parameters replaced by text substitution (elsewhere you may see this called textual substitution or just substitution; we will use the term text substitution throughout this book). This is an extremely simple and dumb mechanism: TiddlyWiki simply takes the text that was provided in the parameter and dumps it into the body of the macro where the placeholder value was. While it's doing this, it completely stops understanding wikitext – it only worries about replacing the placeholders.

To see how this works and how it differs from using variables or transclusions outside of macros, let's define an example macro that prints some text if a person attended a meeting:

\define participantInMeeting(meeting, name)
<$list filter="[title[$meeting$]contains:participants[$name$]]"> The participant was in the meeting. </$list>
\end

If we call this macro as <<participantInMeeting EmployeeProfileSetupMeeting JaneDoe>>, we'll get The participant was in the meeting. If we instead call it as <<participantInMeeting EmployeeProfileSetupMeeting JoeSchmoe>>, we'll get nothing at all.

Notice that we're using the $list widget in a clever way we haven't seen yet: to display some wikitext only if some condition is met. We know the filter will have either one result, namely the title of the meeting (if the participant was in the meeting), in which case the contents of the list widget will get displayed once, or zero results (if the participant was not in the meeting), in which case the contents of the list widget will not be displayed at all. We don't care exactly what the output of the filter is, only whether there was any. Here's how this relates to text substitution. Suppose we call the macro thus: <<participantInMeeting "EmployeeProfileSetupMeeting" "]][[SomeRandomGuy">> ]][[SomeRandomGuy is a pretty weird name for a person (see Bobby Tables), but this causes the filter to become: [title[EmployeeProfileSetupMeeting]contains:participants[]][[SomeRandomGuy]] We then see The participant was in the meeting. – but ]][[SomeRandomGuy is not part of the participants field! (We created a second filter run – a new set of outer [square brackets] – for SomeRandomGuy, so that no matter what the output of the first filter run was, SomeRandomGuy would also be part of the output. We'll see more on multiple filter runs soon.) In contrast, if we use variable references inside the filter, we don't see the message: <$set name=participant value="]][[SomeRandomGuy">
<$set name=meetingTitle value="EmployeeProfileSetupMeeting"> <$list filter="[title<meetingTitle>contains:participants<participant>]">
The participant was in the meeting.
</$list> </$set>
2. TiddlyWiki performs a text substitution operation on the body of the macro using this parameter value, dumping the value ChrisSmith into the macro body in place of the $person$ placeholder. The result of this substitution is the single chunk of wikitext {{ChrisSmith!!phone}}. This expansion replaces the $macrocall widget. 3. Now that the macro is expanded, TiddlyWiki starts parsing the output of that macro as wikitext, sees the transclusion chunk {{ChrisSmith!!phone}}, and replaces that with the value of the phone field. (If the phone field contained further transclusions, TiddlyWiki would repeat this step again.) In other words, text substitution resulted in a single chunk of valid wikitext, which meant TiddlyWiki could understand what we were asking for. When you don't want text substitution If you don't need text substitution in a macro, you can use a different placeholder syntax, <<__parameter__>>, and avoid the possibility of bad effects. If you have the option to do this, you usually should. In all of our examples so far in this section, we have needed text substitution. Here's a modified participantInMeeting macro where we wouldn't need it: \define inMeetingIfFilterTrue(filter) <$list filter="$filter$">
The participant was in the meeting.
</$list> \end <<inMeetingIfFilterTrue "[title[EmployeeProfileSetupMeeting]contains:participants[JaneDoe]]">> This could land us in trouble, for example, if we passed in a filter that contained a double quote. Instead, we can do it this way: \define inMeetingIfFilterTrue(filter) <$list filter=<<__filter__>>>
The participant was in the meeting.
</$list> \end This works just like standard variable transclusions using the <<variable>> syntax in every way, except that by adding the extra double underscores, the reference is to a parameter of the macro rather than a variable. Exercises Exercise: (m) [Ex:BypassSecurityWithTextSubstitution] Suppose that the following macro is being used to validate whether the user is authorized to access something: \define checkAuthorization(password) <$list filter="[[$password$]match{$:/config/SecretPasswordExample}]"> The user is authorized. </$list>
\end

<<checkAuthorization "">>

Without looking up the password, figure out what you can fill in between the quotation marks in the macro call to bypass the password check. Check your answer by editing the live example above.

Exercise: (s) [Ex:PreserveSecurityWithTextSubstitution]

Change the filter in Ex:BypassSecurityWithTextSubstitution so that it is not vulnerable to this kind of manipulation. (Of course, the user could still just edit the tiddler and remove the authentication altogether. TiddlyWiki is not a suitable environment when users need to have limited access.)

Go back through some of the macros you have written for previous exercises in this chapter and see if you can find any opportunities to switch from text substitution to <<__parameter-transclusion__>>.
Tip: Try searching for the word define to find tiddlers that contain macro definitions.