Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.56% |
68 / 90 |
|
50.00% |
2 / 4 |
CRAP | |
0.00% |
0 / 1 |
JCTabularContent | |
75.56% |
68 / 90 |
|
50.00% |
2 / 4 |
37.65 | |
0.00% |
0 / 1 |
createDefaultView | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getWikitextForTransclusion | |
0.00% |
0 / 21 |
|
0.00% |
0 / 1 |
30 | |||
validateContent | |
100.00% |
44 / 44 |
|
100.00% |
1 / 1 |
14 | |||
localizeData | |
100.00% |
24 / 24 |
|
100.00% |
1 / 1 |
7 |
1 | <?php |
2 | |
3 | namespace JsonConfig; |
4 | |
5 | use MediaWiki\Language\Language; |
6 | |
7 | class JCTabularContent extends JCDataContent { |
8 | |
9 | protected function createDefaultView() { |
10 | return new JCTabularContentView(); |
11 | } |
12 | |
13 | /** |
14 | * Returns wiki-table representation of the tabular data |
15 | * |
16 | * @return string|bool The raw text, or false if the conversion failed. |
17 | */ |
18 | public function getWikitextForTransclusion() { |
19 | $toWiki = static function ( $value ) { |
20 | if ( is_object( $value ) ) { |
21 | global $wgLang; |
22 | $value = JCUtils::pickLocalizedString( $value, $wgLang ); |
23 | } |
24 | if ( preg_match( '/^[ .\pL\pN]*$/i', $value ) ) { |
25 | // Optimization: spaces, letters, numbers, and dots are returned without <nowiki> |
26 | return $value; |
27 | } |
28 | return '<nowiki>' . htmlspecialchars( $value ) . '</nowiki>'; |
29 | }; |
30 | |
31 | $data = $this->getData(); |
32 | $result = "{| class='wikitable sortable'\n"; |
33 | |
34 | // Create header |
35 | $result .= '!' . implode( "!!", |
36 | array_map( |
37 | static function ( $field ) use ( $toWiki ) { |
38 | return $toWiki( $field->title ? : $field->name ); |
39 | }, |
40 | $data->schema->fields |
41 | ) |
42 | ) . "\n"; |
43 | |
44 | // Create table content |
45 | foreach ( $data->data as $row ) { |
46 | $result .= "|-\n|" . implode( '||', array_map( $toWiki, $row ) ) . "\n"; |
47 | } |
48 | |
49 | $result .= "\n|}\n"; |
50 | |
51 | return $result; |
52 | } |
53 | |
54 | /** |
55 | * Derived classes must implement this method to perform custom validation |
56 | * using the check(...) calls. |
57 | * |
58 | * This should be kept compatible with mw.JsonConfig.JsonEditDialog validation |
59 | */ |
60 | public function validateContent() { |
61 | parent::validateContent(); |
62 | |
63 | $validators = [ JCValidators::isList() ]; |
64 | $typeValidators = []; |
65 | $fieldsPath = [ 'schema', 'fields' ]; |
66 | if ( $this->test( 'schema', JCValidators::isDictionary() ) && |
67 | $this->test( $fieldsPath, JCValidators::isList() ) && |
68 | $this->testEach( $fieldsPath, JCValidators::isDictionary() ) |
69 | ) { |
70 | $hasError = false; |
71 | $allHeaders = []; |
72 | $fieldCount = count( $this->getField( $fieldsPath )->getValue() ); |
73 | for ( $idx = 0; $idx < $fieldCount; $idx++ ) { |
74 | $header = false; |
75 | $hasError = !$this->test( [ 'schema', 'fields', $idx, 'name' ], |
76 | JCValidators::isHeaderString( $allHeaders ), |
77 | static function ( JCValue $jcv ) use ( &$header ) { |
78 | $header = $jcv->getValue(); |
79 | return true; |
80 | } ) || $hasError; |
81 | $hasError = !$this->test( [ 'schema', 'fields', $idx, 'type' ], |
82 | JCValidators::validateDataType( $typeValidators ) ) || $hasError; |
83 | if ( $header ) { |
84 | $hasError = !$this->testOptional( [ 'schema', 'fields', $idx, 'title' ], |
85 | static function () use ( $header ) { |
86 | return (object)[ 'en' => $header ]; |
87 | }, JCValidators::isLocalizedString() ) || $hasError; |
88 | } |
89 | } |
90 | $countValidator = JCValidators::checkListSize( $fieldCount, 'schema/fields' ); |
91 | $validators[] = $countValidator; |
92 | |
93 | if ( !$hasError ) { |
94 | $this->testEach( $fieldsPath, JCValidators::noExtraValues() ); |
95 | } |
96 | } |
97 | $this->test( 'schema', JCValidators::noExtraValues() ); |
98 | |
99 | if ( !$this->thorough() ) { |
100 | // We are not doing any modifications to the data, so no need to validate it |
101 | return; |
102 | } |
103 | |
104 | $this->test( 'data', JCValidators::isList() ); |
105 | $this->test( [], JCValidators::noExtraValues() ); |
106 | $this->testEach( 'data', $validators ); |
107 | if ( $typeValidators ) { |
108 | /** @noinspection PhpUnusedParameterInspection */ |
109 | $this->testEach( 'data', function ( JCValue $v, array $path ) use ( $typeValidators ) { |
110 | $isOk = true; |
111 | $lastIdx = count( $path ); |
112 | foreach ( array_keys( $typeValidators ) as $k ) { |
113 | $path[$lastIdx] = $k; |
114 | $isOk = $this->test( $path, $typeValidators[$k] ) && $isOk; |
115 | } |
116 | return $isOk; |
117 | } ); |
118 | } |
119 | } |
120 | |
121 | /** |
122 | * Resolve any override-specific localizations, and add it to $result |
123 | * @param \stdClass $result |
124 | * @param Language $lang |
125 | */ |
126 | protected function localizeData( $result, Language $lang ) { |
127 | parent::localizeData( $result, $lang ); |
128 | |
129 | $data = $this->getData(); |
130 | $localize = static function ( $value ) use ( $lang ) { |
131 | return JCUtils::pickLocalizedString( $value, $lang ); |
132 | }; |
133 | |
134 | $isLocalized = []; |
135 | $result->schema = (object)[]; |
136 | $result->schema->fields = []; |
137 | foreach ( $data->schema->fields as $ind => $fld ) { |
138 | if ( $fld->type === 'localized' ) { |
139 | $isLocalized[] = $ind; |
140 | } |
141 | $result->schema->fields[] = (object)[ |
142 | 'name' => $fld->name, |
143 | 'type' => $fld->type, |
144 | 'title' => property_exists( $fld, 'title' ) ? $localize( $fld->title ) : $fld->name |
145 | ]; |
146 | } |
147 | |
148 | if ( !$isLocalized ) { |
149 | // There are no localized strings in the data, optimize |
150 | $result->data = $data->data; |
151 | } else { |
152 | $result->data = array_map( static function ( $row ) use ( $localize, $isLocalized ) { |
153 | foreach ( $isLocalized as $ind ) { |
154 | if ( $row[$ind] !== null ) { |
155 | $row[$ind] = $localize( $row[$ind] ); |
156 | } |
157 | } |
158 | return $row; |
159 | }, $data->data ); |
160 | } |
161 | } |
162 | } |