The most typical kind of "plugin" (I'll call it a "recipe" from here on out because that's how they're named in the PmWiki world) is to establish some kind of "markup rule". This means you are defining some particular "pattern" of text in your page which will cause some action and cause that particular text to be replaced with something else.
The simplest possible markup would be a straight replacement. Here is a markup to replace all occurrences of the letter "a" with the letter "z":
Markup('a2z', '>{$var}', '/a/', 'z');
Then your page with this text:
The alphabet begins with "abc"
will display as this:
The zlphzbet begins with "zbc"
It's not very useful, but it gives you the most basic idea of what markup text is doing.
Creating a new markup involves calling the PmWiki Markup() function. This is usually done by editing your config.php, but you can also put it in a custom group or custom page PHP file -- you can read about those options at LocalCustomizations and GroupCustomizations.
The Markup() function takes 4 arguments:
scripts/stdmarkup.php.)
Arguments 3 and 4 are simply arguments which will be passed to preg_replace. You search for argument #3 and you replace it with argument #4.
Having said all that, the single best way to learn how to write your own recipe or markup is to look at examples of what other people have done.
(:comment ...:) markup ruleHere is the definition of the markup rule for the (:comment ...:) markup from scripts/stdmarkup.php:
Markup('comment', 'directives', '/\\(:comment .*?:\\)/i', '');
The purpose of the this markup is to allow you to put some kind of text in your source that is simply not displayed when browsing the page. So let's look at each argument:
'comment' -- a short, descriptive name - the ID of the rule
'directives' -- this is one of the 9 phases to answer the question when should the markup rule be processed.
'/\\(:comment .*?:\\)/i' -- this is a regular expression that will match (:comment ANY TEXT HERE:) -- this is the pattern that will be searched for. Since the last slash is followed by an i ("/i") then (:COMMENT some text:) and (:CoMmEnT some text:) would be matched as well -- the pattern being matched is case insensitive.
'' -- any occurrence of that pattern will be replaced with NOTHING. Thus the comment will simply disappear which is exactly what you want.
(:include ...:) markup ruleLet's look at another example from stdmarkup.php, the (:include PAGENAME:) markup rule. This rule is designed to pull the text from another page into the current page. Here's how it's defined in stdmarkup.php:
Markup('include', '>if',
'/\\(:include\\s+(\\S.*?):\\)/ei',
"PRR(IncludeText(\$pagename, PSS('$1')))");
Warning the /e modifier has been deprecated for years (and finally disabled with PHP7.2). For details on how to replace it check CustomMarkup#php55.
Each argument, in order:
'include' -- short and descriptive identification of what this rule does
'>if' -- process this rule after the rule with the ID 'if'
'/\\(:include\\s+(\\S.*?):\\)/ei' -- this regular expression matches a pattern (:include pagename:) where "pagename" is any sequence of non-whitespace characters. (Whitespace is a space or a tab or a newline character.) The important change is the /ei at the end. You already know that the "i" means to make the match case insensitive. The "e" means that the replacement text is a PHP expression that should be evaluated. (It also means that a bunch of backslashes will be put in front of certain characters that come from the search pattern when you use parentheses for regex captures.) Note that the "\\S.*?" is surrounded by parentheses which means it will be captured and available as $1 in the replacement text.
"PRR(IncludeText(\$pagename, PSS('$1')))" -- this is what the search pattern will be replaced with. But since that "e" was present above, this text will be interpreted as a PHP expression and evaluated. Note several things about this text that are important:
(:include pagename:) in your source will be replaced by the text from that page.
(:nogroupheader:) markup ruleThis markup rule is for the purpose of suppressing a group header from being displayed. For this to occur the global variable $GroupHeaderFmt must be set to a blank string. Here's the markup definition:
Markup('nogroupheader', '>include',
'/\\(:nogroupheader:\\)/ei',
"PZZ(\$GLOBALS['GroupHeaderFmt']='')");
'nogroupheader' -- short and descriptive identification of what this rule does
'>include' -- process this rule after the rule with the ID 'include' (yes, that's the rule we just looked at above)
'/\\(:nogroupheader:\\)/ei' -- a very simple regular expression with the same /ei options we saw above
"PZZ(\$GLOBALS['GroupHeaderFmt']='')"
$GroupHeaderFmt is accessed by the PHP super-global $GLOBALS[]
$GLOBALS['GroupHeaderFmt']='' -- it sets that value to blank
An action is executed by appending "?action=MYACTION" to the end of the address URL. The default action is always "browse" so if you don't see any action in your address bar then pmwiki will assume you meant "https://www.example.com/pmwiki/...?action=browse".
For example, if you are using CleanUrls? and you want to access the page
but you want to specify an action of "source" (this displays the page source) then you would use
For another example, if you are NOT using CleanUrls and you want to access the page
but you want to specify an action of "edit" (this allows you to edit the page) then you would use
(This is an alternate method to edit a page if there is no link to do so.)
There are several other built-in actions, but sometimes it is convenient (as a PHP developer) to add your own custom action.
If you wanted to add an action named "mynewaction" and you wanted it to call a function HandleMyNewAction() then you might put this code in your config.php or in your recipe script:
$HandleActions['mynewaction'] = 'HandleMyNewAction';
$HandleAuth['mynewaction'] = 'admin';
function HandleMyNewAction($pagename, $auth) {
$page = RetrieveAuthPage($pagename, $auth);
if (!$page) {
...
}
...
}
Note that the array just determines what default authorization is passed as the second argument to your custom function. You are responsible to make enforce that authorization (typically through $HandleAuth[]CondAuth() or RetrieveAuthPage()).
Other pages of interest may be found in the PmWiki Developer and markup categories.
This page may have a more recent version on pmwiki.org: PmWiki:CustomMarkupAlt, and a talk page: PmWiki:CustomMarkupAlt-Talk.