18638ead5SAndreas Gohr<?php 2*9d01c1d9SSatoshi Sahara 38638ead5SAndreas Gohrnamespace dokuwiki\Form; 48638ead5SAndreas Gohr 58638ead5SAndreas Gohr/** 68638ead5SAndreas Gohr * Class DropdownElement 78638ead5SAndreas Gohr * 88638ead5SAndreas Gohr * Represents a HTML select. Please note that this does not support multiple selected options! 98638ead5SAndreas Gohr * 108638ead5SAndreas Gohr * @package dokuwiki\Form 118638ead5SAndreas Gohr */ 12*9d01c1d9SSatoshi Saharaclass DropdownElement extends InputElement 13*9d01c1d9SSatoshi Sahara{ 14238a072bSMichael Grosse /** @var array OptGroup[] */ 15238a072bSMichael Grosse protected $optGroups = array(); 168638ead5SAndreas Gohr 178638ead5SAndreas Gohr /** 188638ead5SAndreas Gohr * @param string $name The name of this form element 194eed441dSMichael Grosse * @param array $options The available options 208638ead5SAndreas Gohr * @param string $label The label text for this element (will be autoescaped) 218638ead5SAndreas Gohr */ 22*9d01c1d9SSatoshi Sahara public function __construct($name, $options, $label = '') 23*9d01c1d9SSatoshi Sahara { 248638ead5SAndreas Gohr parent::__construct('dropdown', $name, $label); 25693978b1SMichael Grosse $this->rmattr('type'); 264eed441dSMichael Grosse $this->optGroups[''] = new OptGroup(null, $options); 27238a072bSMichael Grosse $this->val(''); 28238a072bSMichael Grosse } 29238a072bSMichael Grosse 30238a072bSMichael Grosse /** 314eed441dSMichael Grosse * Add an `<optgroup>` and respective options 324eed441dSMichael Grosse * 334eed441dSMichael Grosse * @param string $label 344eed441dSMichael Grosse * @param array $options 354eed441dSMichael Grosse * @return OptGroup a reference to the added optgroup 36238a072bSMichael Grosse * @throws \Exception 37238a072bSMichael Grosse */ 38*9d01c1d9SSatoshi Sahara public function addOptGroup($label, $options) 39*9d01c1d9SSatoshi Sahara { 40238a072bSMichael Grosse if (empty($label)) { 41238a072bSMichael Grosse throw new \InvalidArgumentException(hsc('<optgroup> must have a label!')); 42238a072bSMichael Grosse } 434eed441dSMichael Grosse $this->optGroups[$label] = 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 552bd1d2c7SMichael Grosse * @return OptGroup[]|DropdownElement 56238a072bSMichael Grosse */ 57*9d01c1d9SSatoshi Sahara public function optGroups($optGroups = null) 58*9d01c1d9SSatoshi Sahara { 59238a072bSMichael Grosse if ($optGroups === null) { 60238a072bSMichael Grosse return $this->optGroups; 61238a072bSMichael Grosse } 62238a072bSMichael Grosse if (!is_array($optGroups)) { 63238a072bSMichael Grosse throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!')); 64238a072bSMichael Grosse } 65238a072bSMichael Grosse $this->optGroups = array(); 66238a072bSMichael Grosse foreach ($optGroups as $label => $options) { 67238a072bSMichael Grosse $this->addOptGroup($label, $options); 68238a072bSMichael Grosse } 692bd1d2c7SMichael Grosse return $this; 708638ead5SAndreas Gohr } 718638ead5SAndreas Gohr 728638ead5SAndreas Gohr /** 738638ead5SAndreas Gohr * Get or set the options of the Dropdown 748638ead5SAndreas Gohr * 758638ead5SAndreas Gohr * Options can be given as associative array (value => label) or as an 76795955b2SMichael Grosse * indexd array (label = value) or as an array of arrays. In the latter 77795955b2SMichael Grosse * case an element has to look as follows: 78795955b2SMichael Grosse * option-value => array ( 79795955b2SMichael Grosse * 'label' => option-label, 80795955b2SMichael Grosse * 'attrs' => array ( 81795955b2SMichael Grosse * attr-key => attr-value, ... 82795955b2SMichael Grosse * ) 83795955b2SMichael Grosse * ) 848638ead5SAndreas Gohr * 858638ead5SAndreas Gohr * @param null|array $options 868638ead5SAndreas Gohr * @return $this|array 878638ead5SAndreas Gohr */ 88*9d01c1d9SSatoshi Sahara public function options($options = null) 89*9d01c1d9SSatoshi Sahara { 90238a072bSMichael Grosse if ($options === null) { 914eed441dSMichael Grosse return $this->optGroups['']->options(); 928638ead5SAndreas Gohr } 934eed441dSMichael Grosse $this->optGroups[''] = new OptGroup(null, $options); 948638ead5SAndreas Gohr return $this; 958638ead5SAndreas Gohr } 968638ead5SAndreas Gohr 978638ead5SAndreas Gohr /** 988638ead5SAndreas Gohr * Gets or sets an attribute 998638ead5SAndreas Gohr * 1008638ead5SAndreas Gohr * When no $value is given, the current content of the attribute is returned. 1018638ead5SAndreas Gohr * An empty string is returned for unset attributes. 1028638ead5SAndreas Gohr * 1038638ead5SAndreas Gohr * When a $value is given, the content is set to that value and the Element 1048638ead5SAndreas Gohr * itself is returned for easy chaining 1058638ead5SAndreas Gohr * 1068638ead5SAndreas Gohr * @param string $name Name of the attribute to access 1078638ead5SAndreas Gohr * @param null|string $value New value to set 1088638ead5SAndreas Gohr * @return string|$this 1098638ead5SAndreas Gohr */ 110*9d01c1d9SSatoshi Sahara public function attr($name, $value = null) 111*9d01c1d9SSatoshi Sahara { 1128638ead5SAndreas Gohr if (strtolower($name) == 'multiple') { 11364159a61SAndreas Gohr throw new \InvalidArgumentException( 11464159a61SAndreas Gohr 'Sorry, the dropdown element does not support the "multiple" attribute' 11564159a61SAndreas Gohr ); 1168638ead5SAndreas Gohr } 1178638ead5SAndreas Gohr return parent::attr($name, $value); 1188638ead5SAndreas Gohr } 1198638ead5SAndreas Gohr 1208638ead5SAndreas Gohr /** 1218638ead5SAndreas Gohr * Get or set the current value 1228638ead5SAndreas Gohr * 1238638ead5SAndreas Gohr * When setting a value that is not defined in the options, the value is ignored 1248638ead5SAndreas Gohr * and the first option's value is selected instead 1258638ead5SAndreas Gohr * 1268638ead5SAndreas Gohr * @param null|string $value The value to set 1278638ead5SAndreas Gohr * @return $this|string 1288638ead5SAndreas Gohr */ 129*9d01c1d9SSatoshi Sahara public function val($value = null) 130*9d01c1d9SSatoshi Sahara { 1318638ead5SAndreas Gohr if ($value === null) return $this->value; 1328638ead5SAndreas Gohr 133238a072bSMichael Grosse $value_exists = $this->setValueInOptGroups($value); 134238a072bSMichael Grosse 135238a072bSMichael Grosse if ($value_exists) { 1368638ead5SAndreas Gohr $this->value = $value; 1378638ead5SAndreas Gohr } else { 1388638ead5SAndreas Gohr // unknown value set, select first option instead 139238a072bSMichael Grosse $this->value = $this->getFirstOption(); 140238a072bSMichael Grosse $this->setValueInOptGroups($this->value); 1418638ead5SAndreas Gohr } 1428638ead5SAndreas Gohr 1438638ead5SAndreas Gohr return $this; 1448638ead5SAndreas Gohr } 1458638ead5SAndreas Gohr 1468638ead5SAndreas Gohr /** 147238a072bSMichael Grosse * Returns the first options as it will be rendered in HTML 148238a072bSMichael Grosse * 149238a072bSMichael Grosse * @return string 150238a072bSMichael Grosse */ 151*9d01c1d9SSatoshi Sahara protected function getFirstOption() 152*9d01c1d9SSatoshi Sahara { 153238a072bSMichael Grosse $options = $this->options(); 154238a072bSMichael Grosse if (!empty($options)) { 1557b6bf7a3SMichael Grosse $keys = array_keys($options); 1567b6bf7a3SMichael Grosse return (string) array_shift($keys); 157238a072bSMichael Grosse } 158238a072bSMichael Grosse foreach ($this->optGroups as $optGroup) { 159238a072bSMichael Grosse $options = $optGroup->options(); 160238a072bSMichael Grosse if (!empty($options)) { 1617b6bf7a3SMichael Grosse $keys = array_keys($options); 1627b6bf7a3SMichael Grosse return (string) array_shift($keys); 163238a072bSMichael Grosse } 164238a072bSMichael Grosse } 165238a072bSMichael Grosse } 166238a072bSMichael Grosse 167238a072bSMichael Grosse /** 168238a072bSMichael Grosse * Set the value in the OptGroups, including the optgroup for the options without optgroup. 169238a072bSMichael Grosse * 170238a072bSMichael Grosse * @param string $value 171238a072bSMichael Grosse * @return bool 172238a072bSMichael Grosse */ 173*9d01c1d9SSatoshi Sahara protected function setValueInOptGroups($value) 174*9d01c1d9SSatoshi Sahara { 175068abb2bSMichael Grosse $value_exists = false; 176068abb2bSMichael Grosse /** @var OptGroup $optGroup */ 1774eed441dSMichael Grosse foreach ($this->optGroups as $optGroup) { 1789c3fca6dSMichael Grosse $value_exists = $optGroup->storeValue($value) || $value_exists; 17945082b9eSMichael Grosse if ($value_exists) { 180068abb2bSMichael Grosse $value = null; 181068abb2bSMichael Grosse } 182238a072bSMichael Grosse } 183238a072bSMichael Grosse return $value_exists; 184238a072bSMichael Grosse } 185238a072bSMichael Grosse 186238a072bSMichael Grosse /** 1878638ead5SAndreas Gohr * Create the HTML for the select it self 1888638ead5SAndreas Gohr * 1898638ead5SAndreas Gohr * @return string 1908638ead5SAndreas Gohr */ 191*9d01c1d9SSatoshi Sahara protected function mainElementHTML() 192*9d01c1d9SSatoshi Sahara { 1938638ead5SAndreas Gohr if ($this->useInput) $this->prefillInput(); 1948638ead5SAndreas Gohr 1958638ead5SAndreas Gohr $html = '<select ' . buildAttributes($this->attrs()) . '>'; 19664159a61SAndreas Gohr $html = array_reduce( 19764159a61SAndreas Gohr $this->optGroups, 19864159a61SAndreas Gohr function ($html, OptGroup $optGroup) { 19964159a61SAndreas Gohr return $html . $optGroup->toHTML(); 20064159a61SAndreas Gohr }, 20164159a61SAndreas Gohr $html 20264159a61SAndreas Gohr ); 2038638ead5SAndreas Gohr $html .= '</select>'; 2048638ead5SAndreas Gohr 2058638ead5SAndreas Gohr return $html; 2068638ead5SAndreas Gohr } 2078638ead5SAndreas Gohr 2088638ead5SAndreas Gohr} 209