Index: trunk/phase3/includes/HTMLForm.php |
— | — | @@ -1,16 +1,54 @@ |
2 | 2 | <?php |
3 | 3 | |
| 4 | +/** |
| 5 | + * Object handling generic submission, CSRF protection, layout and |
| 6 | + * other logic for UI forms. in a reusable manner. |
| 7 | + * |
| 8 | + * In order to generate the form, the HTMLForm object takes an array |
| 9 | + * structure detailing the form fields available. Each element of the |
| 10 | + * array is a basic property-list, including the type of field, the |
| 11 | + * label it is to be given in the form, callbacks for validation and |
| 12 | + * 'filtering', and other pertinent information. |
| 13 | + * |
| 14 | + * Field types are implemented as subclasses of the generic HTMLFormField |
| 15 | + * object, and typically implement at least getInputHTML, which generates |
| 16 | + * the HTML for the input field to be placed in the table. |
| 17 | + * |
| 18 | + * The constructor input is an associative array of $fieldname => $info, |
| 19 | + * where $info is an Associative Array with any of the following: |
| 20 | + * |
| 21 | + * 'class' -- the subclass of HTMLFormField that will be used |
| 22 | + * to create the object. *NOT* the CSS class! |
| 23 | + * 'type' -- roughly translates into the <select> type attribute. |
| 24 | + * if 'class' is not specified, this is used as a map |
| 25 | + * through HTMLForm::$typeMappings to get the class name. |
| 26 | + * 'default' -- default value when the form is displayed |
| 27 | + * 'id' -- HTML id attribute |
| 28 | + * 'options' -- varies according to the specific object. |
| 29 | + * 'label-message' -- message key for a message to use as the label. |
| 30 | + * can be an array of msg key and then parameters to |
| 31 | + * the message. |
| 32 | + * 'label' -- alternatively, a raw text message. Overridden by |
| 33 | + * label-message |
| 34 | + * 'help-message' -- message key for a message to use as a help text. |
| 35 | + * can be an array of msg key and then parameters to |
| 36 | + * the message. |
| 37 | + * 'required' -- passed through to the object, indicating that it |
| 38 | + * is a required field. |
| 39 | + * 'size' -- the length of text fields |
| 40 | + * 'filter-callback -- a function name to give you the chance to |
| 41 | + * massage the inputted value before it's processed. |
| 42 | + * @see HTMLForm::filter() |
| 43 | + * 'validation-callback' -- a function name to give you the chance |
| 44 | + * to impose extra validation on the field input. |
| 45 | + * @see HTMLForm::validate() |
| 46 | + * |
| 47 | + * TODO: Document 'section' / 'subsection' stuff |
| 48 | + */ |
4 | 49 | class HTMLForm { |
5 | 50 | static $jsAdded = false; |
6 | 51 | |
7 | | - /* The descriptor is an array of arrays. |
8 | | - i.e. array( |
9 | | - 'fieldname' => array( 'section' => 'section/subsection', |
10 | | - properties... ), |
11 | | - ... |
12 | | - ) |
13 | | - */ |
14 | | - |
| 52 | + # A mapping of 'type' inputs onto standard HTMLFormField subclasses |
15 | 53 | static $typeMappings = array( |
16 | 54 | 'text' => 'HTMLTextField', |
17 | 55 | 'select' => 'HTMLSelectField', |
— | — | @@ -28,8 +66,25 @@ |
29 | 67 | 'email' => 'HTMLTextField', |
30 | 68 | 'password' => 'HTMLTextField', |
31 | 69 | ); |
| 70 | + |
| 71 | + protected $mMessagePrefix; |
| 72 | + protected $mFlatFields; |
| 73 | + protected $mFieldTree; |
| 74 | + protected $mShowReset; |
| 75 | + public $mFieldData; |
| 76 | + protected $mSubmitCallback; |
| 77 | + protected $mValidationErrorMessage; |
| 78 | + protected $mIntro; |
| 79 | + protected $mSubmitID; |
| 80 | + protected $mSubmitText; |
| 81 | + protected $mTitle; |
32 | 82 | |
33 | | - function __construct( $descriptor, $messagePrefix ) { |
| 83 | + /** |
| 84 | + * Build a new HTMLForm from an array of field attributes |
| 85 | + * @param $descriptor Array of Field constructs, as described above |
| 86 | + * @param $messagePrefix String a prefix to go in front of default messages |
| 87 | + */ |
| 88 | + public function __construct( $descriptor, $messagePrefix='' ) { |
34 | 89 | $this->mMessagePrefix = $messagePrefix; |
35 | 90 | |
36 | 91 | // Expand out into a tree. |
— | — | @@ -43,7 +98,7 @@ |
44 | 99 | |
45 | 100 | $info['name'] = $fieldname; |
46 | 101 | |
47 | | - $field = $this->loadInputFromParameters( $info ); |
| 102 | + $field = self::loadInputFromParameters( $info ); |
48 | 103 | $field->mParent = $this; |
49 | 104 | |
50 | 105 | $setSection =& $loadedDescriptor; |
— | — | @@ -70,6 +125,10 @@ |
71 | 126 | $this->mShowReset = true; |
72 | 127 | } |
73 | 128 | |
| 129 | + /** |
| 130 | + * Add the HTMLForm-specific JavaScript, if it hasn't been |
| 131 | + * done already. |
| 132 | + */ |
74 | 133 | static function addJS() { |
75 | 134 | if( self::$jsAdded ) return; |
76 | 135 | |
— | — | @@ -78,6 +137,11 @@ |
79 | 138 | $wgOut->addScriptClass( 'htmlform' ); |
80 | 139 | } |
81 | 140 | |
| 141 | + /** |
| 142 | + * Initialise a new Object for the field |
| 143 | + * @param $descriptor input Descriptor, as described above |
| 144 | + * @return HTMLFormField subclass |
| 145 | + */ |
82 | 146 | static function loadInputFromParameters( $descriptor ) { |
83 | 147 | if ( isset( $descriptor['class'] ) ) { |
84 | 148 | $class = $descriptor['class']; |
— | — | @@ -95,15 +159,21 @@ |
96 | 160 | return $obj; |
97 | 161 | } |
98 | 162 | |
| 163 | + /** |
| 164 | + * The here's-one-I-made-earlier option: do the submission if |
| 165 | + * posted, or display the form with or without funky valiation |
| 166 | + * errors |
| 167 | + * @return Bool whether submission was successful. |
| 168 | + */ |
99 | 169 | function show() { |
100 | 170 | $html = ''; |
101 | 171 | |
102 | 172 | self::addJS(); |
103 | 173 | |
104 | | - // Load data from the request. |
| 174 | + # Load data from the request. |
105 | 175 | $this->loadData(); |
106 | 176 | |
107 | | - // Try a submission |
| 177 | + # Try a submission |
108 | 178 | global $wgUser, $wgRequest; |
109 | 179 | $editToken = $wgRequest->getVal( 'wpEditToken' ); |
110 | 180 | |
— | — | @@ -114,23 +184,31 @@ |
115 | 185 | if( $result === true ) |
116 | 186 | return $result; |
117 | 187 | |
118 | | - // Display form. |
| 188 | + # Display form. |
119 | 189 | $this->displayForm( $result ); |
| 190 | + return false; |
120 | 191 | } |
121 | 192 | |
122 | | - /** Return values: |
123 | | - * TRUE == Successful submission |
124 | | - * FALSE == No submission attempted |
125 | | - * Anything else == Error to display. |
126 | | - */ |
| 193 | + /** |
| 194 | + * Validate all the fields, and call the submision callback |
| 195 | + * function if everything is kosher. |
| 196 | + * @return Mixed Bool true == Successful submission, Bool false |
| 197 | + * == No submission attempted, anything else == Error to |
| 198 | + * display. |
| 199 | + */ |
127 | 200 | function trySubmit() { |
128 | | - // Check for validation |
| 201 | + # Check for validation |
129 | 202 | foreach( $this->mFlatFields as $fieldname => $field ) { |
130 | | - if ( !empty( $field->mParams['nodata'] ) ) continue; |
131 | | - if ( $field->validate( $this->mFieldData[$fieldname], |
132 | | - $this->mFieldData ) !== true ) { |
133 | | - return isset( $this->mValidationErrorMessage ) ? |
134 | | - $this->mValidationErrorMessage : array( 'htmlform-invalid-input' ); |
| 203 | + if ( !empty( $field->mParams['nodata'] ) ) |
| 204 | + continue; |
| 205 | + if ( $field->validate( |
| 206 | + $this->mFieldData[$fieldname], |
| 207 | + $this->mFieldData ) |
| 208 | + !== true ) |
| 209 | + { |
| 210 | + return isset( $this->mValidationErrorMessage ) |
| 211 | + ? $this->mValidationErrorMessage |
| 212 | + : array( 'htmlform-invalid-input' ); |
135 | 213 | } |
136 | 214 | } |
137 | 215 | |
— | — | @@ -143,18 +221,40 @@ |
144 | 222 | return $res; |
145 | 223 | } |
146 | 224 | |
| 225 | + /** |
| 226 | + * Set a callback to a function to do something with the form |
| 227 | + * once it's been successfully validated. |
| 228 | + * @param $cb String function name. The function will be passed |
| 229 | + * the output from HTMLForm::filterDataForSubmit, and must |
| 230 | + * return Bool true on success, Bool false if no submission |
| 231 | + * was attempted, or String HTML output to display on error. |
| 232 | + */ |
147 | 233 | function setSubmitCallback( $cb ) { |
148 | 234 | $this->mSubmitCallback = $cb; |
149 | 235 | } |
150 | 236 | |
| 237 | + /** |
| 238 | + * Set a message to display on a validation error. |
| 239 | + * @param $msg Mixed String or Array of valid inputs to wfMsgExt() |
| 240 | + * (so each entry can be either a String or Array) |
| 241 | + */ |
151 | 242 | function setValidationErrorMessage( $msg ) { |
152 | 243 | $this->mValidationErrorMessage = $msg; |
153 | 244 | } |
154 | 245 | |
| 246 | + /** |
| 247 | + * Set the introductory message |
| 248 | + * @param $msg String complete text of message to display |
| 249 | + */ |
155 | 250 | function setIntro( $msg ) { |
156 | 251 | $this->mIntro = $msg; |
157 | 252 | } |
158 | 253 | |
| 254 | + /** |
| 255 | + * Display the form (sending to wgOut), with an appropriate error |
| 256 | + * message or stack of messages, and any validation errors, etc. |
| 257 | + * @param $submitResult Mixed output from HTMLForm::trySubmit() |
| 258 | + */ |
159 | 259 | function displayForm( $submitResult ) { |
160 | 260 | global $wgOut; |
161 | 261 | |
— | — | @@ -179,6 +279,11 @@ |
180 | 280 | $wgOut->addHTML( $html ); |
181 | 281 | } |
182 | 282 | |
| 283 | + /** |
| 284 | + * Wrap the form innards in an actual <form> element |
| 285 | + * @param $html String HTML contents to wrap. |
| 286 | + * @return String wrapped HTML. |
| 287 | + */ |
183 | 288 | function wrapForm( $html ) { |
184 | 289 | return Html::rawElement( |
185 | 290 | 'form', |
— | — | @@ -190,6 +295,10 @@ |
191 | 296 | ); |
192 | 297 | } |
193 | 298 | |
| 299 | + /** |
| 300 | + * Get the hidden fields that should go inside the form. |
| 301 | + * @return String HTML. |
| 302 | + */ |
194 | 303 | function getHiddenFields() { |
195 | 304 | global $wgUser; |
196 | 305 | $html = ''; |
— | — | @@ -200,6 +309,10 @@ |
201 | 310 | return $html; |
202 | 311 | } |
203 | 312 | |
| 313 | + /** |
| 314 | + * Get the submit and (potentially) reset buttons. |
| 315 | + * @return String HTML. |
| 316 | + */ |
204 | 317 | function getButtons() { |
205 | 318 | $html = ''; |
206 | 319 | |
— | — | @@ -225,10 +338,17 @@ |
226 | 339 | return $html; |
227 | 340 | } |
228 | 341 | |
| 342 | + /** |
| 343 | + * Get the whole body of the form. |
| 344 | + */ |
229 | 345 | function getBody() { |
230 | 346 | return $this->displaySection( $this->mFieldTree ); |
231 | 347 | } |
232 | 348 | |
| 349 | + /** |
| 350 | + * Format and display an error message stack. |
| 351 | + * @param $errors Mixed String or Array of message keys |
| 352 | + */ |
233 | 353 | function displayErrors( $errors ) { |
234 | 354 | if ( is_array( $errors ) ) { |
235 | 355 | $errorstr = $this->formatErrors( $errors ); |
— | — | @@ -242,6 +362,11 @@ |
243 | 363 | $wgOut->addHTML( $errorstr ); |
244 | 364 | } |
245 | 365 | |
| 366 | + /** |
| 367 | + * Format a stack of error messages into a single HTML string |
| 368 | + * @param $errors Array of message keys/values |
| 369 | + * @return String HTML, a <ul> list of errors |
| 370 | + */ |
246 | 371 | static function formatErrors( $errors ) { |
247 | 372 | $errorstr = ''; |
248 | 373 | foreach ( $errors as $error ) { |
— | — | @@ -263,30 +388,62 @@ |
264 | 389 | return $errorstr; |
265 | 390 | } |
266 | 391 | |
| 392 | + /** |
| 393 | + * Set the text for the submit button |
| 394 | + * @param $t String plaintext. |
| 395 | + */ |
267 | 396 | function setSubmitText( $t ) { |
268 | 397 | $this->mSubmitText = $t; |
269 | 398 | } |
270 | 399 | |
| 400 | + /** |
| 401 | + * Get the text for the submit button, either customised or a default. |
| 402 | + * @return unknown_type |
| 403 | + */ |
271 | 404 | function getSubmitText() { |
272 | | - return isset( $this->mSubmitText ) ? $this->mSubmitText : wfMsg( 'htmlform-submit' ); |
| 405 | + return $this->mSubmitText |
| 406 | + ? $this->mSubmitText |
| 407 | + : wfMsg( 'htmlform-submit' ); |
273 | 408 | } |
274 | 409 | |
| 410 | + /** |
| 411 | + * Set the id for the submit button. |
| 412 | + * @param $t String. FIXME: Integrity is *not* validated |
| 413 | + */ |
275 | 414 | function setSubmitID( $t ) { |
276 | 415 | $this->mSubmitID = $t; |
277 | 416 | } |
278 | 417 | |
| 418 | + /** |
| 419 | + * Set the prefix for various default messages |
| 420 | + * TODO: currently only used for the <fieldset> legend on forms |
| 421 | + * with multiple sections; should be used elsewhre? |
| 422 | + * @param $p String |
| 423 | + */ |
279 | 424 | function setMessagePrefix( $p ) { |
280 | 425 | $this->mMessagePrefix = $p; |
281 | 426 | } |
282 | 427 | |
| 428 | + /** |
| 429 | + * Set the title for form submission |
| 430 | + * @param $t Title of page the form is on/should be posted to |
| 431 | + */ |
283 | 432 | function setTitle( $t ) { |
284 | 433 | $this->mTitle = $t; |
285 | 434 | } |
286 | 435 | |
| 436 | + /** |
| 437 | + * Get the title |
| 438 | + * @return Title |
| 439 | + */ |
287 | 440 | function getTitle() { |
288 | 441 | return $this->mTitle; |
289 | 442 | } |
290 | 443 | |
| 444 | + /** |
| 445 | + * TODO: Document |
| 446 | + * @param $fields |
| 447 | + */ |
291 | 448 | function displaySection( $fields ) { |
292 | 449 | $tableHtml = ''; |
293 | 450 | $subsectionHtml = ''; |
— | — | @@ -295,8 +452,8 @@ |
296 | 453 | foreach( $fields as $key => $value ) { |
297 | 454 | if ( is_object( $value ) ) { |
298 | 455 | $v = empty( $value->mParams['nodata'] ) |
299 | | - ? $this->mFieldData[$key] |
300 | | - : $value->getDefault(); |
| 456 | + ? $this->mFieldData[$key] |
| 457 | + : $value->getDefault(); |
301 | 458 | $tableHtml .= $value->getTableRow( $v ); |
302 | 459 | |
303 | 460 | if( $value->getLabel() != ' ' ) |
— | — | @@ -319,6 +476,9 @@ |
320 | 477 | return $subsectionHtml . "\n" . $tableHtml; |
321 | 478 | } |
322 | 479 | |
| 480 | + /** |
| 481 | + * Construct the form fields from the Descriptor array |
| 482 | + */ |
323 | 483 | function loadData() { |
324 | 484 | global $wgRequest; |
325 | 485 | |
— | — | @@ -333,7 +493,7 @@ |
334 | 494 | } |
335 | 495 | } |
336 | 496 | |
337 | | - // Filter data. |
| 497 | + # Filter data. |
338 | 498 | foreach( $fieldData as $name => &$value ) { |
339 | 499 | $field = $this->mFlatFields[$name]; |
340 | 500 | $value = $field->filter( $value, $this->mFlatFields ); |
— | — | @@ -342,33 +502,60 @@ |
343 | 503 | $this->mFieldData = $fieldData; |
344 | 504 | } |
345 | 505 | |
346 | | - function importData( $fieldData ) { |
347 | | - // Filter data. |
348 | | - foreach( $fieldData as $name => &$value ) { |
349 | | - $field = $this->mFlatFields[$name]; |
350 | | - $value = $field->filter( $value, $this->mFlatFields ); |
351 | | - } |
352 | | - |
353 | | - foreach( $this->mFlatFields as $fieldname => $field ) { |
354 | | - if ( !isset( $fieldData[$fieldname] ) ) |
355 | | - $fieldData[$fieldname] = $field->getDefault(); |
356 | | - } |
357 | | - |
358 | | - $this->mFieldData = $fieldData; |
359 | | - } |
360 | | - |
| 506 | + /** |
| 507 | + * Stop a reset button being shown for this form |
| 508 | + * @param $suppressReset Bool set to false to re-enable the |
| 509 | + * button again |
| 510 | + */ |
361 | 511 | function suppressReset( $suppressReset = true ) { |
362 | 512 | $this->mShowReset = !$suppressReset; |
363 | 513 | } |
364 | 514 | |
| 515 | + /** |
| 516 | + * Overload this if you want to apply special filtration routines |
| 517 | + * to the form as a whole, after it's submitted but before it's |
| 518 | + * processed. |
| 519 | + * @param $data |
| 520 | + * @return unknown_type |
| 521 | + */ |
365 | 522 | function filterDataForSubmit( $data ) { |
366 | 523 | return $data; |
367 | 524 | } |
368 | 525 | } |
369 | 526 | |
| 527 | +/** |
| 528 | + * The parent class to generate form fields. Any field type should |
| 529 | + * be a subclass of this. |
| 530 | + */ |
370 | 531 | abstract class HTMLFormField { |
| 532 | + |
| 533 | + protected $mValidationCallback; |
| 534 | + protected $mFilterCallback; |
| 535 | + protected $mName; |
| 536 | + public $mParams; |
| 537 | + protected $mLabel; # String label. Set on construction |
| 538 | + protected $mID; |
| 539 | + protected $mDefault; |
| 540 | + public $mParent; |
| 541 | + |
| 542 | + /** |
| 543 | + * This function must be implemented to return the HTML to generate |
| 544 | + * the input object itself. It should not implement the surrounding |
| 545 | + * table cells/rows, or labels/help messages. |
| 546 | + * @param $value String the value to set the input to; eg a default |
| 547 | + * text for a text input. |
| 548 | + * @return String valid HTML. |
| 549 | + */ |
371 | 550 | abstract function getInputHTML( $value ); |
372 | 551 | |
| 552 | + /** |
| 553 | + * Override this function to add specific validation checks on the |
| 554 | + * field input. Don't forget to call parent::validate() to ensure |
| 555 | + * that the user-defined callback mValidationCallback is still run |
| 556 | + * @param $value String the value the field was submitted with |
| 557 | + * @param $alldata $all the data collected from the form |
| 558 | + * @return Bool is the input valid |
| 559 | + */ |
373 | 560 | function validate( $value, $alldata ) { |
374 | 561 | if ( isset( $this->mValidationCallback ) ) { |
375 | 562 | return call_user_func( $this->mValidationCallback, $value, $alldata ); |
— | — | @@ -395,6 +582,12 @@ |
396 | 583 | return true; |
397 | 584 | } |
398 | 585 | |
| 586 | + /** |
| 587 | + * Get the value that this input has been set to from a posted form, |
| 588 | + * or the input's default value if it has not been set. |
| 589 | + * @param $request WebRequest |
| 590 | + * @return String the value |
| 591 | + */ |
399 | 592 | function loadDataFromRequest( $request ) { |
400 | 593 | if( $request->getCheck( $this->mName ) ) { |
401 | 594 | return $request->getText( $this->mName ); |
— | — | @@ -403,9 +596,14 @@ |
404 | 597 | } |
405 | 598 | } |
406 | 599 | |
| 600 | + /** |
| 601 | + * Initialise the object |
| 602 | + * @param $params Associative Array. See HTMLForm doc for syntax. |
| 603 | + */ |
407 | 604 | function __construct( $params ) { |
408 | 605 | $this->mParams = $params; |
409 | 606 | |
| 607 | + # Generate the label from a message, if possible |
410 | 608 | if( isset( $params['label-message'] ) ) { |
411 | 609 | $msgInfo = $params['label-message']; |
412 | 610 | |
— | — | @@ -453,8 +651,14 @@ |
454 | 652 | } |
455 | 653 | } |
456 | 654 | |
| 655 | + /** |
| 656 | + * Get the complete table row for the input, including help text, |
| 657 | + * labels, and whatever. |
| 658 | + * @param $value String the value to set the input to. |
| 659 | + * @return String complete HTML table row. |
| 660 | + */ |
457 | 661 | function getTableRow( $value ) { |
458 | | - // Check for invalid data. |
| 662 | + # Check for invalid data. |
459 | 663 | global $wgRequest; |
460 | 664 | |
461 | 665 | $errors = $this->validate( $value, $this->mParent->mFieldData ); |
— | — | @@ -517,7 +721,14 @@ |
518 | 722 | } |
519 | 723 | } |
520 | 724 | |
521 | | - static function flattenOptions( $options ) { |
| 725 | + /** |
| 726 | + * flatten an array of options to a single array, for instance, |
| 727 | + * a set of <options> inside <optgroups>. |
| 728 | + * @param $options Associative Array with values either Strings |
| 729 | + * or Arrays |
| 730 | + * @return Array flattened input |
| 731 | + */ |
| 732 | + public static function flattenOptions( $options ) { |
522 | 733 | $flatOpts = array(); |
523 | 734 | |
524 | 735 | foreach( $options as $key => $value ) { |
— | — | @@ -533,11 +744,11 @@ |
534 | 745 | } |
535 | 746 | |
536 | 747 | class HTMLTextField extends HTMLFormField { |
537 | | - # Override in derived classes to use other Xml::... functions |
538 | | - protected $mFunction = 'input'; |
539 | 748 | |
540 | 749 | function getSize() { |
541 | | - return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 45; |
| 750 | + return isset( $this->mParams['size'] ) |
| 751 | + ? $this->mParams['size'] |
| 752 | + : 45; |
542 | 753 | } |
543 | 754 | |
544 | 755 | function getInputHTML( $value ) { |
— | — | @@ -572,21 +783,25 @@ |
573 | 784 | $attribs[$param] = ''; |
574 | 785 | } |
575 | 786 | } |
| 787 | + |
| 788 | + # Implement tiny differences between some field variants |
| 789 | + # here, rather than creating a new class for each one which |
| 790 | + # is essentially just a clone of this one. |
576 | 791 | if ( isset( $this->mParams['type'] ) ) { |
577 | 792 | switch ( $this->mParams['type'] ) { |
578 | | - case 'email': |
579 | | - $attribs['type'] = 'email'; |
580 | | - break; |
581 | | - case 'int': |
582 | | - $attribs['type'] = 'number'; |
583 | | - break; |
584 | | - case 'float': |
585 | | - $attribs['type'] = 'number'; |
586 | | - $attribs['step'] = 'any'; |
587 | | - break; |
588 | | - case 'password': |
589 | | - $attribs['type'] = 'password'; |
590 | | - break; |
| 793 | + case 'email': |
| 794 | + $attribs['type'] = 'email'; |
| 795 | + break; |
| 796 | + case 'int': |
| 797 | + $attribs['type'] = 'number'; |
| 798 | + break; |
| 799 | + case 'float': |
| 800 | + $attribs['type'] = 'number'; |
| 801 | + $attribs['step'] = 'any'; |
| 802 | + break; |
| 803 | + case 'password': |
| 804 | + $attribs['type'] = 'password'; |
| 805 | + break; |
591 | 806 | } |
592 | 807 | } |
593 | 808 | } |
— | — | @@ -595,9 +810,15 @@ |
596 | 811 | } |
597 | 812 | } |
598 | 813 | |
| 814 | +/** |
| 815 | + * A field that will contain a numeric value |
| 816 | + */ |
599 | 817 | class HTMLFloatField extends HTMLTextField { |
| 818 | + |
600 | 819 | function getSize() { |
601 | | - return isset( $this->mParams['size'] ) ? $this->mParams['size'] : 20; |
| 820 | + return isset( $this->mParams['size'] ) |
| 821 | + ? $this->mParams['size'] |
| 822 | + : 20; |
602 | 823 | } |
603 | 824 | |
604 | 825 | function validate( $value, $alldata ) { |
— | — | @@ -611,8 +832,8 @@ |
612 | 833 | |
613 | 834 | $in_range = true; |
614 | 835 | |
615 | | - # The "int" part of these message names is rather confusing. They make |
616 | | - # equal sense for all numbers. |
| 836 | + # The "int" part of these message names is rather confusing. |
| 837 | + # They make equal sense for all numbers. |
617 | 838 | if ( isset( $this->mParams['min'] ) ) { |
618 | 839 | $min = $this->mParams['min']; |
619 | 840 | if ( $min > $value ) |
— | — | @@ -629,6 +850,9 @@ |
630 | 851 | } |
631 | 852 | } |
632 | 853 | |
| 854 | +/** |
| 855 | + * A field that must contain a number |
| 856 | + */ |
633 | 857 | class HTMLIntField extends HTMLFloatField { |
634 | 858 | function validate( $value, $alldata ) { |
635 | 859 | $p = parent::validate( $value, $alldata ); |
— | — | @@ -643,6 +867,9 @@ |
644 | 868 | } |
645 | 869 | } |
646 | 870 | |
| 871 | +/** |
| 872 | + * A checkbox field |
| 873 | + */ |
647 | 874 | class HTMLCheckField extends HTMLFormField { |
648 | 875 | function getInputHTML( $value ) { |
649 | 876 | if ( !empty( $this->mParams['invert'] ) ) |
— | — | @@ -657,8 +884,12 @@ |
658 | 885 | Html::rawElement( 'label', array( 'for' => $this->mID ), $this->mLabel ); |
659 | 886 | } |
660 | 887 | |
| 888 | + /** |
| 889 | + * For a checkbox, the label goes on the right hand side, and is |
| 890 | + * added in getInputHTML(), rather than HTMLFormField::getRow() |
| 891 | + */ |
661 | 892 | function getLabel() { |
662 | | - return ' '; // In the right-hand column. |
| 893 | + return ' '; |
663 | 894 | } |
664 | 895 | |
665 | 896 | function loadDataFromRequest( $request ) { |
— | — | @@ -682,6 +913,9 @@ |
683 | 914 | } |
684 | 915 | } |
685 | 916 | |
| 917 | +/** |
| 918 | + * A select dropdown field. Basically a wrapper for Xmlselect class |
| 919 | + */ |
686 | 920 | class HTMLSelectField extends HTMLFormField { |
687 | 921 | |
688 | 922 | function validate( $value, $alldata ) { |
— | — | @@ -698,9 +932,9 @@ |
699 | 933 | function getInputHTML( $value ) { |
700 | 934 | $select = new XmlSelect( $this->mName, $this->mID, strval( $value ) ); |
701 | 935 | |
702 | | - // If one of the options' 'name' is int(0), it is automatically selected. |
703 | | - // because PHP sucks and things int(0) == 'some string'. |
704 | | - // Working around this by forcing all of them to strings. |
| 936 | + # If one of the options' 'name' is int(0), it is automatically selected. |
| 937 | + # because PHP sucks and things int(0) == 'some string'. |
| 938 | + # Working around this by forcing all of them to strings. |
705 | 939 | $options = array_map( 'strval', $this->mParams['options'] ); |
706 | 940 | |
707 | 941 | if( !empty( $this->mParams['disabled'] ) ) { |
— | — | @@ -713,6 +947,9 @@ |
714 | 948 | } |
715 | 949 | } |
716 | 950 | |
| 951 | +/** |
| 952 | + * Select dropdown field, with an additional "other" textbox. |
| 953 | + */ |
717 | 954 | class HTMLSelectOrOtherField extends HTMLTextField { |
718 | 955 | static $jsAdded = false; |
719 | 956 | |
— | — | @@ -783,15 +1020,19 @@ |
784 | 1021 | } |
785 | 1022 | } |
786 | 1023 | |
| 1024 | +/** |
| 1025 | + * Multi-select field |
| 1026 | + */ |
787 | 1027 | class HTMLMultiSelectField extends HTMLFormField { |
| 1028 | + |
788 | 1029 | function validate( $value, $alldata ) { |
789 | 1030 | $p = parent::validate( $value, $alldata ); |
790 | 1031 | if( $p !== true ) return $p; |
791 | 1032 | |
792 | 1033 | if( !is_array( $value ) ) return false; |
793 | 1034 | |
794 | | - // If all options are valid, array_intersect of the valid options and the provided |
795 | | - // options will return the provided options. |
| 1035 | + # If all options are valid, array_intersect of the valid options |
| 1036 | + # and the provided options will return the provided options. |
796 | 1037 | $validOptions = HTMLFormField::flattenOptions( $this->mParams['options'] ); |
797 | 1038 | |
798 | 1039 | $validValues = array_intersect( $value, $validOptions ); |
— | — | @@ -834,7 +1075,7 @@ |
835 | 1076 | } |
836 | 1077 | |
837 | 1078 | function loadDataFromRequest( $request ) { |
838 | | - // won't work with getCheck |
| 1079 | + # won't work with getCheck |
839 | 1080 | if( $request->getCheck( 'wpEditToken' ) ) { |
840 | 1081 | $arr = $request->getArray( $this->mName ); |
841 | 1082 | |
— | — | @@ -860,6 +1101,9 @@ |
861 | 1102 | } |
862 | 1103 | } |
863 | 1104 | |
| 1105 | +/** |
| 1106 | + * Radio checkbox fields. |
| 1107 | + */ |
864 | 1108 | class HTMLRadioField extends HTMLFormField { |
865 | 1109 | function validate( $value, $alldata ) { |
866 | 1110 | $p = parent::validate( $value, $alldata ); |
— | — | @@ -876,6 +1120,10 @@ |
877 | 1121 | return wfMsgExt( 'htmlform-select-badoption', 'parseinline' ); |
878 | 1122 | } |
879 | 1123 | |
| 1124 | + /** |
| 1125 | + * This returns a block of all the radio options, in one cell. |
| 1126 | + * @see includes/HTMLFormField#getInputHTML() |
| 1127 | + */ |
880 | 1128 | function getInputHTML( $value ) { |
881 | 1129 | $html = $this->formatOptions( $this->mParams['options'], $value ); |
882 | 1130 | |
— | — | @@ -890,6 +1138,7 @@ |
891 | 1139 | $attribs['disabled'] = 'disabled'; |
892 | 1140 | } |
893 | 1141 | |
| 1142 | + # TODO: should this produce an unordered list perhaps? |
894 | 1143 | foreach( $options as $label => $info ) { |
895 | 1144 | if( is_array( $info ) ) { |
896 | 1145 | $html .= Html::rawElement( 'h1', array(), $label ) . "\n"; |
— | — | @@ -913,6 +1162,9 @@ |
914 | 1163 | } |
915 | 1164 | } |
916 | 1165 | |
| 1166 | +/** |
| 1167 | + * An information field (text blob), not a proper input. |
| 1168 | + */ |
917 | 1169 | class HTMLInfoField extends HTMLFormField { |
918 | 1170 | function __construct( $info ) { |
919 | 1171 | $info['nodata'] = true; |