Localisation updates from https://translatewiki.net.
[mediawiki.git] / includes / htmlform / fields / HTMLRadioField.php
blobf048f9b218a8ca27620f0a11a1595540bb1a4a56
1 <?php
3 namespace MediaWiki\HTMLForm\Field;
5 use InvalidArgumentException;
6 use MediaWiki\Html\Html;
7 use MediaWiki\HTMLForm\HTMLFormField;
8 use MediaWiki\Parser\Sanitizer;
9 use MediaWiki\Xml\Xml;
11 /**
12 * Radio checkbox fields.
14 * @stable to extend
16 class HTMLRadioField extends HTMLFormField {
17 /**
18 * @stable to call
19 * @param array $params
20 * In addition to the usual HTMLFormField parameters, this can take the following fields:
21 * - 'flatlist': If given, the options will be displayed on a single line (wrapping to following
22 * lines if necessary), rather than each one on a line of its own. This is desirable mostly
23 * for very short lists of concisely labelled options.
24 * - 'option-descriptions': Associative array mapping values to raw HTML descriptions. These
25 * descriptions are displayed below their respective options. Note that the key-value
26 * relationship is reversed compared to 'options' and friends. Only supported by the Codex
27 * display format; if this is set when another display format is used, an exception is thrown.
28 * - 'option-descriptions-messages': Associative array mapping values to message keys.
29 * Overwrites 'option-descriptions'.
30 * - 'option-descriptions-messages-parse': If true, parse the messages in
31 * 'option-descriptions-messages'.
33 public function __construct( $params ) {
34 parent::__construct( $params );
36 if ( isset( $params['flatlist'] ) ) {
37 $this->mClass .= ' mw-htmlform-flatlist';
41 public function validate( $value, $alldata ) {
42 $p = parent::validate( $value, $alldata );
44 if ( $p !== true ) {
45 return $p;
48 if ( !is_string( $value ) && !is_int( $value ) ) {
49 return $this->msg( 'htmlform-required' );
52 $validOptions = HTMLFormField::flattenOptions( $this->getOptions() );
54 if ( in_array( strval( $value ), $validOptions, true ) ) {
55 return true;
56 } else {
57 return $this->msg( 'htmlform-select-badoption' );
61 /**
62 * This returns a block of all the radio options, in one cell.
63 * @see includes/HTMLFormField#getInputHTML()
65 * @param string $value
67 * @return string
69 public function getInputHTML( $value ) {
70 if (
71 isset( $this->mParams['option-descriptions'] ) ||
72 isset( $this->mParams['option-descriptions-messages'] ) ) {
73 throw new InvalidArgumentException(
74 "Non-Codex HTMLForms do not support the 'option-descriptions' parameter for radio buttons"
78 $html = $this->formatOptions( $this->getOptions(), strval( $value ) );
80 return $html;
83 public function getInputOOUI( $value ) {
84 if (
85 isset( $this->mParams['option-descriptions'] ) ||
86 isset( $this->mParams['option-descriptions-messages'] ) ) {
87 throw new InvalidArgumentException(
88 "Non-Codex HTMLForms do not support the 'option-descriptions' parameter for radio buttons"
92 $options = [];
93 foreach ( $this->getOptions() as $label => $data ) {
94 if ( is_int( $label ) ) {
95 $label = strval( $label );
97 $options[] = [
98 'data' => $data,
99 // @phan-suppress-next-line SecurityCheck-XSS Labels are raw when not from message
100 'label' => $this->mOptionsLabelsNotFromMessage ? new \OOUI\HtmlSnippet( $label ) : $label,
104 return new \OOUI\RadioSelectInputWidget( [
105 'name' => $this->mName,
106 'id' => $this->mID,
107 'value' => $value,
108 'options' => $options,
109 ] + \OOUI\Element::configFromHtmlAttributes(
110 $this->getAttributes( [ 'disabled', 'tabindex' ] )
111 ) );
114 public function getInputCodex( $value, $hasErrors ) {
115 $optionDescriptions = $this->getOptionDescriptions();
116 $html = '';
118 // Iterate over an array of options and return the HTML markup.
119 foreach ( $this->getOptions() as $label => $radioValue ) {
120 $description = $optionDescriptions[$radioValue] ?? '';
121 $descriptionID = Sanitizer::escapeIdForAttribute(
122 $this->mID . "-$radioValue-description"
125 // Attributes for the radio input.
126 $radioInputClasses = [ 'cdx-radio__input' ];
127 $radioInputAttribs = [
128 'id' => Sanitizer::escapeIdForAttribute( $this->mID . "-$radioValue" ),
129 'type' => 'radio',
130 'name' => $this->mName,
131 'class' => $radioInputClasses,
132 'value' => $radioValue
134 $radioInputAttribs += $this->getAttributes( [ 'disabled', 'tabindex' ] );
135 if ( $description ) {
136 $radioInputAttribs['aria-describedby'] = $descriptionID;
139 // Set the selected value as "checked".
140 if ( $radioValue === $value ) {
141 $radioInputAttribs['checked'] = true;
144 // Attributes for the radio icon.
145 $radioIconClasses = [ 'cdx-radio__icon' ];
146 $radioIconAttribs = [
147 'class' => $radioIconClasses,
150 // Attributes for the radio label.
151 $radioLabelClasses = [ 'cdx-label__label' ];
152 $radioLabelAttribs = [
153 'class' => $radioLabelClasses,
154 'for' => $radioInputAttribs['id']
157 // HTML markup for radio input, radio icon, and radio label elements.
158 $radioInput = Html::element( 'input', $radioInputAttribs );
159 $radioIcon = Html::element( 'span', $radioIconAttribs );
160 $radioLabel = $this->mOptionsLabelsNotFromMessage
161 ? Html::rawElement( 'label', $radioLabelAttribs, $label )
162 : Html::element( 'label', $radioLabelAttribs, $label );
164 $radioDescription = '';
165 if ( isset( $optionDescriptions[$radioValue] ) ) {
166 $radioDescription = Html::rawElement(
167 'span',
168 [ 'id' => $descriptionID, 'class' => 'cdx-label__description' ],
169 $optionDescriptions[$radioValue]
172 $radioLabelWrapper = Html::rawElement(
173 'div',
174 [ 'class' => 'cdx-radio__label cdx-label' ],
175 $radioLabel . $radioDescription
178 // HTML markup for CSS-only Codex Radio.
179 $radio = Html::rawElement(
180 'div',
181 [ 'class' => 'cdx-radio' ],
182 $radioInput . $radioIcon . $radioLabelWrapper
185 // Append the Codex Radio HTML markup to the initialized empty string variable.
186 $html .= $radio;
189 return $html;
192 public function formatOptions( $options, $value ) {
193 $html = '';
195 $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
196 $elementFunc = [ Html::class, $this->mOptionsLabelsNotFromMessage ? 'rawElement' : 'element' ];
198 # @todo Should this produce an unordered list perhaps?
199 foreach ( $options as $label => $info ) {
200 if ( is_array( $info ) ) {
201 $html .= Html::rawElement( 'h1', [], $label ) . "\n";
202 $html .= $this->formatOptions( $info, $value );
203 } else {
204 $id = Sanitizer::escapeIdForAttribute( $this->mID . "-$info" );
205 $classes = [ 'mw-htmlform-flatlist-item' ];
206 $radio = Xml::radio(
207 $this->mName, $info, $info === $value, $attribs + [ 'id' => $id ]
209 $radio .= "\u{00A0}" . call_user_func( $elementFunc, 'label', [ 'for' => $id ], $label );
211 $html .= ' ' . Html::rawElement(
212 'div',
213 [ 'class' => $classes ],
214 $radio
219 return $html;
223 * Fetch the array of option descriptions from the field's parameters. This checks
224 * 'option-descriptions-messages' first, then 'option-descriptions'.
226 * @return array|null Array mapping option values to raw HTML descriptions
228 protected function getOptionDescriptions() {
229 if ( array_key_exists( 'option-descriptions-messages', $this->mParams ) ) {
230 $needsParse = $this->mParams['option-descriptions-messages-parse'] ?? false;
231 $optionDescriptions = [];
232 foreach ( $this->mParams['option-descriptions-messages'] as $value => $msgKey ) {
233 $msg = $this->msg( $msgKey );
234 $optionDescriptions[$value] = $needsParse ? $msg->parse() : $msg->escaped();
236 return $optionDescriptions;
237 } elseif ( array_key_exists( 'option-descriptions', $this->mParams ) ) {
238 return $this->mParams['option-descriptions'];
242 protected function needsLabel() {
243 return false;
247 /** @deprecated class alias since 1.42 */
248 class_alias( HTMLRadioField::class, 'HTMLRadioField' );