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 36068abb2bSMichael 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 55*2bd1d2c7SMichael Grosse * @return OptGroup[]|DropdownElement 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 } 68*2bd1d2c7SMichael Grosse return $this; 698638ead5SAndreas Gohr } 708638ead5SAndreas Gohr 718638ead5SAndreas Gohr /** 728638ead5SAndreas Gohr * Get or set the options of the Dropdown 738638ead5SAndreas Gohr * 748638ead5SAndreas Gohr * Options can be given as associative array (value => label) or as an 75795955b2SMichael Grosse * indexd array (label = value) or as an array of arrays. In the latter 76795955b2SMichael Grosse * case an element has to look as follows: 77795955b2SMichael Grosse * option-value => array ( 78795955b2SMichael Grosse * 'label' => option-label, 79795955b2SMichael Grosse * 'attrs' => array ( 80795955b2SMichael Grosse * attr-key => attr-value, ... 81795955b2SMichael Grosse * ) 82795955b2SMichael Grosse * ) 838638ead5SAndreas Gohr * 848638ead5SAndreas Gohr * @param null|array $options 858638ead5SAndreas Gohr * @return $this|array 868638ead5SAndreas Gohr */ 878638ead5SAndreas Gohr public function options($options = null) { 88238a072bSMichael Grosse if ($options === null) { 89238a072bSMichael Grosse return $this->options->options(); 908638ead5SAndreas Gohr } 91238a072bSMichael Grosse $this->options = new OptGroup(null, $options); 928638ead5SAndreas Gohr return $this; 938638ead5SAndreas Gohr } 948638ead5SAndreas Gohr 958638ead5SAndreas Gohr /** 968638ead5SAndreas Gohr * Gets or sets an attribute 978638ead5SAndreas Gohr * 988638ead5SAndreas Gohr * When no $value is given, the current content of the attribute is returned. 998638ead5SAndreas Gohr * An empty string is returned for unset attributes. 1008638ead5SAndreas Gohr * 1018638ead5SAndreas Gohr * When a $value is given, the content is set to that value and the Element 1028638ead5SAndreas Gohr * itself is returned for easy chaining 1038638ead5SAndreas Gohr * 1048638ead5SAndreas Gohr * @param string $name Name of the attribute to access 1058638ead5SAndreas Gohr * @param null|string $value New value to set 1068638ead5SAndreas Gohr * @return string|$this 1078638ead5SAndreas Gohr */ 1088638ead5SAndreas Gohr public function attr($name, $value = null) { 1098638ead5SAndreas Gohr if(strtolower($name) == 'multiple') { 1108638ead5SAndreas Gohr throw new \InvalidArgumentException('Sorry, the dropdown element does not support the "multiple" attribute'); 1118638ead5SAndreas Gohr } 1128638ead5SAndreas Gohr return parent::attr($name, $value); 1138638ead5SAndreas Gohr } 1148638ead5SAndreas Gohr 1158638ead5SAndreas Gohr /** 1168638ead5SAndreas Gohr * Get or set the current value 1178638ead5SAndreas Gohr * 1188638ead5SAndreas Gohr * When setting a value that is not defined in the options, the value is ignored 1198638ead5SAndreas Gohr * and the first option's value is selected instead 1208638ead5SAndreas Gohr * 1218638ead5SAndreas Gohr * @param null|string $value The value to set 1228638ead5SAndreas Gohr * @return $this|string 1238638ead5SAndreas Gohr */ 1248638ead5SAndreas Gohr public function val($value = null) { 1258638ead5SAndreas Gohr if($value === null) return $this->value; 1268638ead5SAndreas Gohr 127238a072bSMichael Grosse $value_exists = $this->setValueInOptGroups($value); 128238a072bSMichael Grosse 129238a072bSMichael Grosse if($value_exists) { 1308638ead5SAndreas Gohr $this->value = $value; 1318638ead5SAndreas Gohr } else { 1328638ead5SAndreas Gohr // unknown value set, select first option instead 133238a072bSMichael Grosse $this->value = $this->getFirstOption(); 134238a072bSMichael Grosse $this->setValueInOptGroups($this->value); 1358638ead5SAndreas Gohr } 1368638ead5SAndreas Gohr 1378638ead5SAndreas Gohr return $this; 1388638ead5SAndreas Gohr } 1398638ead5SAndreas Gohr 1408638ead5SAndreas Gohr /** 141238a072bSMichael Grosse * Returns the first options as it will be rendered in HTML 142238a072bSMichael Grosse * 143238a072bSMichael Grosse * @return string 144238a072bSMichael Grosse */ 145238a072bSMichael Grosse protected function getFirstOption() { 146238a072bSMichael Grosse $options = $this->options(); 147238a072bSMichael Grosse if (!empty($options)) { 148238a072bSMichael Grosse return (string) array_shift(array_keys($options)); 149238a072bSMichael Grosse } 150238a072bSMichael Grosse foreach ($this->optGroups as $optGroup) { 151238a072bSMichael Grosse $options = $optGroup->options(); 152238a072bSMichael Grosse if (!empty($options)) { 153238a072bSMichael Grosse return (string) array_shift(array_keys($options)); 154238a072bSMichael Grosse } 155238a072bSMichael Grosse } 156238a072bSMichael Grosse } 157238a072bSMichael Grosse 158238a072bSMichael Grosse /** 159238a072bSMichael Grosse * Set the value in the OptGroups, including the optgroup for the options without optgroup. 160238a072bSMichael Grosse * 161238a072bSMichael Grosse * @param string $value 162238a072bSMichael Grosse * @return bool 163238a072bSMichael Grosse */ 164238a072bSMichael Grosse protected function setValueInOptGroups($value) { 165068abb2bSMichael Grosse $value_exists = false; 166068abb2bSMichael Grosse $isMultiSelect = $this->attributes['multiple']; 167068abb2bSMichael Grosse /** @var OptGroup $optGroup */ 168068abb2bSMichael Grosse foreach (array_merge(array($this->options), $this->optGroups) as $optGroup) { 169238a072bSMichael Grosse $value_exists = $optGroup->setValue($value) || $value_exists; 170068abb2bSMichael Grosse if ($value_exists && !$isMultiSelect) { 171068abb2bSMichael Grosse $value = null; 172068abb2bSMichael Grosse } 173238a072bSMichael Grosse } 174238a072bSMichael Grosse return $value_exists; 175238a072bSMichael Grosse } 176238a072bSMichael Grosse 177238a072bSMichael Grosse /** 1788638ead5SAndreas Gohr * Create the HTML for the select it self 1798638ead5SAndreas Gohr * 1808638ead5SAndreas Gohr * @return string 1818638ead5SAndreas Gohr */ 1828638ead5SAndreas Gohr protected function mainElementHTML() { 1838638ead5SAndreas Gohr if($this->useInput) $this->prefillInput(); 1848638ead5SAndreas Gohr 1858638ead5SAndreas Gohr $html = '<select ' . buildAttributes($this->attrs()) . '>'; 186238a072bSMichael Grosse $html .= $this->options->toHTML(); 187238a072bSMichael Grosse $html = array_reduce($this->optGroups, function($html, OptGroup $optGroup) {return $html . $optGroup->toHTML();}, $html); 1888638ead5SAndreas Gohr $html .= '</select>'; 1898638ead5SAndreas Gohr 1908638ead5SAndreas Gohr return $html; 1918638ead5SAndreas Gohr } 1928638ead5SAndreas Gohr 1938638ead5SAndreas Gohr} 194