Recent Changes - Search:

PmWiki

pmwiki.org

edit SideBar

https://www.jetphotos.com/photographer/598301 https://www.jetphotos.com/photographer/598304 https://www.jetphotos.com/photographer/598305 https://www.jetphotos.com/photographer/598307 https://www.jetphotos.com/photographer/598310 https://www.jetphotos.com/photographer/598312 https://www.jetphotos.com/photographer/598317 https://www.jetphotos.com/photographer/598318 https://www.jetphotos.com/photographer/598320 https://www.jetphotos.com/photographer/598321 https://www.jetphotos.com/photographer/598322 https://www.jetphotos.com/photographer/598324 https://www.jetphotos.com/photographer/598328 https://www.jetphotos.com/photographer/598340 https://www.jetphotos.com/photographer/598341 https://www.jetphotos.com/photographer/598346 https://www.jetphotos.com/photographer/598349 https://www.jetphotos.com/photographer/598357 https://www.jetphotos.com/photographer/598366 https://www.jetphotos.com/photographer/598372 https://www.jetphotos.com/photographer/598374 https://www.jetphotos.com/photographer/598378 https://www.jetphotos.com/photographer/600028 https://www.jetphotos.com/photographer/600031 https://www.jetphotos.com/photographer/600032 https://www.jetphotos.com/photographer/600034 https://www.jetphotos.com/photographer/600036 https://www.jetphotos.com/photographer/600037 https://www.jetphotos.com/photographer/600039 https://www.jetphotos.com/photographer/600041 https://www.jetphotos.com/photographer/600042 https://www.jetphotos.com/photographer/600045 https://www.jetphotos.com/photographer/600046 https://www.jetphotos.com/photographer/600047 https://www.jetphotos.com/photographer/600048 https://www.jetphotos.com/photographer/600050 https://www.jetphotos.com/photographer/600051 https://www.jetphotos.com/photographer/600052 https://www.jetphotos.com/photographer/600053 https://www.jetphotos.com/photographer/600055 https://www.jetphotos.com/photographer/600057 https://www.jetphotos.com/photographer/600641 https://www.jetphotos.com/photographer/600644 https://www.jetphotos.com/photographer/600645 https://www.jetphotos.com/photographer/600646 https://www.jetphotos.com/photographer/602231 https://www.jetphotos.com/photographer/602240 https://www.jetphotos.com/photographer/602244 https://www.jetphotos.com/photographer/602247 https://www.jetphotos.com/photographer/602261 https://www.jetphotos.com/photographer/602265 https://www.jetphotos.com/photographer/602279 https://www.jetphotos.com/photographer/602307 https://www.jetphotos.com/photographer/602315 https://www.jetphotos.com/photographer/602323 https://www.jetphotos.com/photographer/602340 https://www.jetphotos.com/photographer/602346 https://www.jetphotos.com/photographer/602741 https://www.jetphotos.com/photographer/602743 https://www.jetphotos.com/photographer/602744 https://www.jetphotos.com/photographer/602745 https://www.jetphotos.com/photographer/602746 https://www.jetphotos.com/photographer/602748 https://www.jetphotos.com/photographer/602749 https://www.jetphotos.com/photographer/602750 https://www.jetphotos.com/photographer/602757 https://www.jetphotos.com/photographer/602758 https://www.jetphotos.com/photographer/602762 https://www.jetphotos.com/photographer/602763 https://www.jetphotos.com/photographer/602764 https://www.jetphotos.com/photographer/602769 https://www.jetphotos.com/photographer/602770 https://www.jetphotos.com/photographer/602772 https://www.jetphotos.com/photographer/602773 https://www.jetphotos.com/photographer/602774 https://www.jetphotos.com/photographer/602775 https://www.jetphotos.com/photographer/601186 https://www.jetphotos.com/photographer/601188 https://www.jetphotos.com/photographer/601189 https://www.jetphotos.com/photographer/601191 https://www.jetphotos.com/photographer/601192 https://www.jetphotos.com/photographer/601194 https://www.jetphotos.com/photographer/601196 https://www.jetphotos.com/photographer/601197 https://www.jetphotos.com/photographer/601248 https://www.jetphotos.com/photographer/601249 https://www.jetphotos.com/photographer/601250 https://www.jetphotos.com/photographer/601251 https://www.jetphotos.com/photographer/601252 https://www.jetphotos.com/photographer/601254 https://www.jetphotos.com/photographer/601255 https://www.jetphotos.com/photographer/601256 https://www.jetphotos.com/photographer/601258 https://www.jetphotos.com/photographer/601260 https://www.jetphotos.com/photographer/601261 https://www.jetphotos.com/photographer/601263 https://www.jetphotos.com/photographer/601264 https://www.jetphotos.com/photographer/601265 https://www.jetphotos.com/photographer/601266 https://www.jetphotos.com/photographer/601267 https://www.jetphotos.com/photographer/601268 https://www.jetphotos.com/photographer/601269 https://www.jetphotos.com/photographer/601270 https://www.jetphotos.com/photographer/601272 https://www.jetphotos.com/photographer/601273 https://www.jetphotos.com/photographer/602779 https://www.jetphotos.com/photographer/602780 https://www.jetphotos.com/photographer/602781 https://www.jetphotos.com/photographer/602782 https://www.jetphotos.com/photographer/600111 https://www.jetphotos.com/photographer/600112 https://www.jetphotos.com/photographer/600148 https://www.jetphotos.com/photographer/600151 https://www.jetphotos.com/photographer/600155 https://www.jetphotos.com/photographer/600157 https://www.jetphotos.com/photographer/600159 https://www.jetphotos.com/photographer/600161 https://www.jetphotos.com/photographer/600163 https://www.jetphotos.com/photographer/600647 https://www.jetphotos.com/photographer/600648 https://www.jetphotos.com/photographer/600649 https://www.jetphotos.com/photographer/600650 https://www.jetphotos.com/photographer/602889 https://www.jetphotos.com/photographer/602890 https://www.jetphotos.com/photographer/602891 https://www.jetphotos.com/photographer/602895 https://www.jetphotos.com/photographer/602897 https://www.jetphotos.com/photographer/602900 https://www.jetphotos.com/photographer/602904 https://www.jetphotos.com/photographer/602907 https://www.jetphotos.com/photographer/602913 https://www.jetphotos.com/photographer/602916 https://www.jetphotos.com/photographer/602918 https://www.jetphotos.com/photographer/602922 https://www.jetphotos.com/photographer/602923 https://www.jetphotos.com/photographer/602925 https://www.jetphotos.com/photographer/602926 https://www.jetphotos.com/photographer/600534 https://www.jetphotos.com/photographer/600535 https://www.jetphotos.com/photographer/600536 https://www.jetphotos.com/photographer/600538 https://www.jetphotos.com/photographer/600539 https://www.jetphotos.com/photographer/600540 https://www.jetphotos.com/photographer/600542 https://www.jetphotos.com/photographer/600543 https://www.jetphotos.com/photographer/600544 https://www.jetphotos.com/photographer/600547 https://www.jetphotos.com/photographer/600548 https://www.jetphotos.com/photographer/600549 https://www.jetphotos.com/photographer/600550 https://www.jetphotos.com/photographer/600552 https://www.jetphotos.com/photographer/600553 https://www.jetphotos.com/photographer/600555 https://www.jetphotos.com/photographer/600558 https://www.jetphotos.com/photographer/600565 https://www.jetphotos.com/photographer/600566 https://www.jetphotos.com/photographer/600567 https://www.jetphotos.com/photographer/600568 https://www.jetphotos.com/photographer/600571 https://www.jetphotos.com/photographer/600573 https://www.jetphotos.com/photographer/600575 https://www.jetphotos.com/photographer/600576 https://www.jetphotos.com/photographer/600577 https://www.jetphotos.com/photographer/600578 https://www.jetphotos.com/photographer/600666 https://www.jetphotos.com/photographer/600668 https://www.jetphotos.com/photographer/600669 https://www.jetphotos.com/photographer/600670 https://www.jetphotos.com/photographer/602963 https://www.jetphotos.com/photographer/601276 https://www.jetphotos.com/photographer/601280 https://www.jetphotos.com/photographer/601281 https://www.jetphotos.com/photographer/601284 https://www.jetphotos.com/photographer/601285 https://www.jetphotos.com/photographer/601286 https://www.jetphotos.com/photographer/601287 https://www.jetphotos.com/photographer/601288 https://www.jetphotos.com/photographer/601291 https://www.jetphotos.com/photographer/601293 https://www.jetphotos.com/photographer/602776 https://www.jetphotos.com/photographer/602777 https://www.jetphotos.com/photographer/602955 https://www.jetphotos.com/photographer/602956 https://www.jetphotos.com/photographer/602957 https://www.jetphotos.com/photographer/602959 https://www.jetphotos.com/photographer/602960 https://www.jetphotos.com/photographer/602961

