Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 95
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
JCTabularContentView
0.00% covered (danger)
0.00%
0 / 95
0.00% covered (danger)
0.00%
0 / 2
1560
0.00% covered (danger)
0.00%
0 / 1
 valueToHtml
0.00% covered (danger)
0.00%
0 / 90
0.00% covered (danger)
0.00%
0 / 1
1482
 getDefault
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace JsonConfig;
4
5use MediaWiki\Html\Html;
6use MediaWiki\MediaWikiServices;
7use MediaWiki\Page\PageReference;
8use MediaWiki\Parser\ParserOutput;
9use ParserOptions;
10
11/**
12 * This class is used in case when there is no custom view defined for JCContent object
13 * @package JsonConfig
14 */
15class JCTabularContentView extends JCContentView {
16
17    /**
18     * Render JCContent object as HTML
19     * Called from an override of AbstractContent::fillParserOutput()
20     *
21     * @param JCContent|JCTabularContent $content
22     * @param PageReference $page Context title for parsing
23     * @param int|null $revId Revision ID (for {{REVISIONID}})
24     * @param ParserOptions $options Parser options
25     * @param bool $generateHtml Whether or not to generate HTML
26     * @param ParserOutput &$output The output object to fill (reference).
27     * @return string
28     */
29    public function valueToHtml(
30        JCContent $content, PageReference $page, $revId,
31        ParserOptions $options, $generateHtml, ParserOutput &$output
32    ) {
33        // Use user's language, and split parser cache.  This should not have a big
34        // impact because data namespace is rarely viewed, but viewing it localized
35        // will be valuable
36        $lang = $options->getUserLangObj();
37
38        $infoClass = [ 'class' => 'mw-tabular-value-info' ];
39        $titleHeaders = [];
40        $nameHeaders = [];
41        $typeHeaders = [];
42        $rows = [];
43        $headerAttributes = [];
44
45        // Helper to add a class value to an array of attributes
46        $addErr = static function ( array $attrs, $isValid ) {
47            if ( !$isValid ) {
48                $attrs['class'] = 'mw-tabular-error';
49            }
50            return $attrs;
51        };
52
53        // Helper to create a <tr> element out of an array of raw HTML values
54        $makeRow = static function ( array $values, array $attrs = [] ) {
55            return Html::rawElement( 'tr', $attrs, implode( '', $values ) );
56        };
57
58        $dataAttrs = [ 'class' => 'mw-tabular sortable' ];
59        if ( !$content->getValidationData() || $content->getValidationData()->error() ) {
60            $dataAttrs['class'] .= ' mw-tabular-error';
61        }
62
63        $flds = $content->getField( [ 'schema', 'fields' ] );
64        if ( $flds && !$flds->error() ) {
65            foreach ( $flds->getValue() as $fld ) {
66                $name = $content->getField( 'name', $fld );
67                $nameIsValid = $name && !$name->error();
68                $name = $nameIsValid ? $name->getValue() : '';
69
70                $title = $content->getField( 'title', $fld );
71                $titleIsValid = $title && !$title->error();
72                $title =
73                    $titleIsValid ? JCUtils::pickLocalizedString( $title->getValue(), $lang, $name )
74                        : '';
75
76                $type = $content->getField( 'type', $fld );
77                $typeIsValid = $type && !$type->error();
78                $type = $typeIsValid ? $type->getValue() : '';
79
80                $thAttr = [];
81                if ( $nameIsValid ) {
82                    $thAttr['data-name'] = $name;
83                }
84                if ( $typeIsValid ) {
85                    $thAttr['data-type'] = $type;
86                    $headerAttributes[] = [ 'data-type' => $type ];
87                } else {
88                    $headerAttributes[] = [];
89                }
90
91                $nameHeaders[] = Html::element( 'th', $addErr( $thAttr, $nameIsValid ), $name );
92
93                $typeHeaders[] =
94                    Html::element( 'th', $addErr( $thAttr, $typeIsValid ),
95                        $typeIsValid ? wfMessage( 'jsonconfig-type-name-' . $type )->plain() : '' );
96
97                $titleHeaders[] = Html::element( 'th', $addErr( $thAttr, $titleIsValid ), $title );
98            }
99        }
100
101        $data = $content->getField( 'data' );
102        if ( $data && !$data->error() ) {
103            foreach ( $data->getValue() as $row ) {
104                $rowIsValid = $row && $row instanceof JCValue && !$row->error();
105                $row = ( $row && $row instanceof JCValue ) ? $row->getValue() : $row;
106                if ( !is_array( $row ) ) {
107                    continue;
108                }
109                $vals = [];
110                foreach ( $row as $column ) {
111                    $colIsValid = $column && $column instanceof JCValue && !$column->error();
112                    $column =
113                        ( $column && $column instanceof JCValue ) ? $column->getValue() : $column;
114
115                    if ( count( $vals ) >= count( $headerAttributes ) ) {
116                        $header = [];
117                    } else {
118                        $header = $headerAttributes[ count( $vals ) ];
119                    }
120
121                    if ( !$colIsValid ) {
122                        $header['class'] = 'mw-tabular-error';
123                    }
124
125                    if ( is_object( $column ) ) {
126                        $valueSize = count( (array)$column );
127                        $column =
128                            htmlspecialchars( JCUtils::pickLocalizedString( $column, $lang ) ) .
129                            Html::element( 'span', $infoClass, "($valueSize)" );
130                    } elseif ( is_bool( $column ) ) {
131                        $column = $column ? '☑' : '☐';
132                    } elseif ( $column === null ) {
133                        $header['class'] = 'mw-tabular-value-null';
134                        $column = '';
135                    } else {
136                        $column = is_string( $column ) || is_numeric( $column )
137                            ? htmlspecialchars( (string)$column )
138                            : '';
139                    }
140                    $vals[] = Html::rawElement( 'td', $header, $column );
141                }
142                $rows[] = $makeRow( $vals, $rowIsValid ? [] : [ 'class' => 'mw-tabular-error' ] );
143            }
144        }
145
146        $html =
147            $content->renderDescription( $lang ) .
148            Html::rawElement( 'table', $dataAttrs, Html::rawElement( 'thead', [], implode( "\n", [
149                    $makeRow( $nameHeaders, [ 'class' => 'mw-tabular-row-key' ] ),
150                    $makeRow( $typeHeaders, [ 'class' => 'mw-tabular-row-type' ] ),
151                    $makeRow( $titleHeaders, [ 'class' => 'mw-tabular-row-name' ] ),
152                ] ) ) . Html::rawElement( 'tbody', [], implode( "\n", $rows ) ) ) .
153            $content->renderSources(
154                MediaWikiServices::getInstance()->getParserFactory()->getInstance(),
155                $page,
156                $revId,
157                $options
158            ) . $content->renderLicense();
159
160        return $html;
161    }
162
163    /**
164     * Returns default content for this object
165     * @param string $modelId
166     * @return string
167     */
168    public function getDefault( $modelId ) {
169        $licenseIntro = JCContentView::getLicenseIntro();
170
171        return <<<EOT
172{
173    // !!!!! All comments will be automatically deleted on save !!!!!
174
175    // Optional "description" field to describe this data
176    "description": {"en": "table description"},
177
178    // Optional "sources" field to describe the sources of the data.  Can use Wiki Markup
179    "sources": "Copied from [http://example.com Example Data Source]",
180
181    $licenseIntro
182
183    // Mandatory fields schema. Each field must be an object with
184    //   "name" being a valid identifier with consisting of letters, digits, and "_"
185    //   "type" being one of the allowed types like "number", "string", "boolean", "localized"
186    "schema": {
187        "fields": [
188            {
189                "name": "header1",
190                "type": "number",
191                // Optional label for this field
192                "title": {"en": "header 1"},
193            },
194            {
195                "name": "header2",
196                "type": "string",
197                // Optional label for this field
198                "title": {"en": "header 2"},
199            }
200        ]
201    },
202
203    // array of data, with each row being an array of values
204    "data": [
205        [ 42, "peace" ]
206    ]
207}
208EOT;
209    }
210}