18638ead5SAndreas Gohr<?php 28638ead5SAndreas Gohrnamespace dokuwiki\Form; 38638ead5SAndreas Gohr 48638ead5SAndreas Gohr/** 58638ead5SAndreas Gohr * Class DropdownElement 68638ead5SAndreas Gohr * 78638ead5SAndreas Gohr * Represents a HTML select. Please note that this does not support multiple selected options! 88638ead5SAndreas Gohr * 98638ead5SAndreas Gohr * @package dokuwiki\Form 108638ead5SAndreas Gohr */ 118638ead5SAndreas Gohrclass DropdownElement extends InputElement { 128638ead5SAndreas Gohr 13238a072bSMichael Grosse /** @var OptGroup */ 14238a072bSMichael Grosse protected $options = null; 15238a072bSMichael Grosse 16238a072bSMichael Grosse /** @var array OptGroup[] */ 17238a072bSMichael Grosse protected $optGroups = array(); 188638ead5SAndreas Gohr 198638ead5SAndreas Gohr protected $value = ''; 208638ead5SAndreas Gohr 218638ead5SAndreas Gohr /** 228638ead5SAndreas Gohr * @param string $name The name of this form element 238638ead5SAndreas Gohr * @param string $options The available options 248638ead5SAndreas Gohr * @param string $label The label text for this element (will be autoescaped) 258638ead5SAndreas Gohr */ 268638ead5SAndreas Gohr public function __construct($name, $options, $label = '') { 278638ead5SAndreas Gohr parent::__construct('dropdown', $name, $label); 28693978b1SMichael Grosse $this->rmattr('type'); 29238a072bSMichael Grosse $this->options = new OptGroup(null, $options); 30238a072bSMichael Grosse $this->val(''); 31238a072bSMichael Grosse } 32238a072bSMichael Grosse 33238a072bSMichael Grosse /** 34238a072bSMichael Grosse * @param $label 35238a072bSMichael Grosse * @param $options 36*068abb2bSMichael Grosse * @return OptGroup 37238a072bSMichael Grosse * @throws \Exception 38238a072bSMichael Grosse */ 39238a072bSMichael Grosse public function addOptGroup($label, $options) { 40238a072bSMichael Grosse if (empty($label)) { 41238a072bSMichael Grosse throw new \InvalidArgumentException(hsc('<optgroup> must have a label!')); 42238a072bSMichael Grosse } 43238a072bSMichael Grosse $this->optGroups[] = new OptGroup($label, $options); 44238a072bSMichael Grosse return end($this->optGroups); 45238a072bSMichael Grosse } 46238a072bSMichael Grosse 47238a072bSMichael Grosse /** 48238a072bSMichael Grosse * Set or get the optgroups of an Dropdown-Element. 49238a072bSMichael Grosse * 50238a072bSMichael Grosse * optgroups have to be given as associative array 51238a072bSMichael Grosse * * the key being the label of the group 52238a072bSMichael Grosse * * the value being an array of options as defined in @see OptGroup::options() 53238a072bSMichael Grosse * 54238a072bSMichael Grosse * @param null|array $optGroups 55238a072bSMichael Grosse * @return array 56238a072bSMichael Grosse */ 57238a072bSMichael Grosse public function optGroups($optGroups = null) { 58238a072bSMichael Grosse if($optGroups === null) { 59238a072bSMichael Grosse return $this->optGroups; 60238a072bSMichael Grosse } 61238a072bSMichael Grosse if (!is_array($optGroups)) { 62238a072bSMichael Grosse throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!')); 63238a072bSMichael Grosse } 64238a072bSMichael Grosse $this->optGroups = array(); 65238a072bSMichael Grosse foreach ($optGroups as $label => $options) { 66238a072bSMichael Grosse $this->addOptGroup($label, $options); 67238a072bSMichael Grosse } 688638ead5SAndreas Gohr } 698638ead5SAndreas Gohr 708638ead5SAndreas Gohr /** 718638ead5SAndreas Gohr * Get or set the options of the Dropdown 728638ead5SAndreas Gohr * 738638ead5SAndreas Gohr * Options can be given as associative array (value => label) or as an 74795955b2SMichael Grosse * indexd array (label = value) or as an array of arrays. In the latter 75795955b2SMichael Grosse * case an element has to look as follows: 76795955b2SMichael Grosse * option-value => array ( 77795955b2SMichael Grosse * 'label' => option-label, 78795955b2SMichael Grosse * 'attrs' => array ( 79795955b2SMichael Grosse * attr-key => attr-value, ... 80795955b2SMichael Grosse * ) 81795955b2SMichael Grosse * ) 828638ead5SAndreas Gohr * 838638ead5SAndreas Gohr * @param null|array $options 848638ead5SAndreas Gohr * @return $this|array 858638ead5SAndreas Gohr */ 868638ead5SAndreas Gohr public function options($options = null) { 87238a072bSMichael Grosse if ($options === null) { 88238a072bSMichael Grosse return $this->options->options(); 898638ead5SAndreas Gohr } 90238a072bSMichael Grosse $this->options = new OptGroup(null, $options); 918638ead5SAndreas Gohr return $this; 928638ead5SAndreas Gohr } 938638ead5SAndreas Gohr 948638ead5SAndreas Gohr /** 958638ead5SAndreas Gohr * Gets or sets an attribute 968638ead5SAndreas Gohr * 978638ead5SAndreas Gohr * When no $value is given, the current content of the attribute is returned. 988638ead5SAndreas Gohr * An empty string is returned for unset attributes. 998638ead5SAndreas Gohr * 1008638ead5SAndreas Gohr * When a $value is given, the content is set to that value and the Element 1018638ead5SAndreas Gohr * itself is returned for easy chaining 1028638ead5SAndreas Gohr * 1038638ead5SAndreas Gohr * @param string $name Name of the attribute to access 1048638ead5SAndreas Gohr * @param null|string $value New value to set 1058638ead5SAndreas Gohr * @return string|$this 1068638ead5SAndreas Gohr */ 1078638ead5SAndreas Gohr public function attr($name, $value = null) { 1088638ead5SAndreas Gohr if(strtolower($name) == 'multiple') { 1098638ead5SAndreas Gohr throw new \InvalidArgumentException('Sorry, the dropdown element does not support the "multiple" attribute'); 1108638ead5SAndreas Gohr } 1118638ead5SAndreas Gohr return parent::attr($name, $value); 1128638ead5SAndreas Gohr } 1138638ead5SAndreas Gohr 1148638ead5SAndreas Gohr /** 1158638ead5SAndreas Gohr * Get or set the current value 1168638ead5SAndreas Gohr * 1178638ead5SAndreas Gohr * When setting a value that is not defined in the options, the value is ignored 1188638ead5SAndreas Gohr * and the first option's value is selected instead 1198638ead5SAndreas Gohr * 1208638ead5SAndreas Gohr * @param null|string $value The value to set 1218638ead5SAndreas Gohr * @return $this|string 1228638ead5SAndreas Gohr */ 1238638ead5SAndreas Gohr public function val($value = null) { 1248638ead5SAndreas Gohr if($value === null) return $this->value; 1258638ead5SAndreas Gohr 126238a072bSMichael Grosse $value_exists = $this->setValueInOptGroups($value); 127238a072bSMichael Grosse 128238a072bSMichael Grosse if($value_exists) { 1298638ead5SAndreas Gohr $this->value = $value; 1308638ead5SAndreas Gohr } else { 1318638ead5SAndreas Gohr // unknown value set, select first option instead 132238a072bSMichael Grosse $this->value = $this->getFirstOption(); 133238a072bSMichael Grosse $this->setValueInOptGroups($this->value); 1348638ead5SAndreas Gohr } 1358638ead5SAndreas Gohr 1368638ead5SAndreas Gohr return $this; 1378638ead5SAndreas Gohr } 1388638ead5SAndreas Gohr 1398638ead5SAndreas Gohr /** 140238a072bSMichael Grosse * Returns the first options as it will be rendered in HTML 141238a072bSMichael Grosse * 142238a072bSMichael Grosse * @return string 143238a072bSMichael Grosse */ 144238a072bSMichael Grosse protected function getFirstOption() { 145238a072bSMichael Grosse $options = $this->options(); 146238a072bSMichael Grosse if (!empty($options)) { 147238a072bSMichael Grosse return (string) array_shift(array_keys($options)); 148238a072bSMichael Grosse } 149238a072bSMichael Grosse foreach ($this->optGroups as $optGroup) { 150238a072bSMichael Grosse $options = $optGroup->options(); 151238a072bSMichael Grosse if (!empty($options)) { 152238a072bSMichael Grosse return (string) array_shift(array_keys($options)); 153238a072bSMichael Grosse } 154238a072bSMichael Grosse } 155238a072bSMichael Grosse } 156238a072bSMichael Grosse 157238a072bSMichael Grosse /** 158238a072bSMichael Grosse * Set the value in the OptGroups, including the optgroup for the options without optgroup. 159238a072bSMichael Grosse * 160238a072bSMichael Grosse * @param string $value 161238a072bSMichael Grosse * @return bool 162238a072bSMichael Grosse */ 163238a072bSMichael Grosse protected function setValueInOptGroups($value) { 164*068abb2bSMichael Grosse $value_exists = false; 165*068abb2bSMichael Grosse $isMultiSelect = $this->attributes['multiple']; 166*068abb2bSMichael Grosse /** @var OptGroup $optGroup */ 167*068abb2bSMichael Grosse foreach (array_merge(array($this->options), $this->optGroups) as $optGroup) { 168238a072bSMichael Grosse $value_exists = $optGroup->setValue($value) || $value_exists; 169*068abb2bSMichael Grosse if ($value_exists && !$isMultiSelect) { 170*068abb2bSMichael Grosse $value = null; 171*068abb2bSMichael Grosse } 172238a072bSMichael Grosse } 173238a072bSMichael Grosse return $value_exists; 174238a072bSMichael Grosse } 175238a072bSMichael Grosse 176238a072bSMichael Grosse /** 1778638ead5SAndreas Gohr * Create the HTML for the select it self 1788638ead5SAndreas Gohr * 1798638ead5SAndreas Gohr * @return string 1808638ead5SAndreas Gohr */ 1818638ead5SAndreas Gohr protected function mainElementHTML() { 1828638ead5SAndreas Gohr if($this->useInput) $this->prefillInput(); 1838638ead5SAndreas Gohr 1848638ead5SAndreas Gohr $html = '<select ' . buildAttributes($this->attrs()) . '>'; 185238a072bSMichael Grosse $html .= $this->options->toHTML(); 186238a072bSMichael Grosse $html = array_reduce($this->optGroups, function($html, OptGroup $optGroup) {return $html . $optGroup->toHTML();}, $html); 1878638ead5SAndreas Gohr $html .= '</select>'; 1888638ead5SAndreas Gohr 1898638ead5SAndreas Gohr return $html; 1908638ead5SAndreas Gohr } 1918638ead5SAndreas Gohr 1928638ead5SAndreas Gohr} 193