summaryrefslogtreecommitdiff
path: root/platform/www/lib/plugins/bureaucracy/helper/field.php
diff options
context:
space:
mode:
authorYaco <franco@reevo.org>2022-03-12 03:48:42 +0000
committerYaco <franco@reevo.org>2022-03-12 03:48:42 +0000
commit5a2b689265654f704d06eb2ea9ee1b21078edcfc (patch)
treeb52e9ce5c8236d2ecf660950238c2dd3d42ad5d2 /platform/www/lib/plugins/bureaucracy/helper/field.php
parent46377a425154286e3072880d55667a18b4518df1 (diff)
add plugins: wrap, bureaucracy, phpwikify
Diffstat (limited to 'platform/www/lib/plugins/bureaucracy/helper/field.php')
-rw-r--r--platform/www/lib/plugins/bureaucracy/helper/field.php508
1 files changed, 508 insertions, 0 deletions
diff --git a/platform/www/lib/plugins/bureaucracy/helper/field.php b/platform/www/lib/plugins/bureaucracy/helper/field.php
new file mode 100644
index 0000000..070e331
--- /dev/null
+++ b/platform/www/lib/plugins/bureaucracy/helper/field.php
@@ -0,0 +1,508 @@
+<?php
+
+/**
+ * Base class for form fields
+ *
+ * This class provides basic functionality for many form fields. It supports
+ * labels, basic validation and template-based XHTML output.
+ *
+ * @author Adrian Lang <lang@cosmocode.de>
+ **/
+
+/**
+ * Class helper_plugin_bureaucracy_field
+ *
+ * base class for all the form fields
+ */
+class helper_plugin_bureaucracy_field extends syntax_plugin_bureaucracy {
+
+ protected $mandatory_args = 2;
+ public $opt = array();
+ /** @var string|array */
+ protected $tpl;
+ protected $checks = array();
+ public $hidden = false;
+ protected $error = false;
+ protected $checktypes = array(
+ '/' => 'match',
+ '<' => 'max',
+ '>' => 'min'
+ );
+
+ /**
+ * Construct a helper_plugin_bureaucracy_field object
+ *
+ * This constructor initializes a helper_plugin_bureaucracy_field object
+ * based on a given definition.
+ *
+ * The first two items represent:
+ * * the type of the field
+ * * and the label the field has been given.
+ * Additional arguments are type-specific mandatory extra arguments and optional arguments.
+ *
+ * The optional arguments may add constraints to the field value, provide a
+ * default value, mark the field as optional or define that the field is
+ * part of a pagename (when using the template action).
+ *
+ * Since the field objects are cached, this constructor may not reference
+ * request data.
+ *
+ * @param array $args The tokenized definition, only split at spaces
+ */
+ public function initialize($args) {
+ $this->init($args);
+ $this->standardArgs($args);
+ }
+
+ /**
+ * Return false to prevent DokuWiki reusing instances of the plugin
+ *
+ * @return bool
+ */
+ public function isSingleton() {
+ return false;
+ }
+
+ /**
+ * Checks number of arguments and store 'cmd', 'label' and 'display' values
+ *
+ * @param array $args array with the definition
+ */
+ protected function init(&$args) {
+ if(count($args) < $this->mandatory_args){
+ msg(sprintf($this->getLang('e_missingargs'), hsc($args[0]),
+ hsc($args[1])), -1);
+ return;
+ }
+
+ // get standard arguments
+ $this->opt = array();
+ foreach (array('cmd', 'label') as $key) {
+ if (count($args) === 0) break;
+ $this->opt[$key] = array_shift($args);
+ }
+ $this->opt['display'] = $this->opt['label']; // allow to modify display value independently
+ }
+
+ /**
+ * Check for additional arguments and store their values
+ *
+ * @param array $args array with remaining definition arguments
+ */
+ protected function standardArgs($args) {
+ // parse additional arguments
+ foreach($args as $arg){
+ if ($arg[0] == '=') {
+ $this->setVal(substr($arg,1));
+ } elseif ($arg == '!') {
+ $this->opt['optional'] = true;
+ } elseif ($arg == '^') {
+ //only one field has focus
+ if (helper_plugin_bureaucracy_field::hasFocus()) {
+ $this->opt['id'] = 'focus__this';
+ }
+ } elseif($arg == '@') {
+ $this->opt['pagename'] = true;
+ } elseif($arg == '@@') {
+ $this->opt['replyto'] = true;
+ } elseif(preg_match('/x\d/', $arg)) {
+ $this->opt['rows'] = substr($arg,1);
+ } elseif($arg[0] == '.') {
+ $this->opt['class'] = substr($arg, 1);
+ } elseif(preg_match('/^0{2,}$/', $arg)) {
+ $this->opt['leadingzeros'] = strlen($arg);
+ } elseif($arg[0].$arg[1] == '**') {
+ $this->opt['matchexplanation'] = substr($arg,2);
+ } else {
+ $t = $arg[0];
+ $d = substr($arg,1);
+ if (in_array($t, array('>', '<')) && !is_numeric($d)) {
+ break;
+ }
+ if ($t == '/') {
+ if (substr($d, -1) !== '/') {
+ break;
+ }
+ $d = substr($d, 0, -1);
+ }
+ if (!isset($this->checktypes[$t]) || !method_exists($this, 'validate_' . $this->checktypes[$t])) {
+ msg(sprintf($this->getLang('e_unknownconstraint'), hsc($t).' ('.hsc($arg).')'), -1);
+ return;
+ }
+ $this->checks[] = array('t' => $t, 'd' => $d);
+ }
+ }
+ }
+
+ /**
+ * Add parsed element to Form which generates XHTML
+ *
+ * Outputs the represented field using the passed Doku_Form object.
+ * Additional parameters (CSS class & HTML name) are passed in $params.
+ * HTML output is created by passing the template $this->tpl to the simple
+ * template engine _parse_tpl.
+ *
+ * @param array $params Additional HTML specific parameters
+ * @param Doku_Form $form The target Doku_Form object
+ * @param int $formid unique identifier of the form which contains this field
+ */
+ public function renderfield($params, Doku_Form $form, $formid) {
+ $this->_handlePreload();
+ if(!$form->_infieldset){
+ $form->startFieldset('');
+ }
+ if ($this->error) {
+ $params['class'] = 'bureaucracy_error';
+ }
+
+ $params = array_merge($this->opt, $params);
+ $form->addElement($this->_parse_tpl($this->tpl, $params));
+ }
+
+ /**
+ * Only the first use get the focus, next calls not
+ *
+ * @return bool
+ */
+ protected static function hasFocus(){
+ static $focus = true;
+ if($focus) {
+ $focus = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+
+ /**
+ * Check for preload value in the request url
+ */
+ protected function _handlePreload() {
+ $preload_name = '@' . strtr($this->getParam('label'),' .','__') . '@';
+ if (isset($_GET[$preload_name])) {
+ $this->setVal($_GET[$preload_name]);
+ }
+ }
+
+ /**
+ * Handle a post to the field
+ *
+ * Accepts and validates a posted value.
+ *
+ * (Overridden by fieldset, which has as argument an array with the form array by reference)
+ *
+ * @param string $value The passed value or array or null if none given
+ * @param helper_plugin_bureaucracy_field[] $fields (reference) form fields (POST handled upto $this field)
+ * @param int $index index number of field in form
+ * @param int $formid unique identifier of the form which contains this field
+ * @return bool Whether the passed value is valid
+ */
+ public function handle_post($value, &$fields, $index, $formid) {
+ return $this->hidden || $this->setVal($value);
+ }
+
+ /**
+ * Get the field type
+ *
+ * @return string
+ **/
+ public function getFieldType() {
+ return $this->opt['cmd'];
+ }
+
+ /**
+ * Get the replacement pattern used by action
+ *
+ * @return string
+ */
+ public function getReplacementPattern() {
+ $label = $this->getParam('label');
+ $value = $this->getParam('value');
+
+ if (is_array($value)) {
+ return '/(@@|##)' . preg_quote($label, '/') .
+ '(?:\((?P<delimiter>.*?)\))?' .//delimiter
+ '(?:\|(?P<default>.*?))' . (count($value) == 0 ? '' : '?') .
+ '\1/si';
+ }
+
+ return '/(@@|##)' . preg_quote($label, '/') .
+ '(?:\|(.*?))' . (is_null($value) ? '' : '?') .
+ '\1/si';
+ }
+
+ /**
+ * Used as an callback for preg_replace_callback
+ *
+ * @param $matches
+ * @return string
+ */
+ public function replacementMultiValueCallback($matches) {
+ $value = $this->opt['value'];
+
+ //default value
+ if (is_null($value) || $value === false) {
+ if (isset($matches['default']) && $matches['default'] != '') {
+ return $matches['default'];
+ }
+ return $matches[0];
+ }
+
+ //check if matched string containts a pair of brackets
+ $delimiter = preg_match('/\(.*\)/s', $matches[0]) ? $matches['delimiter'] : ', ';
+
+ return implode($delimiter, $value);
+ }
+
+ /**
+ * Get the value used by action
+ * If value is a callback preg_replace_callback is called instead preg_replace
+ *
+ * @return mixed|string
+ */
+ public function getReplacementValue() {
+ $value = $this->getParam('value');
+
+ if (is_array($value)) {
+ return array($this, 'replacementMultiValueCallback');
+ }
+
+ return is_null($value) || $value === false ? '$2' : $value;
+ }
+
+ /**
+ * Validate value and stores it
+ *
+ * @param mixed $value value entered into field
+ * @return bool whether the passed value is valid
+ */
+ protected function setVal($value) {
+ if ($value === '') {
+ $value = null;
+ }
+ $this->opt['value'] = $value;
+ try {
+ $this->_validate();
+ $this->error = false;
+ } catch (Exception $e) {
+ msg($e->getMessage(), -1);
+ $this->error = true;
+ }
+ return !$this->error;
+ }
+
+ /**
+ * Whether the field is true (used for depending fieldsets)
+ *
+ * @return bool whether field is set
+ */
+ public function isSet_() {
+ return !is_null($this->getParam('value'));
+ }
+
+ /**
+ * Validate value of field and throws exceptions for bad values.
+ *
+ * @throws Exception when field didn't validate.
+ */
+ protected function _validate() {
+ $value = $this->getParam('value');
+ if (is_null($value)) {
+ if(!isset($this->opt['optional'])) {
+ throw new Exception(sprintf($this->getLang('e_required'),hsc($this->opt['label'])));
+ }
+ return;
+ }
+
+ foreach ($this->checks as $check) {
+ $checktype = $this->checktypes[$check['t']];
+ if (!call_user_func(array($this, 'validate_' . $checktype), $check['d'], $value)) {
+ //replacement is custom explanation or just the regexp or the requested value
+ if(isset($this->opt['matchexplanation'])) {
+ $replacement = hsc($this->opt['matchexplanation']);
+ } elseif($checktype == 'match') {
+ $replacement = sprintf($this->getLang('checkagainst'), hsc($check['d']));
+ } else {
+ $replacement = hsc($check['d']);
+ }
+
+ throw new Exception(sprintf($this->getLang('e_' . $checktype), hsc($this->opt['label']), $replacement));
+ }
+ }
+ }
+
+ /**
+ * Get an arbitrary parameter
+ *
+ * @param string $name
+ * @return mixed|null
+ */
+ public function getParam($name) {
+ if (!isset($this->opt[$name]) || $name === 'value' && $this->hidden) {
+ return null;
+ }
+ if ($name === 'pagename') {
+ // If $this->opt['pagename'] is set, return the escaped value of the field.
+ $value = $this->getParam('value');
+ if (is_null($value)) {
+ return null;
+ }
+ global $conf;
+ if($conf['useslash']) $value = str_replace('/',' ',$value);
+ return str_replace(':',' ',$value);
+ }
+ return $this->opt[$name];
+ }
+
+ /**
+ * Parse a template with given parameters
+ *
+ * Replaces variables specified like @@VARNAME|default@@ using the passed
+ * value map.
+ *
+ * @param string|array $tpl The template as string or array
+ * @param array $params A hash mapping parameters to values
+ *
+ * @return string|array The parsed template
+ */
+ protected function _parse_tpl($tpl, $params) {
+ // addElement supports a special array format as well. In this case
+ // not all elements should be escaped.
+ $is_simple = !is_array($tpl);
+ if ($is_simple) $tpl = array($tpl);
+
+ foreach ($tpl as &$val) {
+ // Select box passes options as an array. We do not escape those.
+ if (is_array($val)) continue;
+
+ // find all variables and their defaults or param values
+ preg_match_all('/@@([A-Z]+)(?:\|((?:[^@]|@$|@[^@])*))?@@/', $val, $pregs);
+ for ($i = 0 ; $i < count($pregs[2]) ; ++$i) {
+ if (isset($params[strtolower($pregs[1][$i])])) {
+ $pregs[2][$i] = $params[strtolower($pregs[1][$i])];
+ }
+ }
+ // we now have placeholders in $pregs[0] and their values in $pregs[2]
+ $replacements = array(); // check if empty to prevent php 5.3 warning
+ if (!empty($pregs[0])) {
+ $replacements = array_combine($pregs[0], $pregs[2]);
+ }
+
+ if($is_simple){
+ // for simple string templates, we escape all replacements
+ $replacements = array_map('hsc', $replacements);
+ }else{
+ // for the array ones, we escape the label and display only
+ if(isset($replacements['@@LABEL@@'])) $replacements['@@LABEL@@'] = hsc($replacements['@@LABEL@@']);
+ if(isset($replacements['@@DISPLAY@@'])) $replacements['@@DISPLAY@@'] = hsc($replacements['@@DISPLAY@@']);
+ }
+
+ // we attach a mandatory marker to the display
+ if(isset($replacements['@@DISPLAY@@']) && !isset($params['optional'])){
+ $replacements['@@DISPLAY@@'] .= ' <sup>*</sup>';
+ }
+ $val = str_replace(array_keys($replacements), array_values($replacements), $val);
+ }
+ return $is_simple ? $tpl[0] : $tpl;
+ }
+
+ /**
+ * Executed after performing the action hooks
+ */
+ public function after_action() {
+ }
+
+ /**
+ * Constraint function: value of field should match this regexp
+ *
+ * @param string $d regexp
+ * @param mixed $value
+ * @return int|bool
+ */
+ protected function validate_match($d, $value) {
+ return @preg_match('/' . $d . '/i', $value);
+ }
+
+ /**
+ * Constraint function: value of field should be bigger
+ *
+ * @param int|number $d lower bound
+ * @param mixed $value of field
+ * @return bool
+ */
+ protected function validate_min($d, $value) {
+ return $value > $d;
+ }
+
+ /**
+ * Constraint function: value of field should be smaller
+ *
+ * @param int|number $d upper bound
+ * @param mixed $value of field
+ * @return bool
+ */
+ protected function validate_max($d, $value) {
+ return $value < $d;
+ }
+
+ /**
+ * Available methods
+ *
+ * @return array
+ */
+ public function getMethods() {
+ $result = array();
+ $result[] = array(
+ 'name' => 'initialize',
+ 'desc' => 'Initiate object, first parameters are at least cmd and label',
+ 'params' => array(
+ 'params' => 'array'
+ )
+ );
+ $result[] = array(
+ 'name' => 'renderfield',
+ 'desc' => 'Add parsed element to Form which generates XHTML',
+ 'params' => array(
+ 'params' => 'array',
+ 'form' => 'Doku_Form',
+ 'formid' => 'integer'
+ )
+ );
+ $result[] = array(
+ 'name' => 'handle_post',
+ 'desc' => 'Handle a post to the field',
+ 'params' => array(
+ 'value' => 'array',
+ 'fields' => 'helper_plugin_bureaucracy_field[]',
+ 'index' => 'Doku_Form',
+ 'formid' => 'integer'
+ ),
+ 'return' => array('isvalid' => 'bool')
+ );
+ $result[] = array(
+ 'name' => 'getFieldType',
+ 'desc' => 'Get the field type',
+ 'return' => array('fieldtype' => 'string')
+ );
+ $result[] = array(
+ 'name' => 'isSet_',
+ 'desc' => 'Whether the field is true (used for depending fieldsets) ',
+ 'return' => array('isset' => 'bool')
+ );
+ $result[] = array(
+ 'name' => 'getParam',
+ 'desc' => 'Get an arbitrary parameter',
+ 'params' => array(
+ 'name' => 'string'
+ ),
+ 'return' => array('Parameter value' => 'mixed|null')
+ );
+ $result[] = array(
+ 'name' => 'after_action',
+ 'desc' => 'Executed after performing the action hooks'
+ );
+ return $result;
+ }
+
+}