Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 151 |
|
0.00% |
0 / 11 |
CRAP | |
0.00% |
0 / 1 |
HTMLTextField | |
0.00% |
0 / 150 |
|
0.00% |
0 / 11 |
1560 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
42 | |||
getSize | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSpellCheck | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 | |||
isPersistent | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
12 | |||
getInputHTML | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
20 | |||
getType | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
72 | |||
getInputOOUI | |
0.00% |
0 / 38 |
|
0.00% |
0 / 1 |
42 | |||
getInputCodex | |
0.00% |
0 / 33 |
|
0.00% |
0 / 1 |
20 | |||
buildCodexComponent | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
6 | |||
getInputWidget | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDataAttribs | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\HTMLForm\Field; |
4 | |
5 | use MediaWiki\Html\Html; |
6 | use MediaWiki\HTMLForm\HTMLFormField; |
7 | use OOUI\Widget; |
8 | |
9 | /** |
10 | * <input> field. |
11 | * |
12 | * Besides the parameters recognized by HTMLFormField, the following are |
13 | * recognized: |
14 | * autocomplete - HTML autocomplete value (a boolean for on/off or a string according to |
15 | * https://html.spec.whatwg.org/multipage/forms.html#autofill ) |
16 | * |
17 | * @stable to extend |
18 | */ |
19 | class HTMLTextField extends HTMLFormField { |
20 | protected $mPlaceholder = ''; |
21 | |
22 | /** @var bool HTML autocomplete attribute */ |
23 | protected $autocomplete; |
24 | |
25 | /** |
26 | * @stable to call |
27 | * |
28 | * @param array $params |
29 | * - type: HTML textfield type |
30 | * - size: field size in characters (defaults to 45) |
31 | * - placeholder/placeholder-message: set HTML placeholder attribute |
32 | * - spellcheck: set HTML spellcheck attribute |
33 | * - persistent: upon unsuccessful requests, retain the value (defaults to true, except |
34 | * for password fields) |
35 | */ |
36 | public function __construct( $params ) { |
37 | if ( isset( $params['autocomplete'] ) && is_bool( $params['autocomplete'] ) ) { |
38 | $params['autocomplete'] = $params['autocomplete'] ? 'on' : 'off'; |
39 | } |
40 | |
41 | parent::__construct( $params ); |
42 | |
43 | if ( isset( $params['placeholder-message'] ) ) { |
44 | $this->mPlaceholder = $this->getMessage( $params['placeholder-message'] )->text(); |
45 | } elseif ( isset( $params['placeholder'] ) ) { |
46 | $this->mPlaceholder = $params['placeholder']; |
47 | } |
48 | } |
49 | |
50 | /** |
51 | * @stable to override |
52 | * @return int |
53 | */ |
54 | public function getSize() { |
55 | return $this->mParams['size'] ?? 45; |
56 | } |
57 | |
58 | public function getSpellCheck() { |
59 | $val = $this->mParams['spellcheck'] ?? null; |
60 | if ( is_bool( $val ) ) { |
61 | // "spellcheck" attribute literally requires "true" or "false" to work. |
62 | return $val ? 'true' : 'false'; |
63 | } |
64 | return null; |
65 | } |
66 | |
67 | public function isPersistent() { |
68 | if ( isset( $this->mParams['persistent'] ) ) { |
69 | return $this->mParams['persistent']; |
70 | } |
71 | // don't put passwords into the HTML body, they could get cached or otherwise leaked |
72 | return !( isset( $this->mParams['type'] ) && $this->mParams['type'] === 'password' ); |
73 | } |
74 | |
75 | /** |
76 | * @inheritDoc |
77 | * @stable to override |
78 | */ |
79 | public function getInputHTML( $value ) { |
80 | if ( !$this->isPersistent() ) { |
81 | $value = ''; |
82 | } |
83 | |
84 | $attribs = [ |
85 | 'id' => $this->mID, |
86 | 'name' => $this->mName, |
87 | 'size' => $this->getSize(), |
88 | 'value' => $value, |
89 | 'dir' => $this->mDir, |
90 | 'spellcheck' => $this->getSpellCheck(), |
91 | ] + $this->getTooltipAndAccessKey() + $this->getDataAttribs(); |
92 | |
93 | if ( $this->mClass !== '' ) { |
94 | $attribs['class'] = $this->mClass; |
95 | } |
96 | if ( $this->mPlaceholder !== '' ) { |
97 | $attribs['placeholder'] = $this->mPlaceholder; |
98 | } |
99 | |
100 | # @todo Enforce pattern, step, required, readonly on the server side as |
101 | # well |
102 | $allowedParams = [ |
103 | 'type', |
104 | 'min', |
105 | 'max', |
106 | 'step', |
107 | 'title', |
108 | 'maxlength', |
109 | 'minlength', |
110 | 'tabindex', |
111 | 'disabled', |
112 | 'required', |
113 | 'autofocus', |
114 | 'readonly', |
115 | 'autocomplete', |
116 | // Only used in HTML mode: |
117 | 'pattern', |
118 | 'list', |
119 | ]; |
120 | |
121 | $attribs += $this->getAttributes( $allowedParams ); |
122 | |
123 | # Extract 'type' |
124 | $type = $this->getType( $attribs ); |
125 | |
126 | $inputHtml = Html::input( $this->mName, $value, $type, $attribs ); |
127 | return $inputHtml; |
128 | } |
129 | |
130 | protected function getType( &$attribs ) { |
131 | $type = $attribs['type'] ?? 'text'; |
132 | unset( $attribs['type'] ); |
133 | |
134 | # Implement tiny differences between some field variants |
135 | # here, rather than creating a new class for each one which |
136 | # is essentially just a clone of this one. |
137 | if ( isset( $this->mParams['type'] ) ) { |
138 | switch ( $this->mParams['type'] ) { |
139 | case 'int': |
140 | $type = 'number'; |
141 | $attribs['step'] = 1; |
142 | break; |
143 | case 'float': |
144 | $type = 'number'; |
145 | $attribs['step'] = 'any'; |
146 | break; |
147 | # Pass through |
148 | case 'email': |
149 | case 'password': |
150 | case 'url': |
151 | $type = $this->mParams['type']; |
152 | break; |
153 | case 'textwithbutton': |
154 | $type = $this->mParams['inputtype'] ?? 'text'; |
155 | break; |
156 | } |
157 | } |
158 | |
159 | return $type; |
160 | } |
161 | |
162 | /** |
163 | * @inheritDoc |
164 | * @stable to override |
165 | */ |
166 | public function getInputOOUI( $value ) { |
167 | if ( !$this->isPersistent() ) { |
168 | $value = ''; |
169 | } |
170 | |
171 | $attribs = $this->getTooltipAndAccessKeyOOUI(); |
172 | |
173 | if ( $this->mClass !== '' ) { |
174 | $attribs['classes'] = [ $this->mClass ]; |
175 | } |
176 | if ( $this->mPlaceholder !== '' ) { |
177 | $attribs['placeholder'] = $this->mPlaceholder; |
178 | } |
179 | |
180 | # @todo Enforce pattern, step, required, readonly on the server side as |
181 | # well |
182 | $allowedParams = [ |
183 | 'type', |
184 | 'min', |
185 | 'max', |
186 | 'step', |
187 | 'title', |
188 | 'maxlength', |
189 | 'minlength', |
190 | 'tabindex', |
191 | 'disabled', |
192 | 'required', |
193 | 'autofocus', |
194 | 'readonly', |
195 | 'autocomplete', |
196 | // Only used in OOUI mode: |
197 | 'autosize', |
198 | 'flags', |
199 | 'indicator', |
200 | ]; |
201 | |
202 | $attribs += \OOUI\Element::configFromHtmlAttributes( |
203 | $this->getAttributes( $allowedParams ) |
204 | ); |
205 | |
206 | $type = $this->getType( $attribs ); |
207 | if ( isset( $attribs['step'] ) && $attribs['step'] === 'any' ) { |
208 | $attribs['step'] = null; |
209 | } |
210 | |
211 | return $this->getInputWidget( [ |
212 | 'id' => $this->mID, |
213 | 'name' => $this->mName, |
214 | 'value' => $value, |
215 | 'type' => $type, |
216 | 'dir' => $this->mDir, |
217 | ] + $attribs ); |
218 | } |
219 | |
220 | public function getInputCodex( $value, $hasErrors ) { |
221 | if ( !$this->isPersistent() ) { |
222 | $value = ''; |
223 | } |
224 | |
225 | $attribs = [ |
226 | 'id' => $this->mID, |
227 | 'name' => $this->mName, |
228 | 'size' => $this->getSize(), |
229 | 'value' => $value, |
230 | 'dir' => $this->mDir, |
231 | 'spellcheck' => $this->getSpellCheck(), |
232 | ] + $this->getTooltipAndAccessKey() + $this->getDataAttribs(); |
233 | |
234 | if ( $this->mPlaceholder !== '' ) { |
235 | $attribs['placeholder'] = $this->mPlaceholder; |
236 | } |
237 | $attribs['class'] = $this->mClass ? [ $this->mClass ] : []; |
238 | |
239 | $allowedParams = [ |
240 | 'type', |
241 | 'min', |
242 | 'max', |
243 | 'step', |
244 | 'title', |
245 | 'maxlength', |
246 | 'minlength', |
247 | 'tabindex', |
248 | 'disabled', |
249 | 'required', |
250 | 'autofocus', |
251 | 'readonly', |
252 | 'autocomplete', |
253 | 'pattern', |
254 | 'list', |
255 | ]; |
256 | |
257 | $attribs += $this->getAttributes( $allowedParams ); |
258 | |
259 | // Extract 'type'. |
260 | $type = $this->getType( $attribs ); |
261 | |
262 | return static::buildCodexComponent( $value, $hasErrors, $type, $this->mName, $attribs ); |
263 | } |
264 | |
265 | /** |
266 | * Build the markup of the Codex component |
267 | * |
268 | * @param string $value The value to set the input to |
269 | * @param bool $hasErrors Whether there are validation errors. |
270 | * @param string $type The input's type attribute |
271 | * @param string $name The input's name attribute |
272 | * @param array $inputAttribs Other input attributes |
273 | * @return string Raw HTML |
274 | */ |
275 | public static function buildCodexComponent( $value, $hasErrors, $type, $name, $inputAttribs ) { |
276 | // Set up classes for the outer <div>. |
277 | $wrapperClass = [ 'cdx-text-input' ]; |
278 | if ( $hasErrors ) { |
279 | $wrapperClass[] = 'cdx-text-input--status-error'; |
280 | } |
281 | |
282 | $inputAttribs['class'][] = 'cdx-text-input__input'; |
283 | $inputHtml = Html::input( $name, $value, $type, $inputAttribs ); |
284 | |
285 | return Html::rawElement( 'div', [ 'class' => $wrapperClass ], $inputHtml ); |
286 | } |
287 | |
288 | /** |
289 | * @stable to override |
290 | * |
291 | * @param array $params |
292 | * |
293 | * @return Widget |
294 | */ |
295 | protected function getInputWidget( $params ) { |
296 | return new \OOUI\TextInputWidget( $params ); |
297 | } |
298 | |
299 | /** |
300 | * Returns an array of data-* attributes to add to the field. |
301 | * @stable to override |
302 | * |
303 | * @return array |
304 | */ |
305 | protected function getDataAttribs() { |
306 | return []; |
307 | } |
308 | } |
309 | |
310 | /** @deprecated class alias since 1.42 */ |
311 | class_alias( HTMLTextField::class, 'HTMLTextField' ); |