<?php if (!defined('PmWiki')) exit(); /* PmWiki module for generating forms. See http://www.pmwiki.org/wiki/Cookbook/Input for documentation. Copyright (C) 2005 Joachim Durchholz. This code is licensed under the GNU General Public License version 2.0 as distributed with PmWiki. */ define('INPUT_VERSION', '0.94'); Markup( 'input', '>directives', '/(?:^\\(|(\\()):input(.*?):\\)/ei', 'InputMakeHtml(PSS(\'$1\'), PSS(\'$2\'))' ); function InputWarning($msg) { return '<i><b>[PmWiki Warning: ' . htmlentities($msg, ENT_QUOTES) . ']</b></i>'; } # Helper functions for constructing entries in $InputFmt. function InputOptionBoolean($option) { return " $option=\"$option\""; } function InputOptionString($option) { return '/^.*$/e/\' ' . $option . '="\' . htmlentities(\'$0\') . \'"\''; } SDVA($InputMethods, array( 'get' => ' method="get" enctype="application/x-www-form-urlencoded"', 'post/urlencoded' => ' method="post" enctype="application/x-www-form-urlencoded"', 'post/binary' => ' method="post" enctype="multipart/form-data"', 'post/text' => ' method="post" enctype="text/plain"' )); SDV($InputMethods['post'], $InputMethods['post/binary']); SDVA($InputFmt, array( # All (:input...:) markup is of the form: # (:input <verb> <required> <optional>:) # For each <verb>, this array has the following entries: # <verb>.fmt # The HTML to emit, with $xxx placeholders # (xxx is the name of the placeheld position). # <verb>.required # A space-separated list of parameter names. # These will be filled from the required (positional) # parameters of the markup. # <verb>.<option>.default # What to generate for $<option> if the markup doesn't have # <option>=value. # <verb>.<option>.present # What to generate if just <option> (without =<value>) # is supplied by the markup. # <verb>.<option>.match # A pattern-plus-replacement string that's used to generate # the text for $<option> if the markup supplies an # <option>=<value> pair. # The string is expected to have the form # /<pattern>/<modifiers>/<replacement> # where # <pattern> is a PCRE pattern that's supposed to # match the markup-supplied <value>, # <modifiers> are PCRE modifiers, # <replacement> is a replacement string that will # be substituted into the HTML output. # /<pattern>/<modifiers> and <replacement> are sumitted to PHP's # preg_replace function (see http://www.php.net/preg_replace). # You can use an alternate delimiter instead of /, but Perl-style # (), {}, [], or <> delimiters won't work. All other features # of preg_replace (in particular subpattern matching, # backreferences, and the /e modifier) work as described. 'start.fmt' => '<form$script$method>', 'start.required' => 'script', 'start.script.match' => '/^.*$/e/\' action="\' . PUE(\'$0\') . \'"\'', 'start.method.default' => $InputMethods['post'], 'start.method.match' => '/^.*$/ei/$InputMethods[\'$0\']', 'end.fmt' => '</form>', 'button.fmt' => '<input type="submit"$name$label$disabled/>', 'button.required' => 'name label', 'button.name.match' => InputOptionString('name'), 'button.label.match' => InputOptionString('value'), 'button.disabled.present' => InputOptionBoolean ('disabled'), 'hidden.fmt' => '<input type="hidden"$name$value/>', 'hidden.required' => 'name value', 'hidden.name.match' => InputOptionString('name'), 'hidden.value.match' => InputOptionString('value'), 'line.fmt' => '<input$password$name$columns$disabled$maxcolumns$readonly$text/>', 'line.required' => 'name', 'line.name.match' => InputOptionString('name'), 'line.password.default' => ' type="text"', 'line.password.present' => ' type="password"', 'line.columns.match' => InputOptionString ('size'), 'line.disabled.present' => ' disabled="disabled"', 'line.maxcolumns.match' => InputOptionString('maxlength'), 'line.readonly.present' => InputOptionBoolean('readonly'), 'line.text.match' => InputOptionString('value'), 'text.fmt' => '<textarea$name$columns$disabled$readonly$rows>$text</textarea>', 'text.required' => 'name', 'text.name.match' => InputOptionString('name'), 'text.columns.match' => InputOptionString('cols'), 'text.columns.default' => ' cols="65"', 'text.disabled.present' => InputOptionBoolean('disabled'), 'text.readonly.present' => InputOptionBoolean('readonly'), 'text.rows.match' => InputOptionString('rows'), 'text.rows.default' => ' rows="5"', 'text.text.match' => '/^.*$/e/htmlentities(PSS(\'$0\'))', 'checkbox.fmt' => '<input type="checkbox"$name$disabled$checked$value/>', 'checkbox.required' => 'name', 'checkbox.name.match' => InputOptionString('name'), 'checkbox.disabled.present' => InputOptionBoolean('disabled'), 'checkbox.checked.present' => InputOptionBoolean('checked'), 'checkbox.value.match' => InputOptionString('value'), 'radiobutton.fmt' => '<input type="radio"$name$disabled$checked$value/>', 'radiobutton.required' => 'name', 'radiobutton.name.match' => InputOptionString('name'), 'radiobutton.disabled.present' => InputOptionBoolean('disabled'), 'radiobutton.checked.present' => InputOptionBoolean('checked'), 'radiobutton.value.match' => InputOptionString('value'), 'menu.fmt' => '<select$name$multiple$disabled$lines>', 'menu.required' => 'name', 'menu.name.match' => '/^.*$/e/\' name="\' . htmlentities(\'$0\')', 'menu.multiple.present' => '[]"' . InputOptionBoolean('multiple'), 'menu.multiple.default' => '"', 'menu.disabled.present' => InputOptionBoolean('disabled'), 'menu.lines.match' => InputOptionString('size'), 'menu.lines.default' => ' size="1"', 'menuentry.fmt' => '<option$checked$disabled$label>$value</option>', 'menuentry.required' => 'value', 'menuentry.value.match' => '/^.*$/e/htmlentities(PSS(\'$0\'))', 'menuentry.checked.present' => InputOptionBoolean('selected'), 'menuentry.disabled.present' => InputOptionBoolean('disabled'), 'menugroup.fmt' => '<optgroup$label$disabled>', 'menugroup.required' => 'label', 'menugroup.label.match' => InputOptionString('label'), 'menugroup.disabled.present' => InputOptionBoolean('disabled'), 'menugroupend.fmt' => '</optgroup>', 'menuend.fmt' => '</select>' )); function InputFindDefault($verb, $option) { global $InputFmt; return IsEnabled($InputFmt["$verb.$option.default"], ''); } function InputMakeControl($verb, $args) { global $InputFmt, $InputMethods; $fmt = $InputFmt["$verb.fmt"]; if (!isset($fmt)) { return InputWarning ("'$verb' unknown in (:input $verb ...:)."); } $required_str = $InputFmt["$verb.required"]; if (isset($required_str)) { $required = explode(' ', $required_str); } else { $required = array(); } $args = array_slice($args ['#'], 2); while (count($args) > 0) { $param = array_shift($args); $arg = array_shift($args); $positional_name = array_shift($required); if (isset($positional_name)) { if (strlen($param) > 0 && $param != $positional_name) { return InputWarning("(:input $verb:) needs positional parameters '$required_str', but those starting from '$positional_name' are missing."); } else { $param = $positional_name; } } else { if (strlen($param) == 0) { $param = $arg; $arg = 1; } } $text = $InputFmt["$verb.$param.present"]; if (!isset($text)) { $text = $InputFmt["$verb.$param.match"]; if (isset($text)) { preg_match('/^((.).*?\\2.*?)\\2(.*)$/', $text, $matches); $pat = $matches['1']; $rep = $matches['3']; if (preg_match($pat, $arg) > 0) { $text = preg_replace($pat, $rep, $arg, -1); } else { return InputWarning("Unrecognised value '$arg' for option '$param' in (:input $verb...:) markup."); } } else { return InputWarning("Unknown option '$param' in (:input $verb...:) markup."); } } $fmt = str_replace("\$$param", $text, $fmt); } # $args is processed, now fill in defaults $fmt = preg_replace( '/\\$([a-z]+)/ei', 'InputFindDefault(\'' . $verb . '\', \'$1\')', $fmt); if (count($required) > 0) { return InputWarning("(:input $verb:) needs positional parameters '$required_str', but those starting from '$required[0]' are missing."); } return $fmt; } $InputActive = 0; function InputMakeHtml($flag, $argstring) { global $InputActive; $o = array(); $at_line_start = $flag == ''; $args = ParseArgs($argstring); $verb = $args[''][0]; if ($verb == 'start' || $verb == 'end') { if ($at_line_start) { $o[] = '<:block>'; } else { return Keep(InputWarning ("(:input $verb...:) must be at the start of a line.")); } } if (!isset($verb)) { $o[] = Keep(InputWarning('(:input:) needs at least one parameter.')); } else { if ($verb == 'start') { if ($InputActive) { $o[] = '</form>'; } else { $InputActive = 1; } } else { if (!$InputActive) { $o[] = Keep(InputWarning('There should have been an (:input start...:) before this (:input...:) markup.')); } if ($verb == 'end') { $InputActive = 0; } } $o[] = Keep(InputMakeControl($verb, $args)); } return implode("\n", $o); }