Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
CargoTableFormat
0.00% covered (danger)
0.00%
0 / 93
0.00% covered (danger)
0.00%
0 / 3
1482
0.00% covered (danger)
0.00%
0 / 1
 allowedParameters
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 getHelperDataForMerging
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
132
 display
0.00% covered (danger)
0.00%
0 / 67
0.00% covered (danger)
0.00%
0 / 1
702
1<?php
2/**
3 * @author Yaron Koren
4 * @ingroup Cargo
5 */
6
7use MediaWiki\Html\Html;
8use MediaWiki\MediaWikiServices;
9use MediaWiki\Title\Title;
10
11class CargoTableFormat extends CargoDisplayFormat {
12
13    public static function allowedParameters() {
14        return [
15            'merge similar cells' => [ 'type' => 'boolean' ],
16            'edit link' => [ 'type' => 'boolean' ],
17        ];
18    }
19
20    /**
21     * Creates helper data structures that make merging cells
22     * easier, if it's going to be done.
23     */
24    private function getHelperDataForMerging( $formattedValuesTable ) {
25        $duplicateValuesInTable = [];
26        $blankedCells = [];
27        $numRows = count( $formattedValuesTable );
28        foreach ( $formattedValuesTable as $rowNum => $row ) {
29            foreach ( $row as $columnNum => $value ) {
30                if ( strpos( $columnNum, '__' ) !== false ) {
31                    continue;
32                }
33                if ( array_key_exists( $rowNum, $blankedCells ) && in_array( $columnNum, $blankedCells[$rowNum] ) ) {
34                    continue;
35                }
36                $numMatches = 0;
37                $nextRowNum = $rowNum;
38                while (
39                    ( ++$nextRowNum < $numRows ) &&
40                    ( $formattedValuesTable[$nextRowNum][$columnNum] == $value )
41                ) {
42                    $numMatches++;
43                    if ( !array_key_exists( $nextRowNum, $blankedCells ) ) {
44                        $blankedCells[$nextRowNum] = [];
45                    }
46                    $blankedCells[$nextRowNum][] = $columnNum;
47                }
48                if ( $numMatches > 0 ) {
49                    if ( !array_key_exists( $rowNum, $duplicateValuesInTable ) ) {
50                        $duplicateValuesInTable[$rowNum] = [];
51                    }
52                    $duplicateValuesInTable[$rowNum][$columnNum] = $numMatches + 1;
53                }
54            }
55        }
56
57        return [ $duplicateValuesInTable, $blankedCells ];
58    }
59
60    /**
61     * @param array $valuesTable Unused
62     * @param array $formattedValuesTable
63     * @param array $fieldDescriptions
64     * @param array $displayParams Unused
65     * @return string HTML
66     */
67    public function display( $valuesTable, $formattedValuesTable, $fieldDescriptions, $displayParams ) {
68        $this->mOutput->addModules( [ 'ext.cargo.main', 'jquery.tablesorter' ] );
69        $this->mOutput->addModuleStyles( [ 'jquery.tablesorter.styles' ] );
70
71        $mergeSimilarCells = false;
72        if ( array_key_exists( 'merge similar cells', $displayParams ) ) {
73            $mergeSimilarCells = strtolower( $displayParams['merge similar cells'] ) == 'yes';
74        }
75        $editLink = false;
76        $linkRenderer = null;
77        if ( array_key_exists( 'edit link', $displayParams ) ) {
78            $editLink = strtolower( $displayParams['edit link'] ) == 'yes';
79            $linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
80        }
81
82        $tableClass = 'cargoTable';
83        if ( $mergeSimilarCells ) {
84            $tableClass .= ' mergeSimilarCells';
85        } else {
86            $tableClass .= ' noMerge sortable';
87        }
88
89        $text = "<table class=\"$tableClass\">";
90        $text .= '<thead><tr>';
91        foreach ( array_keys( $fieldDescriptions ) as $field ) {
92            if ( strpos( $field, 'Blank value ' ) === false ) {
93                // We add a class to enable special CSS and/or
94                // JS handling.
95                $className = 'field_' . str_replace( ' ', '_', $field );
96                $text .= Html::rawElement( 'th', [ 'class' => $className ], $field ) . "\n";
97            }
98        }
99        $text .= "</tr></thead>\n<tbody>";
100
101        if ( $mergeSimilarCells ) {
102            [ $duplicateValuesInTable, $blankedCells ] = $this->getHelperDataForMerging( $formattedValuesTable );
103        } else {
104            $duplicateValuesInTable = [];
105            $blankedCells = [];
106        }
107
108        $columnIsOdd = [];
109
110        foreach ( $formattedValuesTable as $rowNum => $row ) {
111            $text .= "<tr>\n";
112            foreach ( array_keys( $fieldDescriptions ) as $field ) {
113                if (
114                    $mergeSimilarCells &&
115                    array_key_exists( $rowNum, $blankedCells ) &&
116                    in_array( $field, $blankedCells[$rowNum] )
117                ) {
118                    continue;
119                }
120
121                if ( !array_key_exists( $field, $columnIsOdd ) ) {
122                    $columnIsOdd[$field] = true;
123                }
124
125                // Add a unique class to enable special CSS
126                // and/or JS handling.
127                $className = 'field_' . str_replace( ' ', '_', $field );
128
129                if ( $mergeSimilarCells ) {
130                    // If there are merged cells, we can't
131                    // use the standard "nth-child" CSS
132                    // approach, so add a class to indicate
133                    // whether this is an odd or even row.
134                    if ( $columnIsOdd[$field] ) {
135                        $className .= ' odd';
136                        $columnIsOdd[$field] = false;
137                    } else {
138                        $className .= ' even';
139                        $columnIsOdd[$field] = true;
140                    }
141                }
142
143                $attrs = [ 'class' => $className ];
144                if (
145                    $mergeSimilarCells &&
146                    array_key_exists( $rowNum, $duplicateValuesInTable ) &&
147                    array_key_exists( $field, $duplicateValuesInTable[$rowNum] )
148                ) {
149                    $attrs['rowspan'] = $duplicateValuesInTable[$rowNum][$field];
150                }
151
152                if ( array_key_exists( $field, $row ) ) {
153                    $value = $row[$field];
154                    if ( $fieldDescriptions[$field]->isDateOrDatetime() ) {
155                        $attrs['data-sort-value'] = $valuesTable[$rowNum][$field];
156                    }
157                } else {
158                    $value = null;
159                }
160
161                $valueText = $value == null ? '' : $value;
162
163                // Cargo reuses the "Page" system message of the main namespace
164                $pageMsg = wfMessage( "nstab-main" )->text();
165                if ( ( $field == "Page" || $field == $pageMsg || $field == '_pageName' ) &&
166                $editLink ) {
167                    $rawValue = $valuesTable[$rowNum][$field];
168                    $title = Title::newFromText( $rawValue );
169                    if ( $title === null ) {
170                        continue;
171                    }
172                    $link = CargoUtils::makeLink(
173                        $linkRenderer, $title, wfMessage( 'editold' )->escaped(), [], [ 'action' => 'edit', ]
174                    );
175                    $valueText .= ' (' . $link . ')';
176                }
177
178                $text .= Html::rawElement( 'td', $attrs, $valueText ) . "\n";
179            }
180            $text .= "</tr>\n";
181        }
182        $text .= "</tbody></table>";
183        return $text;
184    }
185
186}