Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
CargoDynamicTableFormat
0.00% covered (danger)
0.00%
0 / 122
0.00% covered (danger)
0.00%
0 / 2
2070
0.00% covered (danger)
0.00%
0 / 1
 allowedParameters
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 display
0.00% covered (danger)
0.00%
0 / 115
0.00% covered (danger)
0.00%
0 / 1
1980
1<?php
2/**
3 * Defines a "dynamic table" format, that displays query results in a
4 * JavaScript-based table that has sorting, pagination and searching, using
5 * the DataTables JS library.
6 *
7 * @author Yaron Koren
8 * @ingroup Cargo
9 */
10
11use MediaWiki\Html\Html;
12
13class CargoDynamicTableFormat extends CargoDisplayFormat {
14
15    public static function allowedParameters() {
16        return [
17            'rows per page' => [ 'type' => 'int' ],
18            'details fields' => [ 'type' => 'string' ],
19            'hidden fields' => [ 'type' => 'string' ],
20            'column widths' => [ 'type' => 'string' ],
21            'header tooltips' => [ 'type' => 'string' ]
22        ];
23    }
24
25    /**
26     * @param array $valuesTable Unused
27     * @param array $formattedValuesTable
28     * @param array $fieldDescriptions
29     * @param array $displayParams Unused
30     * @return string HTML
31     */
32    public function display( $valuesTable, $formattedValuesTable, $fieldDescriptions, $displayParams ) {
33        $this->mOutput->addModules( [ 'ext.cargo.datatables' ] );
34
35        $tableAttrs = [ 'class' => 'cargoDynamicTable display', 'cellspacing' => '0', 'width' => '100%' ];
36
37        $detailsFields = [];
38        if ( array_key_exists( 'details fields', $displayParams ) && !empty( $displayParams[ 'details fields' ] ) ) {
39            $detailsFields = explode( ',', $displayParams['details fields'] );
40            // The field names in the $fieldDescriptions lack table names, and they
41            // have spaces instead of underscores. Since we need to compare these
42            // values to those, get the $detailsFields values in the same format.
43            foreach ( $detailsFields as &$detailsField ) {
44                $locOfDot = strpos( $detailsField, '.' );
45                if ( $locOfDot !== false ) {
46                    $detailsField = substr( $detailsField, $locOfDot + 1 );
47                }
48                $detailsField = trim( $detailsField );
49                if ( strpos( $detailsField, '_' ) > 0 ) {
50                    $detailsField = str_replace( '_', ' ', $detailsField );
51                }
52            }
53            $tableAttrs['data-details-fields'] = "1";
54        }
55        // Special handlng for ordering.
56        $dataTableOrderByParams = [];
57        if ( array_key_exists( 'order by', $displayParams ) ) {
58            $orderByClauses = explode( ',', $displayParams['order by'] );
59            foreach ( $orderByClauses as $orderByClause ) {
60                $orderByClause = strtolower( trim( $orderByClause ) );
61                $sortAscending = true;
62                if ( substr( $orderByClause, -4 ) === ' asc' ) {
63                    $orderByClause = trim( substr( $orderByClause, 0, -3 ) );
64                } elseif ( substr( $orderByClause, -5 ) === ' desc' ) {
65                    $sortAscending = false;
66                    $orderByClause = trim( substr( $orderByClause, 0, -4 ) );
67                }
68                if ( $detailsFields ) {
69                    $i = 1;
70                } else {
71                    $i = 0;
72                }
73                foreach ( $fieldDescriptions as $fieldName => $fieldDescription ) {
74                    if ( in_array( $fieldName, $detailsFields ) ) {
75                        continue;
76                    }
77                    $fieldName = strtolower( str_replace( ' ', '_', $fieldName ) );
78                    if ( $orderByClause == $fieldName ) {
79                        $dataTableOrderByParams[] = [ $i, $sortAscending ? 'asc' : 'desc' ];
80                    }
81                    $i++;
82                }
83            }
84        }
85        // We have to set the text in this awkward way,
86        // instead of using the Html class, because it
87        // has to be displayed in a very specific way -
88        // single quotes outside, double quotes inside -
89        // for the jQuery part to work, and the Html
90        // class won't do it that way.
91        $tableAttrs['data-order'] = json_encode( $dataTableOrderByParams );
92
93        if ( array_key_exists( 'rows per page', $displayParams ) && $displayParams['rows per page'] != '' ) {
94            $tableAttrs['data-page-length'] = $displayParams['rows per page'];
95        }
96        $text = '';
97        if ( array_key_exists( 'column widths', $displayParams ) ) {
98            if ( trim( $displayParams['column widths'] ) != '' ) {
99                $tableAttrs['data-widths'] = $displayParams['column widths'];
100            }
101        }
102        if ( array_key_exists( 'header tooltips', $displayParams ) ) {
103            if ( trim( $displayParams['header tooltips'] ) != '' ) {
104                $tableAttrs['data-tooltips'] = $displayParams['header tooltips'];
105            }
106        }
107        if ( array_key_exists( 'hidden fields', $displayParams ) ) {
108            $hiddenFields = array_map( 'trim', explode( ',', $displayParams['hidden fields'] ) );
109            $text .= wfMessage( 'cargo-dynamictables-togglecolumns' )->escaped() . ' ';
110            $matchFound = 0;
111            foreach ( $hiddenFields as $hiddenField ) {
112                if ( $detailsFields ) {
113                    $fieldNum = 1;
114                } else {
115                    $fieldNum = 0;
116                }
117                foreach ( $fieldDescriptions as $fieldName => $fieldDescription ) {
118                    if ( in_array( $fieldName, $detailsFields ) ) {
119                        continue;
120                    }
121                    if ( $hiddenField == $fieldName ) {
122                        if ( $matchFound++ > 0 ) {
123                            $text .= ' - ';
124                        }
125                        $text .= Html::element( 'a', [
126                            'class' => 'toggle-vis',
127                            'data-column' => $fieldNum,
128                        ], $hiddenField );
129                        break;
130                    }
131                    $fieldNum++;
132                }
133            }
134        }
135        $searchableColumns = false;
136        if ( array_key_exists( 'searchable columns', $displayParams ) ) {
137            $searchableColumns = strtolower( $displayParams['searchable columns'] ) == 'yes';
138        }
139        $tableContents = '<thead><tr>';
140        if ( $detailsFields ) {
141            $tableContents .= Html::rawElement( 'th', [ 'class' => 'details-control' ], null );
142        }
143        foreach ( $fieldDescriptions as $fieldName => $fieldDescription ) {
144            if ( in_array( $fieldName, $detailsFields ) ) {
145                continue;
146            }
147            if ( strpos( $fieldName, 'Blank value ' ) === false ) {
148                $tableContents .= "\t\t\t\t" . Html::element( 'th', null, $fieldName );
149            } else {
150                $tableContents .= "\t\t\t\t" . Html::element( 'th', null, null );
151            }
152        }
153
154        $tableContents .= '</tr></thead><tfoot><tr>';
155
156        if ( $detailsFields ) {
157            $tableContents .= Html::rawElement( 'th', [ 'class' => 'details-control' ], null );
158        }
159        foreach ( $fieldDescriptions as $fieldName => $fieldDescription ) {
160            if ( in_array( $fieldName, $detailsFields ) ) {
161                continue;
162            }
163            if ( $searchableColumns ) {
164                $placeholder = wfMessage( 'cargo-dynamictables-searchcolumn', $fieldName )->escaped();
165                $attribs = [ 'data-placeholder' => $placeholder ];
166            } else {
167                $attribs = null;
168            }
169            if ( strpos( $fieldName, 'Blank value ' ) === false ) {
170                $tableContents .= "\t\t\t\t" . Html::element( 'th', $attribs, $fieldName );
171            } else {
172                $tableContents .= "\t\t\t\t" . Html::element( 'th', $attribs, null );
173            }
174        }
175
176        $tableContents .= '</tr></tfoot><tbody>';
177
178        foreach ( $formattedValuesTable as $rowNum => $row ) {
179            if ( $detailsFields ) {
180                $tableData = Html::rawElement( 'td', [ 'class' => 'details-control' ], null );
181            } else {
182                $tableData = '';
183            }
184            $details = '';
185            foreach ( $fieldDescriptions as $field => $fieldDescription ) {
186                $attribs = null;
187                $value = null;
188
189                if ( array_key_exists( $field, $row ) ) {
190                    $value = $row[$field];
191                    if ( $fieldDescription->isDateOrDatetime() ) {
192                        $attribs = [ 'data-order' => $valuesTable[$rowNum][$field] ];
193                    }
194                }
195
196                if ( in_array( $field, $detailsFields ) ) {
197                    $detailsText = "\t\t\t\t" . Html::rawElement( 'td', $attribs, "<strong>$field: </strong>" );
198                    $detailsText .= "\t\t\t\t" . Html::rawElement( 'td', $attribs, $value );
199                    $details .= "\t\t\t" . Html::rawElement( 'tr', $attribs, $detailsText );
200                } else {
201                    $tableData .= "\t\t\t\t" . Html::rawElement( 'td', $attribs, $value );
202                }
203            }
204            $detailsTable =
205                Html::rawElement( 'table', [ 'border' => '0', 'cellspacing' => '0' ],
206                    Html::rawElement( 'tbody', null, $details ) );
207
208            $tableContents .= Html::rawElement( 'tr', [ 'data-details' => $detailsTable ],
209                $tableData );
210        }
211
212        $tableContents .= '</tbody>';
213
214        $text .= Html::rawElement( 'table', $tableAttrs, $tableContents );
215
216        return $text;
217    }
218
219}