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