(alternate) Introduction to Custom markup for Beginners

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.

(If you are wanting to create a custom action then click here.)

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:

  1. The arbitrary name you are going to give your new markup. It should be short but descriptive. Be careful you don't use the same name as another markup out there or that markup will no longer be active. (You can see the standard markup definitions in scripts/stdmarkup.php.)
  2. An indicator of WHEN you want this to occur. PmWiki has dozens of these markup rules and it makes a big difference in what order they occur.
    If one markup rules (#1) changes all occurrences of "a" into "b" and another markup (#2) changes all occurrences of "az" into "zz" it obviously makes a big difference in what order they occur.
    If #1 occurs before #2 on the text "azazaz" then you will end up with "bzbzbz". But if #2 occurs before #1 then you will end up with "zzzzzz".
    This argument is normally specified as a left-angle bracket ("before") or a right-angle bracket ("after") followed by the name of another rule. In my experience the most significant rule in terms of ordering is "{$var}" which substitutes variables -- if you say "<{$var}" then your markup will be processed before variables are substituted whereas if you say ">{$var}" then your markup will be processed after variables are substituted. But there are lots of other places in the whole order of rules -- someone else will have to go into more detail if you need it. That CustomMarkup page gives some good pointers there.

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.

  1. This is a regular expression. It can be as simple as "/a/" (match every occurrence of the character "a") up to very complicated and intricate patterns. Every time this pattern matches in your text it will be replaced with argument #4. Note that your pattern is always surrounded by forward slashes and there can be modifiers after the closing forward slash.
    These modifiers are single characters which you can read more about them at PCRE pattern modifiers. The key ones are "i" (ignore case), "s" (allow dot to match newlines), "m" (allow ^ and $ to match before/after newlines as well as begin/end of strings).
  2. This is the replacement text. It can be a simple string or it can include things like $1, $2, etc if you have parenthesized groups in argument #3 (you've got to be careful to put backslashes in front of the $ or else surround it in single-quotes, etc to delay the interpolation of those variables). Once you are into PHP functions then you need to read some of the many PHP tutorials on the net to see which way to go.

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.

The (:comment ...:) markup rule

Here 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:

  • ARG1: 'comment' -- a short, descriptive name - the ID of the rule
  • ARG2: 'directives' -- this is one of the 9 phases to answer the question when should the markup rule be processed.
  • ARG3: '/\\(: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.
  • ARG4: '' -- any occurrence of that pattern will be replaced with NOTHING. Thus the comment will simply disappear which is exactly what you want.

The (:include ...:) markup rule

Let'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:

  • ARG1: 'include' -- short and descriptive identification of what this rule does
  • ARG2: '>if' -- process this rule after the rule with the ID 'if'
  • ARG3: '/\\(: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.
  • ARG4: "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:
    • any variable names are protected from immediate substitution, either by putting a backslash in front of the dollar sign or by putting single-quotes around them.
    • The function PSS('$1') "Strips Slashes" -- it gets rid of those slashes that are inserted by the /e option above.
    • The function IncludeText() is a PmWiki function which accepts 2 arguments (a reference pagename and the pagename of the text that should be retrieved) and it returns the text from that page. This means that the (:include pagename:) in your source will be replaced by the text from that page.
    • The surrounding PRR() just tells PmWiki to run through the markup rules again in case something came in from the included text that needs to be processed by rules that have already been processed. PRR() always returns the value of its argument, so it's kind of a transparent function that has a side effect...

The (:nogroupheader:) markup rule

This 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']='')");
  • ARG1: 'nogroupheader' -- short and descriptive identification of what this rule does
  • ARG2: '>include' -- process this rule after the rule with the ID 'include' (yes, that's the rule we just looked at above)
  • ARG3: '/\\(:nogroupheader:\\)/ei' -- a very simple regular expression with the same /ei options we saw above
  • ARG4: "PZZ(\$GLOBALS['GroupHeaderFmt']='')"
    • Once again the variables are protected by escaping the dollar sign (placing a backslash in front of it)
    • The global variable $GroupHeaderFmt is accessed by the PHP super-global $GLOBALS[]
    • The core of this rule has this PHP text: $GLOBALS['GroupHeaderFmt']='' -- it sets that value to blank
    • The surrounding PZZ() simply says to get rid of any return value -- the search pattern will be replaced with a blank string

Custom actions

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 $HandleAuth[] 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 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.

Edit - History - Print - Recent Changes - Search
Page last modified on December 17, 2022, at 03:37 AM