18638ead5SAndreas Gohr<?php 29d01c1d9SSatoshi Sahara 38638ead5SAndreas Gohrnamespace dokuwiki\Form; 48638ead5SAndreas Gohr 58638ead5SAndreas Gohr/** 68638ead5SAndreas Gohr * Class DropdownElement 78638ead5SAndreas Gohr * 85a10fbceSAndreas Gohr * Represents a HTML select. Please not that prefilling with input data only works for single values. 98638ead5SAndreas Gohr * 108638ead5SAndreas Gohr * @package dokuwiki\Form 118638ead5SAndreas Gohr */ 129d01c1d9SSatoshi Saharaclass DropdownElement extends InputElement 139d01c1d9SSatoshi Sahara{ 14238a072bSMichael Grosse /** @var array OptGroup[] */ 155a10fbceSAndreas Gohr protected $optGroups = []; 165a10fbceSAndreas Gohr 175a10fbceSAndreas Gohr /** @var string[] the currently set values */ 185a10fbceSAndreas Gohr protected $values = []; 198638ead5SAndreas Gohr 208638ead5SAndreas Gohr /** 218638ead5SAndreas Gohr * @param string $name The name of this form element 224eed441dSMichael Grosse * @param array $options The available options 238638ead5SAndreas Gohr * @param string $label The label text for this element (will be autoescaped) 248638ead5SAndreas Gohr */ 259d01c1d9SSatoshi Sahara public function __construct($name, $options, $label = '') 269d01c1d9SSatoshi Sahara { 278638ead5SAndreas Gohr parent::__construct('dropdown', $name, $label); 28693978b1SMichael Grosse $this->rmattr('type'); 294eed441dSMichael Grosse $this->optGroups[''] = new OptGroup(null, $options); 30238a072bSMichael Grosse $this->val(''); 31238a072bSMichael Grosse } 32238a072bSMichael Grosse 33238a072bSMichael Grosse /** 344eed441dSMichael Grosse * Add an `<optgroup>` and respective options 354eed441dSMichael Grosse * 364eed441dSMichael Grosse * @param string $label 374eed441dSMichael Grosse * @param array $options 384eed441dSMichael Grosse * @return OptGroup a reference to the added optgroup 395a10fbceSAndreas Gohr * @throws \InvalidArgumentException 40238a072bSMichael Grosse */ 419d01c1d9SSatoshi Sahara public function addOptGroup($label, $options) 429d01c1d9SSatoshi Sahara { 43238a072bSMichael Grosse if (empty($label)) { 44238a072bSMichael Grosse throw new \InvalidArgumentException(hsc('<optgroup> must have a label!')); 45238a072bSMichael Grosse } 464eed441dSMichael Grosse $this->optGroups[$label] = new OptGroup($label, $options); 47238a072bSMichael Grosse return end($this->optGroups); 48238a072bSMichael Grosse } 49238a072bSMichael Grosse 50238a072bSMichael Grosse /** 51238a072bSMichael Grosse * Set or get the optgroups of an Dropdown-Element. 52238a072bSMichael Grosse * 53238a072bSMichael Grosse * optgroups have to be given as associative array 54238a072bSMichael Grosse * * the key being the label of the group 555a10fbceSAndreas Gohr * * the value being an array of options as defined in @param null|array $optGroups 562bd1d2c7SMichael Grosse * @return OptGroup[]|DropdownElement 575a10fbceSAndreas Gohr * @see OptGroup::options() 585a10fbceSAndreas Gohr * 59238a072bSMichael Grosse */ 609d01c1d9SSatoshi Sahara public function optGroups($optGroups = null) 619d01c1d9SSatoshi Sahara { 62238a072bSMichael Grosse if ($optGroups === null) { 63238a072bSMichael Grosse return $this->optGroups; 64238a072bSMichael Grosse } 65238a072bSMichael Grosse if (!is_array($optGroups)) { 66238a072bSMichael Grosse throw new \InvalidArgumentException(hsc('Argument must be an associative array of label => [options]!')); 67238a072bSMichael Grosse } 68*6fd0861fSAndreas Gohr $this->optGroups = []; 69238a072bSMichael Grosse foreach ($optGroups as $label => $options) { 70238a072bSMichael Grosse $this->addOptGroup($label, $options); 71238a072bSMichael Grosse } 722bd1d2c7SMichael Grosse return $this; 738638ead5SAndreas Gohr } 748638ead5SAndreas Gohr 758638ead5SAndreas Gohr /** 768638ead5SAndreas Gohr * Get or set the options of the Dropdown 778638ead5SAndreas Gohr * 788638ead5SAndreas Gohr * Options can be given as associative array (value => label) or as an 79795955b2SMichael Grosse * indexd array (label = value) or as an array of arrays. In the latter 80795955b2SMichael Grosse * case an element has to look as follows: 81795955b2SMichael Grosse * option-value => array ( 82795955b2SMichael Grosse * 'label' => option-label, 83795955b2SMichael Grosse * 'attrs' => array ( 84795955b2SMichael Grosse * attr-key => attr-value, ... 85795955b2SMichael Grosse * ) 86795955b2SMichael Grosse * ) 878638ead5SAndreas Gohr * 888638ead5SAndreas Gohr * @param null|array $options 898638ead5SAndreas Gohr * @return $this|array 908638ead5SAndreas Gohr */ 919d01c1d9SSatoshi Sahara public function options($options = null) 929d01c1d9SSatoshi Sahara { 93238a072bSMichael Grosse if ($options === null) { 944eed441dSMichael Grosse return $this->optGroups['']->options(); 958638ead5SAndreas Gohr } 964eed441dSMichael Grosse $this->optGroups[''] = new OptGroup(null, $options); 978638ead5SAndreas Gohr return $this; 988638ead5SAndreas Gohr } 998638ead5SAndreas Gohr 1008638ead5SAndreas Gohr /** 1018638ead5SAndreas Gohr * Get or set the current value 1028638ead5SAndreas Gohr * 1038638ead5SAndreas Gohr * When setting a value that is not defined in the options, the value is ignored 1048638ead5SAndreas Gohr * and the first option's value is selected instead 1058638ead5SAndreas Gohr * 1065a10fbceSAndreas Gohr * @param null|string|string[] $value The value to set 1075a10fbceSAndreas Gohr * @return $this|string|string[] 1088638ead5SAndreas Gohr */ 1099d01c1d9SSatoshi Sahara public function val($value = null) 1109d01c1d9SSatoshi Sahara { 1115a10fbceSAndreas Gohr // getter 1125a10fbceSAndreas Gohr if ($value === null) { 1135a10fbceSAndreas Gohr if (isset($this->attributes['multiple'])) { 1145a10fbceSAndreas Gohr return $this->values; 1158638ead5SAndreas Gohr } else { 1165a10fbceSAndreas Gohr return $this->values[0]; 1175a10fbceSAndreas Gohr } 1185a10fbceSAndreas Gohr } 1195a10fbceSAndreas Gohr 1205a10fbceSAndreas Gohr // setter 1215a10fbceSAndreas Gohr $this->values = $this->setValuesInOptGroups((array) $value); 1225a10fbceSAndreas Gohr if (!$this->values) { 1238638ead5SAndreas Gohr // unknown value set, select first option instead 1245a10fbceSAndreas Gohr $this->values = $this->setValuesInOptGroups((array) $this->getFirstOptionKey()); 1258638ead5SAndreas Gohr } 1268638ead5SAndreas Gohr 1278638ead5SAndreas Gohr return $this; 1288638ead5SAndreas Gohr } 1298638ead5SAndreas Gohr 1308638ead5SAndreas Gohr /** 1315a10fbceSAndreas Gohr * Returns the first option's key 132238a072bSMichael Grosse * 133238a072bSMichael Grosse * @return string 134238a072bSMichael Grosse */ 1355a10fbceSAndreas Gohr protected function getFirstOptionKey() 1369d01c1d9SSatoshi Sahara { 137238a072bSMichael Grosse $options = $this->options(); 138238a072bSMichael Grosse if (!empty($options)) { 1397b6bf7a3SMichael Grosse $keys = array_keys($options); 1407b6bf7a3SMichael Grosse return (string)array_shift($keys); 141238a072bSMichael Grosse } 142238a072bSMichael Grosse foreach ($this->optGroups as $optGroup) { 143238a072bSMichael Grosse $options = $optGroup->options(); 144238a072bSMichael Grosse if (!empty($options)) { 1457b6bf7a3SMichael Grosse $keys = array_keys($options); 1467b6bf7a3SMichael Grosse return (string)array_shift($keys); 147238a072bSMichael Grosse } 148238a072bSMichael Grosse } 1495a10fbceSAndreas Gohr 1505a10fbceSAndreas Gohr return ''; // should not happen 151238a072bSMichael Grosse } 152238a072bSMichael Grosse 153238a072bSMichael Grosse /** 154238a072bSMichael Grosse * Set the value in the OptGroups, including the optgroup for the options without optgroup. 155238a072bSMichael Grosse * 1565a10fbceSAndreas Gohr * @param string[] $values The values to be set 1575a10fbceSAndreas Gohr * @return string[] The values actually set 158238a072bSMichael Grosse */ 1595a10fbceSAndreas Gohr protected function setValuesInOptGroups($values) 1609d01c1d9SSatoshi Sahara { 1615a10fbceSAndreas Gohr $valueset = []; 1625a10fbceSAndreas Gohr 163068abb2bSMichael Grosse /** @var OptGroup $optGroup */ 1644eed441dSMichael Grosse foreach ($this->optGroups as $optGroup) { 1655a10fbceSAndreas Gohr $found = $optGroup->storeValues($values); 1665a10fbceSAndreas Gohr $values = array_diff($values, $found); 1675a10fbceSAndreas Gohr $valueset = array_merge($valueset, $found); 168068abb2bSMichael Grosse } 1695a10fbceSAndreas Gohr 1705a10fbceSAndreas Gohr return $valueset; 171238a072bSMichael Grosse } 172238a072bSMichael Grosse 173238a072bSMichael Grosse /** 1748638ead5SAndreas Gohr * Create the HTML for the select it self 1758638ead5SAndreas Gohr * 1768638ead5SAndreas Gohr * @return string 1778638ead5SAndreas Gohr */ 1789d01c1d9SSatoshi Sahara protected function mainElementHTML() 1799d01c1d9SSatoshi Sahara { 1805a10fbceSAndreas Gohr $attr = $this->attrs(); 1815a10fbceSAndreas Gohr if (isset($attr['multiple'])) { 1825a10fbceSAndreas Gohr // use array notation when multiple values are allowed 1835a10fbceSAndreas Gohr $attr['name'] .= '[]'; 1845a10fbceSAndreas Gohr } elseif ($this->useInput) { 1855a10fbceSAndreas Gohr // prefilling is only supported for non-multi fields 1865a10fbceSAndreas Gohr $this->prefillInput(); 1875a10fbceSAndreas Gohr } 1888638ead5SAndreas Gohr 1895a10fbceSAndreas Gohr $html = '<select ' . buildAttributes($attr) . '>'; 19064159a61SAndreas Gohr $html = array_reduce( 19164159a61SAndreas Gohr $this->optGroups, 192*6fd0861fSAndreas Gohr static fn($html, OptGroup $optGroup) => $html . $optGroup->toHTML(), 19364159a61SAndreas Gohr $html 19464159a61SAndreas Gohr ); 1958638ead5SAndreas Gohr $html .= '</select>'; 1968638ead5SAndreas Gohr 1978638ead5SAndreas Gohr return $html; 1988638ead5SAndreas Gohr } 1998638ead5SAndreas Gohr} 200