はじまりの大地
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user