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