]> scripts.mit.edu Git - autoinstallsdev/mediawiki.git/blob - includes/htmlform/fields/HTMLCheckMatrix.php
MediaWiki 1.30.2
[autoinstallsdev/mediawiki.git] / includes / htmlform / fields / HTMLCheckMatrix.php
1 <?php
2
3 /**
4  * A checkbox matrix
5  * Operates similarly to HTMLMultiSelectField, but instead of using an array of
6  * options, uses an array of rows and an array of columns to dynamically
7  * construct a matrix of options. The tags used to identify a particular cell
8  * are of the form "columnName-rowName"
9  *
10  * Options:
11  *   - columns
12  *     - Required list of columns in the matrix.
13  *   - rows
14  *     - Required list of rows in the matrix.
15  *   - force-options-on
16  *     - Accepts array of column-row tags to be displayed as enabled but unavailable to change
17  *   - force-options-off
18  *     - Accepts array of column-row tags to be displayed as disabled but unavailable to change.
19  *   - tooltips
20  *     - Optional array mapping row label to tooltip content
21  *   - tooltip-class
22  *     - Optional CSS class used on tooltip container span. Defaults to mw-icon-question.
23  */
24 class HTMLCheckMatrix extends HTMLFormField implements HTMLNestedFilterable {
25         static private $requiredParams = [
26                 // Required by underlying HTMLFormField
27                 'fieldname',
28                 // Required by HTMLCheckMatrix
29                 'rows',
30                 'columns'
31         ];
32
33         public function __construct( $params ) {
34                 $missing = array_diff( self::$requiredParams, array_keys( $params ) );
35                 if ( $missing ) {
36                         throw new HTMLFormFieldRequiredOptionsException( $this, $missing );
37                 }
38                 parent::__construct( $params );
39         }
40
41         public function validate( $value, $alldata ) {
42                 $rows = $this->mParams['rows'];
43                 $columns = $this->mParams['columns'];
44
45                 // Make sure user-defined validation callback is run
46                 $p = parent::validate( $value, $alldata );
47                 if ( $p !== true ) {
48                         return $p;
49                 }
50
51                 // Make sure submitted value is an array
52                 if ( !is_array( $value ) ) {
53                         return false;
54                 }
55
56                 // If all options are valid, array_intersect of the valid options
57                 // and the provided options will return the provided options.
58                 $validOptions = [];
59                 foreach ( $rows as $rowTag ) {
60                         foreach ( $columns as $columnTag ) {
61                                 $validOptions[] = $columnTag . '-' . $rowTag;
62                         }
63                 }
64                 $validValues = array_intersect( $value, $validOptions );
65                 if ( count( $validValues ) == count( $value ) ) {
66                         return true;
67                 } else {
68                         return $this->msg( 'htmlform-select-badoption' );
69                 }
70         }
71
72         /**
73          * Build a table containing a matrix of checkbox options.
74          * The value of each option is a combination of the row tag and column tag.
75          * mParams['rows'] is an array with row labels as keys and row tags as values.
76          * mParams['columns'] is an array with column labels as keys and column tags as values.
77          *
78          * @param array $value Array of the options that should be checked
79          *
80          * @return string
81          */
82         public function getInputHTML( $value ) {
83                 $html = '';
84                 $tableContents = '';
85                 $rows = $this->mParams['rows'];
86                 $columns = $this->mParams['columns'];
87
88                 $attribs = $this->getAttributes( [ 'disabled', 'tabindex' ] );
89
90                 // Build the column headers
91                 $headerContents = Html::rawElement( 'td', [], '&#160;' );
92                 foreach ( $columns as $columnLabel => $columnTag ) {
93                         $headerContents .= Html::rawElement( 'td', [], $columnLabel );
94                 }
95                 $tableContents .= Html::rawElement( 'tr', [], "\n$headerContents\n" );
96
97                 $tooltipClass = 'mw-icon-question';
98                 if ( isset( $this->mParams['tooltip-class'] ) ) {
99                         $tooltipClass = $this->mParams['tooltip-class'];
100                 }
101
102                 // Build the options matrix
103                 foreach ( $rows as $rowLabel => $rowTag ) {
104                         // Append tooltip if configured
105                         if ( isset( $this->mParams['tooltips'][$rowLabel] ) ) {
106                                 $tooltipAttribs = [
107                                         'class' => "mw-htmlform-tooltip $tooltipClass",
108                                         'title' => $this->mParams['tooltips'][$rowLabel],
109                                 ];
110                                 $rowLabel .= ' ' . Html::element( 'span', $tooltipAttribs, '' );
111                         }
112                         $rowContents = Html::rawElement( 'td', [], $rowLabel );
113                         foreach ( $columns as $columnTag ) {
114                                 $thisTag = "$columnTag-$rowTag";
115                                 // Construct the checkbox
116                                 $thisAttribs = [
117                                         'id' => "{$this->mID}-$thisTag",
118                                         'value' => $thisTag,
119                                 ];
120                                 $checked = in_array( $thisTag, (array)$value, true );
121                                 if ( $this->isTagForcedOff( $thisTag ) ) {
122                                         $checked = false;
123                                         $thisAttribs['disabled'] = 1;
124                                 } elseif ( $this->isTagForcedOn( $thisTag ) ) {
125                                         $checked = true;
126                                         $thisAttribs['disabled'] = 1;
127                                 }
128
129                                 $checkbox = $this->getOneCheckbox( $checked, $attribs + $thisAttribs );
130
131                                 $rowContents .= Html::rawElement(
132                                         'td',
133                                         [],
134                                         $checkbox
135                                 );
136                         }
137                         $tableContents .= Html::rawElement( 'tr', [], "\n$rowContents\n" );
138                 }
139
140                 // Put it all in a table
141                 $html .= Html::rawElement( 'table',
142                                 [ 'class' => 'mw-htmlform-matrix' ],
143                                 Html::rawElement( 'tbody', [], "\n$tableContents\n" ) ) . "\n";
144
145                 return $html;
146         }
147
148         protected function getOneCheckbox( $checked, $attribs ) {
149                 if ( $this->mParent instanceof OOUIHTMLForm ) {
150                         return new OOUI\CheckboxInputWidget( [
151                                 'name' => "{$this->mName}[]",
152                                 'selected' => $checked,
153                         ] + OOUI\Element::configFromHtmlAttributes(
154                                 $attribs
155                         ) );
156                 } else {
157                         $checkbox = Xml::check( "{$this->mName}[]", $checked, $attribs );
158                         if ( $this->mParent->getConfig()->get( 'UseMediaWikiUIEverywhere' ) ) {
159                                 $checkbox = Html::openElement( 'div', [ 'class' => 'mw-ui-checkbox' ] ) .
160                                         $checkbox .
161                                         Html::element( 'label', [ 'for' => $attribs['id'] ] ) .
162                                         Html::closeElement( 'div' );
163                         }
164                         return $checkbox;
165                 }
166         }
167
168         protected function isTagForcedOff( $tag ) {
169                 return isset( $this->mParams['force-options-off'] )
170                         && in_array( $tag, $this->mParams['force-options-off'] );
171         }
172
173         protected function isTagForcedOn( $tag ) {
174                 return isset( $this->mParams['force-options-on'] )
175                         && in_array( $tag, $this->mParams['force-options-on'] );
176         }
177
178         /**
179          * Get the complete table row for the input, including help text,
180          * labels, and whatever.
181          * We override this function since the label should always be on a separate
182          * line above the options in the case of a checkbox matrix, i.e. it's always
183          * a "vertical-label".
184          *
185          * @param string $value The value to set the input to
186          *
187          * @return string Complete HTML table row
188          */
189         public function getTableRow( $value ) {
190                 list( $errors, $errorClass ) = $this->getErrorsAndErrorClass( $value );
191                 $inputHtml = $this->getInputHTML( $value );
192                 $fieldType = static::class;
193                 $helptext = $this->getHelpTextHtmlTable( $this->getHelpText() );
194                 $cellAttributes = [ 'colspan' => 2 ];
195
196                 $hideClass = '';
197                 $hideAttributes = [];
198                 if ( $this->mHideIf ) {
199                         $hideAttributes['data-hide-if'] = FormatJson::encode( $this->mHideIf );
200                         $hideClass = 'mw-htmlform-hide-if';
201                 }
202
203                 $label = $this->getLabelHtml( $cellAttributes );
204
205                 $field = Html::rawElement(
206                         'td',
207                         [ 'class' => 'mw-input' ] + $cellAttributes,
208                         $inputHtml . "\n$errors"
209                 );
210
211                 $html = Html::rawElement( 'tr',
212                         [ 'class' => "mw-htmlform-vertical-label $hideClass" ] + $hideAttributes,
213                         $label );
214                 $html .= Html::rawElement( 'tr',
215                         [ 'class' => "mw-htmlform-field-$fieldType {$this->mClass} $errorClass $hideClass" ] +
216                                 $hideAttributes,
217                         $field );
218
219                 return $html . $helptext;
220         }
221
222         /**
223          * @param WebRequest $request
224          *
225          * @return array
226          */
227         public function loadDataFromRequest( $request ) {
228                 if ( $this->isSubmitAttempt( $request ) ) {
229                         // Checkboxes are just not added to the request arrays if they're not checked,
230                         // so it's perfectly possible for there not to be an entry at all
231                         return $request->getArray( $this->mName, [] );
232                 } else {
233                         // That's ok, the user has not yet submitted the form, so show the defaults
234                         return $this->getDefault();
235                 }
236         }
237
238         public function getDefault() {
239                 if ( isset( $this->mDefault ) ) {
240                         return $this->mDefault;
241                 } else {
242                         return [];
243                 }
244         }
245
246         public function filterDataForSubmit( $data ) {
247                 $columns = HTMLFormField::flattenOptions( $this->mParams['columns'] );
248                 $rows = HTMLFormField::flattenOptions( $this->mParams['rows'] );
249                 $res = [];
250                 foreach ( $columns as $column ) {
251                         foreach ( $rows as $row ) {
252                                 // Make sure option hasn't been forced
253                                 $thisTag = "$column-$row";
254                                 if ( $this->isTagForcedOff( $thisTag ) ) {
255                                         $res[$thisTag] = false;
256                                 } elseif ( $this->isTagForcedOn( $thisTag ) ) {
257                                         $res[$thisTag] = true;
258                                 } else {
259                                         $res[$thisTag] = in_array( $thisTag, $data );
260                                 }
261                         }
262                 }
263
264                 return $res;
265         }
266 }