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 array OptGroup[] */ 14238a072bSMichael Grosse protected $optGroups = array(); 158638ead5SAndreas Gohr 168638ead5SAndreas Gohr /** 178638ead5SAndreas Gohr * @param string $name The name of this form element 18*4eed441dSMichael Grosse * @param array $options The available options 198638ead5SAndreas Gohr * @param string $label The label text for this element (will be autoescaped) 208638ead5SAndreas Gohr */ 218638ead5SAndreas Gohr public function __construct($name, $options, $label = '') { 228638ead5SAndreas Gohr parent::__construct('dropdown', $name, $label); 23693978b1SMichael Grosse $this->rmattr('type'); 24*4eed441dSMichael Grosse $this->optGroups[''] = new OptGroup(null, $options); 25238a072bSMichael Grosse $this->val(''); 26238a072bSMichael Grosse } 27238a072bSMichael Grosse 28238a072bSMichael Grosse /** 29*4eed441dSMichael Grosse * Add an `<optgroup>` and respective options 30*4eed441dSMichael Grosse * 31*4eed441dSMichael Grosse * @param string $label 32*4eed441dSMichael Grosse * @param array $options 33*4eed441dSMichael Grosse * @return OptGroup a reference to the added optgroup 34238a072bSMichael Grosse * @throws \Exception 35238a072bSMichael Grosse */ 36238a072bSMichael Grosse public function addOptGroup($label, $options) { 37238a072bSMichael Grosse if (empty($label)) { 38238a072bSMichael Grosse throw new \InvalidArgumentException(hsc('<optgroup> must have a label!')); 39238a072bSMichael Grosse } 40*4eed441dSMichael Grosse $this->optGroups[$label] = new OptGroup($label, $options); 41238a072bSMichael Grosse return end($this->optGroups); 42238a072bSMichael Grosse } 43238a072bSMichael Grosse 44238a072bSMichael Grosse /** 45238a072bSMichael Grosse * Set or get the optgroups of an Dropdown-Element. 46238a072bSMichael Grosse * 47238a072bSMichael Grosse * optgroups have to be given as associative array 48238a072bSMichael Grosse * * the key being the label of the group 49238a072bSMichael Grosse * * the value being an array of options as defined in @see OptGroup::options() 50238a072bSMichael Grosse * 51238a072bSMichael Grosse * @param null|array $optGroups 522bd1d2c7SMichael Grosse * @return OptGroup[]|DropdownElement 53238a072bSMichael Grosse */ 54238a072bSMichael Grosse public function optGroups($optGroups = null) { 55238a072bSMichael Grosse if($optGroups === null) { 56238a072bSMichael Grosse return $this->optGroups; 57238a072bSMichael Grosse } 58238a072bSMichael Grosse if (!is_array($optGroups)) { 59238a072bSMichael Grosse throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!')); 60238a072bSMichael Grosse } 61238a072bSMichael Grosse $this->optGroups = array(); 62238a072bSMichael Grosse foreach ($optGroups as $label => $options) { 63238a072bSMichael Grosse $this->addOptGroup($label, $options); 64238a072bSMichael Grosse } 652bd1d2c7SMichael Grosse return $this; 668638ead5SAndreas Gohr } 678638ead5SAndreas Gohr 688638ead5SAndreas Gohr /** 698638ead5SAndreas Gohr * Get or set the options of the Dropdown 708638ead5SAndreas Gohr * 718638ead5SAndreas Gohr * Options can be given as associative array (value => label) or as an 72795955b2SMichael Grosse * indexd array (label = value) or as an array of arrays. In the latter 73795955b2SMichael Grosse * case an element has to look as follows: 74795955b2SMichael Grosse * option-value => array ( 75795955b2SMichael Grosse * 'label' => option-label, 76795955b2SMichael Grosse * 'attrs' => array ( 77795955b2SMichael Grosse * attr-key => attr-value, ... 78795955b2SMichael Grosse * ) 79795955b2SMichael Grosse * ) 808638ead5SAndreas Gohr * 818638ead5SAndreas Gohr * @param null|array $options 828638ead5SAndreas Gohr * @return $this|array 838638ead5SAndreas Gohr */ 848638ead5SAndreas Gohr public function options($options = null) { 85238a072bSMichael Grosse if ($options === null) { 86*4eed441dSMichael Grosse return $this->optGroups['']->options(); 878638ead5SAndreas Gohr } 88*4eed441dSMichael Grosse $this->optGroups[''] = new OptGroup(null, $options); 898638ead5SAndreas Gohr return $this; 908638ead5SAndreas Gohr } 918638ead5SAndreas Gohr 928638ead5SAndreas Gohr /** 938638ead5SAndreas Gohr * Gets or sets an attribute 948638ead5SAndreas Gohr * 958638ead5SAndreas Gohr * When no $value is given, the current content of the attribute is returned. 968638ead5SAndreas Gohr * An empty string is returned for unset attributes. 978638ead5SAndreas Gohr * 988638ead5SAndreas Gohr * When a $value is given, the content is set to that value and the Element 998638ead5SAndreas Gohr * itself is returned for easy chaining 1008638ead5SAndreas Gohr * 1018638ead5SAndreas Gohr * @param string $name Name of the attribute to access 1028638ead5SAndreas Gohr * @param null|string $value New value to set 1038638ead5SAndreas Gohr * @return string|$this 1048638ead5SAndreas Gohr */ 1058638ead5SAndreas Gohr public function attr($name, $value = null) { 1068638ead5SAndreas Gohr if(strtolower($name) == 'multiple') { 1078638ead5SAndreas Gohr throw new \InvalidArgumentException('Sorry, the dropdown element does not support the "multiple" attribute'); 1088638ead5SAndreas Gohr } 1098638ead5SAndreas Gohr return parent::attr($name, $value); 1108638ead5SAndreas Gohr } 1118638ead5SAndreas Gohr 1128638ead5SAndreas Gohr /** 1138638ead5SAndreas Gohr * Get or set the current value 1148638ead5SAndreas Gohr * 1158638ead5SAndreas Gohr * When setting a value that is not defined in the options, the value is ignored 1168638ead5SAndreas Gohr * and the first option's value is selected instead 1178638ead5SAndreas Gohr * 1188638ead5SAndreas Gohr * @param null|string $value The value to set 1198638ead5SAndreas Gohr * @return $this|string 1208638ead5SAndreas Gohr */ 1218638ead5SAndreas Gohr public function val($value = null) { 1228638ead5SAndreas Gohr if($value === null) return $this->value; 1238638ead5SAndreas Gohr 124238a072bSMichael Grosse $value_exists = $this->setValueInOptGroups($value); 125238a072bSMichael Grosse 126238a072bSMichael Grosse if($value_exists) { 1278638ead5SAndreas Gohr $this->value = $value; 1288638ead5SAndreas Gohr } else { 1298638ead5SAndreas Gohr // unknown value set, select first option instead 130238a072bSMichael Grosse $this->value = $this->getFirstOption(); 131238a072bSMichael Grosse $this->setValueInOptGroups($this->value); 1328638ead5SAndreas Gohr } 1338638ead5SAndreas Gohr 1348638ead5SAndreas Gohr return $this; 1358638ead5SAndreas Gohr } 1368638ead5SAndreas Gohr 1378638ead5SAndreas Gohr /** 138238a072bSMichael Grosse * Returns the first options as it will be rendered in HTML 139238a072bSMichael Grosse * 140238a072bSMichael Grosse * @return string 141238a072bSMichael Grosse */ 142238a072bSMichael Grosse protected function getFirstOption() { 143238a072bSMichael Grosse $options = $this->options(); 144238a072bSMichael Grosse if (!empty($options)) { 145238a072bSMichael Grosse return (string) array_shift(array_keys($options)); 146238a072bSMichael Grosse } 147238a072bSMichael Grosse foreach ($this->optGroups as $optGroup) { 148238a072bSMichael Grosse $options = $optGroup->options(); 149238a072bSMichael Grosse if (!empty($options)) { 150238a072bSMichael Grosse return (string) array_shift(array_keys($options)); 151238a072bSMichael Grosse } 152238a072bSMichael Grosse } 153238a072bSMichael Grosse } 154238a072bSMichael Grosse 155238a072bSMichael Grosse /** 156238a072bSMichael Grosse * Set the value in the OptGroups, including the optgroup for the options without optgroup. 157238a072bSMichael Grosse * 158238a072bSMichael Grosse * @param string $value 159238a072bSMichael Grosse * @return bool 160238a072bSMichael Grosse */ 161238a072bSMichael Grosse protected function setValueInOptGroups($value) { 162068abb2bSMichael Grosse $value_exists = false; 163068abb2bSMichael Grosse /** @var OptGroup $optGroup */ 164*4eed441dSMichael Grosse foreach ($this->optGroups as $optGroup) { 1659c3fca6dSMichael Grosse $value_exists = $optGroup->storeValue($value) || $value_exists; 16645082b9eSMichael Grosse if ($value_exists) { 167068abb2bSMichael Grosse $value = null; 168068abb2bSMichael Grosse } 169238a072bSMichael Grosse } 170238a072bSMichael Grosse return $value_exists; 171238a072bSMichael Grosse } 172238a072bSMichael Grosse 173238a072bSMichael Grosse /** 1748638ead5SAndreas Gohr * Create the HTML for the select it self 1758638ead5SAndreas Gohr * 1768638ead5SAndreas Gohr * @return string 1778638ead5SAndreas Gohr */ 1788638ead5SAndreas Gohr protected function mainElementHTML() { 1798638ead5SAndreas Gohr if($this->useInput) $this->prefillInput(); 1808638ead5SAndreas Gohr 1818638ead5SAndreas Gohr $html = '<select ' . buildAttributes($this->attrs()) . '>'; 182238a072bSMichael Grosse $html = array_reduce($this->optGroups, function($html, OptGroup $optGroup) {return $html . $optGroup->toHTML();}, $html); 1838638ead5SAndreas Gohr $html .= '</select>'; 1848638ead5SAndreas Gohr 1858638ead5SAndreas Gohr return $html; 1868638ead5SAndreas Gohr } 1878638ead5SAndreas Gohr 1888638ead5SAndreas Gohr} 189