version=pmwiki-2.2.69 ordered=1 urlencoded=1
author=Petko
charset=UTF-8
csum=correction; if $replace is a function name, the function has a name so not anonymous, works in PHP4
name=PmWiki.CustomMarkup
rev=147
targets=PmWiki.CustomMarkup,PmWiki.LocalCustomizations,Cookbook.ParseArgs,PmWiki.CustomMarkup-Talk,PmWiki.MailingLists,Cookbook.JavaScript,Cookbook.JavaScript-Editable,PmWiki.Skins,PmWiki.CustomMarkupAlt
text=(:Summary: Using the Markup() function for custom wiki syntax:)%0a(:Audience: administrators (intermediate) :)%0a!! Introduction%0a%0aPmWiki's markup translation engine is handled by a set of rules; each rule searches for a specific pattern in the markup text and replaces it with some replacement text.  Internally, this is accomplished by using PHP's "[[(http://www.php.net/)preg_replace]]" function.%0a%0aRules are added to the translation engine via PmWiki's Markup() or [[#php55|Markup_e()]] functions, which look like%0a%0a->[@Markup($name, $when, $pattern, $replace); # OR%0aMarkup_e($name, $when, $pattern, $replace);@]%0a%0awhere [@$name@] is a unique name (a string) given to the rule, [@$when@] says when the rule should be applied relative to other rules, [@$pattern@] is the pattern to be searched for in the markup text, and [@$replace@] is what the pattern should be replaced with.%0a%0aFor example, here's the code that creates the rule for [@''emphasized text''@] (in ''scripts/stdmarkup.php''):%0a%0a->[@Markup("em", "inline", "/''(.*?)''/", "%3cem>$1%3c/em>");@]%0a%0aBasically this statement says to create a rule called "em" to be performed with the other "inline" markups, and the rule replaces any text inside two pairs of single quotes with the same text ($1) surrounded by [@%3cem>@] and [@%3c/em>@].%0a%0aThe first two parameters to Markup() are used to specify the sequence in which rules should be applied.  The first parameter provides a name for a rule -- "[@em@]" in the example above.  We could've chosen other names such as "[@''@]", or even "[@twosinglequotes@]".  In general PmWiki uses the markup itself to name the rule (i.e., PmWiki uses "[@''@]" instead of "[@em@]"), but to keep this example easier to read later on we'll use a mnemonic name for now.%0a%0aThe second parameter says that this rule is to be done along with the other "inline" markups.  PmWiki divides the translation process into a number of phases:%0a%0a[@%0a_begin      start of translation%0afulltext    translations to be performed on the full text            %0asplit       conversion of the full markup text into lines to be processed%0adirectives  directive processing%0ainline      inline markups%0alinks       conversion of [[links]], url-links, and WikiWords     %0ablock       block markups%0astyle       style handling       %0a_end        end of translation%0a@]%0a%0aThis argument is normally specified as a left-angle bracket ("before") or a right-angle bracket ("after") followed by the name of another rule. %0a%0aThus, specifying "inline" for the second parameter says that this rule should be applied when the other "inline" rules are being performed.  If we want a rule to be performed with the directives -- i.e., before inline rules are processed, we would specify "directives" or "%3cinline" for the second parameter.%0a%0a->'-  A significant rule in terms of ordering is "[={$var}=]" which substitutes variables -- if you say "%3c[={$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. -'%0a%0aThe third parameter is a Perl-compatible regular expression. Basically, it is a slash, a [[regular expression -> http://www.php.net/manual/en/reference.pcre.pattern.syntax.php]], another slash, and a set of optional [[modifiers -> http://www.php.net/manual/en/reference.pcre.pattern.modifiers.php]].%0a%0aThe example uses the pattern string [@"/''(.*?)''/"@], which uses [@''(.*?)''@] as the regular expression and no options. (The regular expression says "find two single quotes in succession, then as few arbitrary characters as are needed to make the match find something, then two additional single quotes in succession"; the parentheses "capture" a part of the wikitext for later use.)%0a%0aThe fourth parameter is the replacement text that should be inserted instead of the marked-up wikitext. You can use [@$1@], [@$2@], etc. to insert the text from the first, second etc. parenthesised part of the regular expression.%0a%0aIn the example, we have [@"%3cem>$1%3c/em>"@], which is an [@%3cem>@], the text matched by the first parentheses (i.e. by the [@.*?@] section of the pattern), and [@%3c/em>@].%0a%0aHere's a rule for [@@@monospaced@@@] text:%0a%0a->[@Markup("@@", "inline", "/@@(.*?)@@/", "%3ccode>$1%3c/code>");@]%0a%0aand for a [@[:comment ...:]@] directive that is simply removed from the output:%0a%0a->[@Markup("comment", "directives", "/\\[:comment .*?:\\]/", '');@]%0a%0aOkay, now how about the rule for [@'''strong emphasis'''@]?  We have to be a bit careful here, because although this translation should be performed along with other inline markup, we also have to make sure that the rule for [@'''@] is handled ''before'' the rule for [@''@], because [@'''@] also contains [@''@]. The second parameter to Markup() can be used to specify the new rule's relationship to any other rule:%0a%0a->[@Markup("strong", "%3cem", "/'''(.*?)'''/", "%3cstrong>$1%3c/strong>");@]%0a%0aThis creates a rule called "strong", and the second parameter "%3cem" says to be sure that this rule is processed before the "em" rule we defined above.  If we wanted to do something after the "em" rule, we would use ">em" instead.  Thus, it's possible to add rules at any point in PmWiki's markup translation process in an extensible manner.  (In fact, the "inline", "block", "directives", etc., phases above are just placeholder rules used to provide an overall sequence for other rules.  Thus one can use "%3cinline" to specify rules that should be handled before any other inline rules.)             %0a%0aIf you want to disable available markup just call e.g.:%0a%0a->[@DisableMarkup("strong");@]%0a%0aPmWiki's default markup rules are defined in the ''scripts/stdmarkup.php'' file.  To see the entire translation table as the program is running, the scripts/diag.php module adds "[@?action=ruleset@]", which displays the set of defined markup rules in the sequence in which they will be processed.  You can see it at [[CustomMarkup?action=ruleset | CustomMarkup?action=ruleset]]. You must first enable the action by setting $EnableDiag = 1 in your configuration file.%0a%0a!! Other common examples%0a%0a!!! Define a custom markup to produce a specific HTML or Javascript sequence%0a%0aSuppose an admin wants to have a simple "[@(:example:)@]" markup that will always produce a fixed HTML string in the output, such as for a webring, Google AdSense display, or Javascript.  The Markup() call to do this would be:%0a%0a->[@%0aMarkup('example', 'directives',%0a  '/\\(:example:\\)/',%0a  Keep("%3cdiv class='example'>%3cp>Here is a %0a    %3ca target='_blank' href='http://www.example.com'>link%3c/a> to%0a    %3cem>example.com%3c/em>%3c/p>%3c/div>") );%0a@]%0a%0a* The first argument is a unique name for the markup ("example").%0a* The second argument says to perform this markup along with other directives.%0a* The third argument is the pattern to look for "(:example:)".%0a* The fourth argument is the HTML that "(:example:)" is to be replaced with.  We use the Keep() function here to prevent the output from being further processed by PmWiki's markup rule -- in the above example, we don't want the http://www.example.com url to be again converted to a link.%0a%0a[[#random]]%0a!!! Define a markup to call a custom function that returns content%0a%0aAn 'e' option on the [@$pattern@] parameter will cause the [@$replace@] parameter to be treated as a PHP expression to be evaluated instead of replacement text.  Thus, a markup to produce a random number between 1 and 100 might look like:%0a%0a->[@%0aMarkup('random', 'directives',%0a  '/\\(:random:\\)/e',%0a  "rand(1, 100)");%0a@]%0a%0aThis calls the PHP built-in rand() function and substitutes the directive with the result.  Any function can be called, including functions defined in a [[local customization(s)]] file.%0a%0aArguments can also be passed by using regular expression capturing parentheses, thus%0a%0a->[@%0aMarkup('randomargs', 'directives',%0a  '/\\(:random (\\d+) (\\d+):\\)/e',%0a  "rand('$1', '$2')");%0a@]%0a%0awill cause the markup [@(:random 50 100:)@] to generate a random number between 50 and 100.%0a%0a->%25note%25 Note:  Be very careful with the /e modifier in regular expressions; malicious authors may be able to pass strings that cause arbitrary and undesirable PHP functions to be executed.%0a%0aFor a PmWiki function to help with parsing arbitrary sequences of arguments and key=value pairs, see Cookbook:ParseArgs.%0a%0a[[#php55]]%0a!! Migration to PHP 5.5 and Markup_e()%0aSince PHP version 5.5, the /e evaluation modifier is deprecated and some hosting providers don't allow its use.%0a%0aRecent versions of the PmWiki core (2.2.58 and newer) allow new ways to define markup rules without being dependent on the /e eval modifier. The historical ways to define markup rules are not removed and work, but may be incompatible with PHP 5.5 installations.%0a%0a''Note: if your replacement pattern doesn't need evaluation, you must use Markup() like before and not Markup_e().''%0a%0aThe following is acceptable for PHP 5.5+ (compatible with PmWiki 2.2.58+, will also work in PHP 5.4 and older)%0a* Markup($name, $when, $pattern, $replace); %0a** $pattern can no longer have an "/e" modifier%0a** $replace can be a string with matches as $1, $2 etc.%0a** $replace can be a function name (callback) which will be called with the array of matches as argument%0a** instead of a string, the fourth parameter can be a definition of an anonymous function (note you can use anon functions this way since [[http://php.net/manual/en/functions.anonymous.php  | PHP 5.3.0+]]).%0a** for PHP 5.4 or earlier, $pattern can have an /e modifier. The existing ways still work, but under PHP 5.5, they trigger warnings for deprecated feature.%0a%0a* Markup_e($name, $when, $pattern, $replace);%0a** $pattern cannot have an "/e" modifier%0a** $replace can be a string with program code to be evaluated; note that the matches can be accessed with $m[1], $m[2] instead of '$1', '$2'%0a** $replace can be a function name (callback) which will be called with the array of matches as argument (PmWiki 2.2.59+).%0a%0aExamples:%0a%0a* For PHP 5.4 and older, this is acceptable:[@%0aMarkup('randomargs', 'directives',%0a  '/\\(:random (\\d+) (\\d+):\\)/e',%0a  "rand('$1', '$2')"%0a  );@]%0a%0a* For PHP 5.5 and newer, $replace is code, we call Markup_e():[@%0aMarkup_e('randomargs', 'directives',%0a  '/\\(:random (\\d+) (\\d+):\\)/',%0a  "rand(\$m[1], \$m[2])"%0a  );@]%0a  The array $m contains the matches in parentheses from the $pattern. The matches \$m[1] have a backslash before the $ sign to not be evaluated in the double-quoted string. %25note%25 This will also work in PHP 5.4 and older%25%25 but requires PmWiki 2.2.58 or newer.%0a%0a* For PHP 5.5 and newer, $replace is callback, we call Markup():[@%0aMarkup('randomargs', 'directives',%0a  '/\\(:random (\\d+) (\\d+):\\)/',%0a  "MyRandom"%0a  );%0afunction MyRandom($matches) {%0a  return rand($matches[1], $matches[2]);%0a}%0a@]%0a  %25note%25 This will also work in PHP 5.4 and older%25%25 but requires PmWiki 2.2.58 or newer.%0a%0aOther example:%0a* PHP 5.4 or older: [@%0aMarkup('Maxi:','%3clinks',%0a  "/\\b([Mm]axi:)([^\\s\"\\|\\[\\]]+)(\"([^\"]*)\")?/e",%0a  "Keep(LinkMaxi(\$pagename,'$1','$2','$4','$1$2'),'L')"%0a  );%0a@]%0a%0a* PHP 5.5 or newer, PmWiki 2.2.58+, $replace is program code, we call Markup_e(): [@%0aMarkup_e('Maxi:','%3clinks',%0a  "/\\b([Mm]axi:)([^\\s\"\\|\\[\\]]+)(\"([^\"]*)\")?/",%0a  "Keep(LinkMaxi(\$pagename,\$m[1],\$m[2],\$m[4],\$m[1].\$m[2]),'L')"%0a);%0a@]%0a  The '$1$2' string in the old version is replaced with \$m[1].\$m[2], two concatenated elements of the matches array.  %25note%25 This will also work in PHP 5.4 and older%25%25 but requires PmWiki 2.2.58 or newer.%0a%0a* $replace can also be a callback function, we call Markup(): [@%0aMarkup('Maxi:','%3clinks',%0a  "/\\b([Mm]axi:)([^\\s\"\\|\\[\\]]+)(\"([^\"]*)\")?/",%0a  "CallbackMaxi"%0a);%0afunction CallbackMaxi($m) {%0a  extract($GLOBALS["MarkupToHTML"]); # to get $pagename%0a  return Keep(LinkMaxi($pagename,$m[1],$m[2],$m[4],$m[1].$m[2]),'L');%0a}%0a@]%0a  Obviously, the LinkMaxi() function could also be rewritten to accept the matches array directly.  %25note%25 This will also work in PHP 5.4 and older%25%25 but requires PmWiki 2.2.58 or newer.%0a%0aThe above may seem complicated, but it is actually simpler to use your own callback function:%0a>>frame%3c%3c[@%0aMarkup('mykey', 'directives', %0a  '/\\(:mydirective (.*?) (.*?):\\)/i',%0a  'MyFunction'%0a);%0afunction MyFunction($matches) {%0a  extract($GLOBALS["MarkupToHTML"]);%0a%0a  # ... do stuff with $matches ...%0a%0a  return $out; # or return Keep($html);%0a}%0a@]%0a>>%3c%3c%0a%0a!!!Markup for both new and old versions of PmWiki%0aIf you want your recipe to work with PmWiki 2.2.58 and newer on PHP 5.5 and with older PmWiki+PHP versions, you can use something like this:%0a>>frame%3c%3c[@%0aif(function_exists('Markup_e')) { # new format, no /e%0a  Markup_e('rnd', 'directives', '/\\(:random (\\d+) (\\d+):\\)/', "rand(\$m[1], \$m[2])");%0a}%0aelse { # old format%0a  Markup('rnd', 'directives', '/\\(:random (\\d+) (\\d+):\\)/e', "rand($1, $2)");%0a}%0a@]%0a>>%3c%3c%0a%0aIf you have any questions about the new way to define custom markup, you can ask us [[CustomMarkup-Talk|at the talk page]] or on the [[PmWiki/mailing lists]].%0a%0a%0a!!FAQ%0a>>faq%3c%3c [[#faq]]%0aQ: How can I embed JavaScript into a page's output?%0aA: There are several ways to do this.  The [[Cookbook:JavaScript]] recipe describes a simple means for embedding static JavaScript into web pages using [[custom markup]].  For editing JavaScript directly in wiki pages (which can pose various security risks), see the [[(Cookbook:)JavaScript-Editable]] recipe.  For JavaScript that is to appear in headers or footers of pages, the [[skin(s)]] template can be modified directly, or %3cscript> statements can be inserted using the $HTMLHeaderFmt array.%0a%0aQ: How would I create a markup (''[=(:nodiscussion:)=]'') that will set a page variable (''[={$HideDiscussion}=]'') which can be used by ''[=(:if enabled HideDiscussion:)=]'' in [=.PageActions=]?%0aA: Add the following section of code to your config.php%0a-> [@%0aSDV($HideDiscussion, 0); 	#define var name%0aMarkup('hideDiscussion', '%3c{$var}',%0a '/\\(:nodiscussion:\\)/e', 'setHideDiscussion(true)'); %0afunction setHideDiscussion($val) { %0a  global $HideDiscussion; %0a  $HideDiscussion = $val;%0a} %0a@]%0a%0aThis will enable the @@[=(:if enabled HideDiscussion:)=]@@ markup to be used.  If you want to print the current value of [={$HideDiscussion}=] (for testing purposes) on the page, you'll also need to add the line:  \\%0a@@[=$FmtPV['$HideDiscussion'] = '$GLOBALS["HideDiscussion"]';=]@@%0a%0a%0aQ: It appears that (.*?) does not match newlines in these functions, making the above example inoperable if the text to be wrappen in %3cem> contains new lines.%0aA: If you include the "s" modifier on the regular expression then the dot (.) will match newlines.  Thus your regular expression will be "/STUFF(.*?)/s".  That s at the very end is what you are looking for.  If you start getting into multi-line regexes you may be forced to look at the m option as well - let's anchors (^ and $) match not begin/end of strings but also begin/end of lines (i.e., right before/after a newline).%0a %0aQ: How can the text returned by my markup function be re-processed by the markup engine?%0aA: If the result of your markup contains more markup that should be processed, you have two options. First is to select a "when" argument that is processed earlier than the markup in your result. For example, if your markup may return [=[[links]]=], your "when" argument could be @@"%3clinks"@@ and your markup will be processed before the links markup. The second option is to call the PRR() function in your markup definition or inside your markup function. In this case, after your markup is processed, PmWiki will restart all markups from the beginning. %0a%0aQ: How do I get started writing recipes and creating my own custom markup?%0aA: [[PmWiki:CustomMarkupAlt|(alternate) Introduction to custom markup for Beginners]]%0a%0aQ: How do I make a rule that runs once at the end of all other rule processing?%0aA: Use this statement instead of the usual @@Markup()@@ call:%0a-->@@$MarkupFrameBase['posteval']['myfooter'] = "\$out = onetimerule(\$out);";@@%0a
time=1414942229
