Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
44.53% |
57 / 128 |
|
63.64% |
7 / 11 |
CRAP | |
0.00% |
0 / 1 |
CustomIndexFieldsParser | |
44.53% |
57 / 128 |
|
63.64% |
7 / 11 |
358.56 | |
0.00% |
0 / 1 |
__construct | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
getCustomIndexFieldsConfiguration | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
loadCustomIndexFieldsConfiguration | |
0.00% |
0 / 65 |
|
0.00% |
0 / 1 |
272 | |||
parseCustomIndexFields | |
100.00% |
12 / 12 |
|
100.00% |
1 / 1 |
4 | |||
parseCustomIndexFieldsForHeader | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
parseCustomIndexFieldsForJs | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
parseCustomIndexField | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
getCustomIndexFieldForDataKey | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
3 | |||
parseCustomIndexFieldWithVariablesReplacedWithIndexEntries | |
100.00% |
14 / 14 |
|
100.00% |
1 / 1 |
3 | |||
parseCustomIndexFieldsAsTemplateParams | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
getContentLanguage | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
12 |
1 | <?php |
2 | |
3 | namespace ProofreadPage\Index; |
4 | |
5 | use FormatJson; |
6 | use OutOfBoundsException; |
7 | use RequestContext; |
8 | |
9 | /** |
10 | * @license GPL-2.0-or-later |
11 | * |
12 | * Returns the custom index entries from an IndexContent |
13 | */ |
14 | class CustomIndexFieldsParser { |
15 | |
16 | /** @var array */ |
17 | private $configuration; |
18 | |
19 | /** |
20 | * Loads custom configuration |
21 | * @param array|null $customIndexFieldsConfiguration (optional) |
22 | */ |
23 | public function __construct( array $customIndexFieldsConfiguration = null ) { |
24 | $this->configuration = ( $customIndexFieldsConfiguration === null ) |
25 | ? $this->loadCustomIndexFieldsConfiguration() |
26 | : $customIndexFieldsConfiguration; |
27 | } |
28 | |
29 | /** |
30 | * @return array the configuration |
31 | * The configuration is a list of properties like this : |
32 | * array( |
33 | * 'ID' => array( //the property id |
34 | * 'type' => 'string', // the property type (for compatibility reasons the values have not |
35 | * // to be of this type). Possible values: string, number, page |
36 | * 'size' => 1, // for type = string : the size of the form input |
37 | * 'default' => '', // the default value |
38 | * 'label' => 'ID', // the label of the property |
39 | * 'help' => '', // a short help text |
40 | * 'values' => null, // an array value => label that list the possible values |
41 | * // (for compatibility reasons the stored values have not to be one of these) |
42 | * 'header' => false, // give the content of this property to |
43 | * // MediaWiki:Proofreadpage_header_template as template parameter |
44 | * 'hidden' => false // don't show the property in the index pages form. Useful for data |
45 | * // that have always the same value (as language=en for en Wikisource) or are |
46 | * // only set at the <pages> tag level. |
47 | * ) |
48 | * ); |
49 | * NB: The values set are the default values |
50 | */ |
51 | public function getCustomIndexFieldsConfiguration() { |
52 | return $this->configuration; |
53 | } |
54 | |
55 | private function loadCustomIndexFieldsConfiguration() { |
56 | $data = wfMessage( 'proofreadpage_index_data_config.json' )->inContentLanguage(); |
57 | |
58 | // fallback to the legacy name - T263094 |
59 | if ( !( $data->exists() && $data->plain() != '' ) ) { |
60 | $data = wfMessage( 'proofreadpage_index_data_config' )->inContentLanguage(); |
61 | } |
62 | |
63 | if ( $data->exists() && $data->plain() != '' ) { |
64 | $config = FormatJson::decode( $data->plain(), true ); |
65 | if ( $config === null ) { |
66 | RequestContext::getMain()->getOutput()->showErrorPage( |
67 | 'proofreadpage_dataconfig_badformatted', |
68 | 'proofreadpage_dataconfig_badformattedtext' |
69 | ); |
70 | $config = []; |
71 | } |
72 | } else { |
73 | $attributes = explode( "\n", wfMessage( 'proofreadpage_index_attributes' ) |
74 | ->inContentLanguage()->text() ); |
75 | $headerAttributes = explode( ' ', wfMessage( 'proofreadpage_js_attributes' ) |
76 | ->inContentLanguage()->text() ); |
77 | $config = []; |
78 | foreach ( $attributes as $attribute ) { |
79 | $m = explode( '|', $attribute ); |
80 | $params = [ |
81 | 'type' => 'string', |
82 | 'size' => 1, |
83 | 'default' => '', |
84 | 'label' => $m[0], |
85 | 'help' => '', |
86 | 'values' => null, |
87 | 'header' => false |
88 | ]; |
89 | |
90 | if ( $m[0] == 'Header' ) { |
91 | $params['default'] = wfMessage( 'proofreadpage_default_header' ) |
92 | ->inContentLanguage()->plain(); |
93 | } |
94 | if ( $m[0] == 'Footer' ) { |
95 | $params['default'] = wfMessage( 'proofreadpage_default_footer' ) |
96 | ->inContentLanguage()->plain(); |
97 | } |
98 | if ( isset( $m[1] ) && $m[1] !== '' ) { |
99 | $params['label'] = $m[1]; |
100 | } |
101 | if ( isset( $m[2] ) ) { |
102 | $params['size'] = intval( $m[2] ); |
103 | } |
104 | $config[$m[0]] = $params; |
105 | } |
106 | |
107 | foreach ( $headerAttributes as $attribute ) { |
108 | if ( isset( $config[$attribute] ) ) { |
109 | $config[$attribute]['header'] = true; |
110 | } else { |
111 | $config[$attribute] = [ |
112 | 'type' => 'string', |
113 | 'size' => 1, |
114 | 'default' => '', |
115 | 'label' => $attribute, |
116 | 'values' => null, |
117 | 'header' => true, |
118 | 'hidden' => true |
119 | ]; |
120 | } |
121 | } |
122 | } |
123 | |
124 | if ( !array_key_exists( 'Header', $config ) ) { |
125 | $config['Header'] = [ |
126 | 'default' => wfMessage( 'proofreadpage_default_header' ) |
127 | ->inContentLanguage()->plain(), |
128 | 'header' => true, |
129 | 'hidden' => true |
130 | ]; |
131 | } |
132 | if ( !array_key_exists( 'Footer', $config ) ) { |
133 | $config['Footer'] = [ |
134 | 'default' => wfMessage( 'proofreadpage_default_footer' ) |
135 | ->inContentLanguage()->plain(), |
136 | 'header' => true, |
137 | 'hidden' => true |
138 | ]; |
139 | } |
140 | |
141 | return $config; |
142 | } |
143 | |
144 | /** |
145 | * @param IndexContent $content |
146 | * @return CustomIndexField[] |
147 | */ |
148 | public function parseCustomIndexFields( IndexContent $content ) { |
149 | $contentFields = []; |
150 | foreach ( $content->getFields() as $key => $value ) { |
151 | $contentFields[strtolower( $key )] = $value; |
152 | } |
153 | |
154 | $values = []; |
155 | foreach ( $this->configuration as $varName => $property ) { |
156 | $key = strtolower( $varName ); |
157 | if ( array_key_exists( $key, $contentFields ) ) { |
158 | $values[$varName] = new CustomIndexField( |
159 | $varName, $contentFields[$key]->getText(), $property |
160 | ); |
161 | } else { |
162 | $values[$varName] = new CustomIndexField( $varName, '', $property ); |
163 | } |
164 | } |
165 | return $values; |
166 | } |
167 | |
168 | /** |
169 | * Return metadata from the index page that have to be given to header template. |
170 | * @param IndexContent $content |
171 | * @return CustomIndexField[] |
172 | */ |
173 | public function parseCustomIndexFieldsForHeader( IndexContent $content ) { |
174 | $entries = $this->parseCustomIndexFields( $content ); |
175 | $headerEntries = []; |
176 | foreach ( $entries as $entry ) { |
177 | if ( $entry->isHeader() ) { |
178 | $headerEntries[$entry->getKey()] = $entry; |
179 | } |
180 | } |
181 | return $headerEntries; |
182 | } |
183 | |
184 | /** |
185 | * Returns index entries that are to be exposed via mw.config |
186 | * @param IndexContent $content |
187 | * @return CustomIndexField[] |
188 | */ |
189 | public function parseCustomIndexFieldsForJs( IndexContent $content ) { |
190 | $indexFields = $this->parseCustomIndexFields( $content ); |
191 | $jsIndexFields = []; |
192 | foreach ( $indexFields as $field ) { |
193 | if ( $field->isAllowedInJs() ) { |
194 | $jsIndexFields[$field->getKey()] = $field; |
195 | } |
196 | } |
197 | return $jsIndexFields; |
198 | } |
199 | |
200 | /** |
201 | * Return the index entry with the same name or null if it's not found |
202 | * Note: the comparison is case insensitive |
203 | * @param IndexContent $content |
204 | * @param string $fieldName |
205 | * @return CustomIndexField |
206 | * @throws OutOfBoundsException |
207 | */ |
208 | public function parseCustomIndexField( IndexContent $content, $fieldName ) { |
209 | $fieldName = strtolower( $fieldName ); |
210 | $entries = $this->parseCustomIndexFields( $content ); |
211 | foreach ( $entries as $entry ) { |
212 | if ( strtolower( $entry->getKey() ) === $fieldName ) { |
213 | return $entry; |
214 | } |
215 | } |
216 | throw new OutOfBoundsException( 'Custom index entry ' . $fieldName . ' does not exist.' ); |
217 | } |
218 | |
219 | /** |
220 | * Return the index entry with the same "data" attribute value |
221 | * |
222 | * @param IndexContent $content |
223 | * @param string $dataKey |
224 | * @return CustomIndexField |
225 | * @throws OutOfBoundsException |
226 | */ |
227 | public function getCustomIndexFieldForDataKey( IndexContent $content, string $dataKey ): CustomIndexField { |
228 | $fieldName = strtolower( $dataKey ); |
229 | $entries = $this->parseCustomIndexFields( $content ); |
230 | foreach ( $entries as $entry ) { |
231 | if ( $entry->getData() === $fieldName ) { |
232 | return $entry; |
233 | } |
234 | } |
235 | throw new OutOfBoundsException( 'Custom index entry ' . $fieldName . ' does not exist.' ); |
236 | } |
237 | |
238 | /** |
239 | * Return the value of an entry as wikitext with variable replaced with index entries and |
240 | * $otherParams |
241 | * Example: if 'header' entry is 'Page of {{title}} number {{pagenum}}' with |
242 | * $otherParams = array( 'pagenum' => 23 ) |
243 | * the function called for 'header' will returns 'Page page my book number 23' |
244 | * @param IndexContent $content |
245 | * @param string $fieldName |
246 | * @param array $otherParams associative array other possible values to replace |
247 | * @return string the value with variables replaced |
248 | * @throws OutOfBoundsException |
249 | */ |
250 | public function parseCustomIndexFieldWithVariablesReplacedWithIndexEntries( |
251 | IndexContent $content, $fieldName, array $otherParams |
252 | ) { |
253 | $entry = $this->parseCustomIndexField( $content, $fieldName ); |
254 | |
255 | // we can't use the parser here because it replace tags like <references /> by strange UIDs |
256 | $params = $this->parseCustomIndexFieldsAsTemplateParams( $content ) + $otherParams; |
257 | return preg_replace_callback( |
258 | '/{\{\{(.*)(\|(.*))?\}\}\}/U', |
259 | static function ( $matches ) use ( $params ) { |
260 | $paramKey = trim( strtolower( $matches[1] ) ); |
261 | if ( array_key_exists( $paramKey, $params ) ) { |
262 | return $params[$paramKey]; |
263 | } elseif ( array_key_exists( 3, $matches ) ) { |
264 | return trim( $matches[3] ); |
265 | } else { |
266 | return $matches[0]; |
267 | } |
268 | }, |
269 | $entry->getStringValue() |
270 | ); |
271 | } |
272 | |
273 | /** |
274 | * Returns the index entries formatted in order to be transcluded in templates |
275 | * |
276 | * @param IndexContent $content |
277 | * @return string[] |
278 | */ |
279 | private function parseCustomIndexFieldsAsTemplateParams( IndexContent $content ) { |
280 | $indexEntries = $this->parseCustomIndexFieldsForHeader( $content ); |
281 | $params = []; |
282 | foreach ( $indexEntries as $entry ) { |
283 | $params[strtolower( $entry->getKey() )] = $entry->getStringValue(); |
284 | } |
285 | return $params; |
286 | } |
287 | |
288 | /** |
289 | * @param IndexContent $content |
290 | * @return string|null |
291 | */ |
292 | public function getContentLanguage( IndexContent $content ) { |
293 | try { |
294 | $entry = $this->getCustomIndexFieldForDataKey( $content, 'language' ); |
295 | return $entry->getType() === 'langcode' ? $entry->getStringValue() : null; |
296 | } catch ( OutOfBoundsException $e ) { |
297 | return null; |
298 | } |
299 | } |
300 | |
301 | } |