<?php declare(strict_types=1); namespace MessagesReplacement; # add suffix 'new' for development version if (!defined('PmWiki')) exit(); const MESSAGESNAME = 'MessagesReplacement'; $MessagesReplacementNew = ''; # empty string for production version, 'new' for development version /* Messages Replacement See https://www.pmwiki.org/wiki/Cookbook/Messages and https://kiwiwiki.nz/pmwiki/pmwiki.php/Cookbook/Messages Based on code from https://www.pmwiki.org/wiki/PITS/01368 + Copyright 2022-present Petko Yotov, Simon Davis This software is written for PmWiki; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. See pmwiki.php for full details and lack of warranty. The recipe implements a drop in PmWiki directive to replace the standard (:messages:). The global variable $MessagesFmt is written to by PmWiki recipes and extensions with debug and similar information. By convention the array has two dimensions * the first identifying the source of the message * the second the debug message itself. Differences are: * comma or space separated parameters, with wildcards, can be supplied to select messages displayed * only messages for specified parameters are displayed * parameters are case insensitive * directive does not have to start at the beginning of a line * placed within a HTML section of class=messages to allow formatting * groups of messages are separated by a blank line + Revision History <reverse chronological order please> # 2024-05-05 add key= parameter to maintain compatibility with built in directive # 2024-04-27 update for PHP 8 warnings, add details=show||hide parameter # 2023-12-31 use mb_strtolower, refactor debug message out # 2022-02-02 Separate groups of messages, enable headings, additional simple displays # 2022-01-22 Initial version */ // Initialise constants const NL = "\n"; const BR = '<br/>' . NL; # $localDmsg = false; if (!function_exists('dmsg')) { # if (!isset ($MessagesFmt[__NAMESPACE__])) $MessagesFmt[__NAMESPACE__] = []; # initialise $localDmsg = true; function dmsg (string $smsgprefix, $smsgdata) { # local instance global $MessagesFmt; $MessagesFmt[__NAMESPACE__] [] = '<span class="dmsgprefix">' . $smsgprefix . '</span>= ' . (is_array ($smsgdata) ? @implode (BR . '<span class="dmsgprefix">' . $smsgprefix . '</span>= ', \PHSC ($smsgdata)) : \PHSC ($smsgdata)); } # end function } # end if # insert into HTML <head> metadata element \SDV($HTMLStylesFmt[__NAMESPACE__], NL . '.dmsgprefix {display:inline-block; font-style:italic; min-width:9rem;}' . NL . '.dmsgpre {font-size:smaller;}' . NL . '.messages {font-family: monospace; font-size:small;}' . NL); # set debug flag \SDV($MessagesReplacementDebug, false); # set default debug setting if not defined in a configuration file \SDV($MessagesReplacementHeading, false); # set headings \SDV($MessagesReplacementDetails, ''); # set details display (show or hide) $Messages_debugon = boolval ($MessagesReplacementDebug); # if on writes input and output to $MessagesFmt $Messages_heading = boolval ($MessagesReplacementHeading); # if on writes headings level 3 to web page $MessagesReplacementDetails = mb_strtolower ($MessagesReplacementDetails); $Messages_detailOpts = (('show' === $MessagesReplacementDetails) || ('hide' === $MessagesReplacementDetails)) ? $MessagesReplacementDetails : ''; # Version date $RecipeInfo[MESSAGESNAME]['Version'] = '2024-05-31' . $MessagesReplacementNew; # PmWiki version numbering is done by date # recipe version page variable $FmtPV['$MessagesReplacementVersion'] = "'" . MESSAGESNAME . ' version ' . $RecipeInfo[MESSAGESNAME]['Version'] . "'"; // return version as a custom page variable # if ($Messages_debugon) { dmsg('<hr>' . __FILE__, $RecipeInfo[MESSAGESNAME]['Version']); dmsg ('Messages_detailOpts', $Messages_detailOpts); } # declare $Messages for (:if enabled MessagesReplacement:) recipe installation check $MessagesReplacement = true; # enabled # ## Add a PmWiki custom markup # (:messages optional,name,list :) $markup_pattern = '/\\(:messages(?: (.*?))?:\\)/i'; # see https://regex101.com/r/HNkxC5/2 \Markup('messages', 'directives', $markup_pattern, __NAMESPACE__ . '\MessagesReplacement_Parse' ); # i = case insensitive # uses lazy evaluation, preserves leading and trailing white space // return; # completed messages replacement recipe setup /*-----------------------------------------------------------------------------------------------------------*/ # /** Main Messages parser * /param arguments as documented above * /return The HTML-formatted information wrapped in a <article> of class "messages". */ function MessagesReplacement_Parse (array $m):string { global $Messages_debugon, $pagename; # import variables global $MessagesFmt; # import data array for reading global $Messages_heading, $Messages_detailOpts; $retVal = '<:block><section class="messages">' . NL; # start a block-level element, i.e. break out of the paragraphs. if (empty ($MessagesFmt)) { return ($Messages_debugon) ? 'No messages' . BR : ''; # no messages } if ($Messages_debugon) { $retVal .= '<details><summary>' . __NAMESPACE__ . '</summary>' . NL; $retVal .= 'm[]: ' . (is_array ($m) ? "'" . implode ("' '", $m) . "'" : '"' . $m . '"') . BR; } # end debug $detailOpts = $Messages_detailOpts; # initialise $args[''] = ['*']; # initialise, defualt is all messages $msgKeys = ''; # initialise if (isset($m[1])) { # parameters supplied $args = \ParseArgs($m[1]); # contains all text within directive if (!empty($args ['keys'])) { # some keys were specified $msgKeys .= mb_strtolower ($args['keys']); # parameters case insensitive if ($Messages_debugon) { $retVal .= "args['keys']: " . (is_array ($args['keys']) ? "'" . implode ("' '", $args['keys']) . "'" : '"' . $args['keys'] . '"') . BR; } } if (!empty($args [''])) { # some keys were specified $msgKeys .= mb_strtolower (implode (',', $args[''])); # parameters case insensitive if ($Messages_debugon) { $retVal .= "args['']: " . (is_array ($args['']) ? "'" . implode ("' '", $args['']) . "'" : '"' . $args[''] . '"') . BR; } } if (array_key_exists ('details', $args)) { $detailOpts = mb_strtolower ($args ['details']); } $debugOpt = array_key_exists ('debug', $args) ? $args['debug'] === 'true' : false; if ($debugOpt) $Messages_debugon = true; # set on } $displayDetail = ('show' === $detailOpts) || ('hide' === $detailOpts); if ($Messages_debugon) { $retVal .= 'detailOpts: ' . (is_array ($detailOpts) ? "'" . implode ("' '", $detailOpts) . "'" : '"' . $detailOpts . '"') . BR; $retVal .= 'displayDetail: "' . var_export($displayDetail, true) . '"' . BR; } # end debug if (empty ($msgKeys)) $msgKeys = '*'; # display all keys if ($Messages_debugon) { $retVal .= 'msgKeys: ' . (is_array ($msgKeys) ? "'" . implode ("' '", $msgKeys) . "'" : '"' . $msgKeys . '"') . BR; } $outArr = []; # store messages by key foreach ($MessagesFmt as $key=>$val) { # set up a case insensitive key, numeric keys are coalesced to the key '' $mky = $key; if (is_integer($key)) $mky = ''; # coalesce numeric keys if (is_string($key)) $mky = mb_strtolower ($key); # treat keys as case insensitive if (!isset($outArr[$mky])) $outArr[$mky] = []; # initialise # extract all the values for this key to the output array foreach((array)$val as $vvl) { $outArr[$mky][] = $vvl; } # end foreach } # end foreach if ($Messages_debugon) { $retVal .= 'MsgF keys: ' . implode (', ', array_keys($outArr)) . BR; } $out = ''; $foundkeys = \MatchNames (array_keys($outArr), $msgKeys); # PmWiki function if ($Messages_debugon) { $retVal .= 'foundkeys: ' . implode (', ', $foundkeys) . BR; $retVal .= '</details>' . NL; } foreach ($foundkeys as $fky) { if ($displayDetail) { $out .= '<details'; if ('show' == $detailOpts) $out .= ' open="open"'; $out .= '><summary>' . $fky . '</summary>'; } if ($Messages_heading) { $out .= '<h3>' . $fky . '</h3>'; } $out .= implode(BR, $outArr[$fky]) . BR; if ($displayDetail) { $out .= '</details>' . NL; } } # end foreach $retVal .= \FmtPageName($out, $pagename); # convert $ constants return Keep ($retVal . NL . '</section>' . NL); } # end MessagesReplacement_Parse