Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
98.75% |
79 / 80 |
|
85.71% |
6 / 7 |
CRAP | |
0.00% |
0 / 1 |
ConfigParser | |
98.75% |
79 / 80 |
|
85.71% |
6 / 7 |
31 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
getParsedConfig | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
2 | |||
getTemplates | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
parseConfig | |
100.00% |
46 / 46 |
|
100.00% |
1 / 1 |
17 | |||
parseArrayValues | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
5 | |||
parseValue | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
1 | |||
updateTemplates | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\MediaUploader\Config; |
4 | |
5 | use MediaWiki\Page\PageReference; |
6 | use Parser; |
7 | use ParserFactory; |
8 | use ParserOptions; |
9 | use ParserOutput; |
10 | use Title; |
11 | |
12 | /** |
13 | * Class responsible for parsing wikitext in MediaUploader's config. |
14 | * This applies both to the main "global" config and campaigns. |
15 | */ |
16 | class ConfigParser { |
17 | |
18 | /** @var ParserFactory */ |
19 | private $parserFactory; |
20 | |
21 | /** @var ParserOptions */ |
22 | private $parserOptions; |
23 | |
24 | /** @var Title */ |
25 | private $pageRef; |
26 | |
27 | /** @var array */ |
28 | private $unparsedConfig; |
29 | |
30 | /** @var array|null */ |
31 | private $parsedConfig = null; |
32 | |
33 | /** @var array */ |
34 | private $templates = []; |
35 | |
36 | /** |
37 | * @param PageReference $pageRef campaign page or title of Special:MediaUploader |
38 | * if not parsing a campaign. |
39 | * @param ParserFactory $parserFactory |
40 | * @param ParserOptions $parserOptions |
41 | * @param array $configToParse |
42 | * |
43 | * @internal Only for use by ConfigParserFactory |
44 | */ |
45 | public function __construct( |
46 | PageReference $pageRef, |
47 | ParserFactory $parserFactory, |
48 | ParserOptions $parserOptions, |
49 | array $configToParse |
50 | ) { |
51 | $options = clone $parserOptions; |
52 | // Open links in a new tab for slightly better UX |
53 | $options->setOption( 'externalLinkTarget', '_blank' ); |
54 | |
55 | $this->pageRef = $pageRef; |
56 | $this->parserFactory = $parserFactory; |
57 | $this->parserOptions = $options; |
58 | $this->unparsedConfig = $configToParse; |
59 | } |
60 | |
61 | /** |
62 | * Returns the parsed config array. |
63 | * |
64 | * @return array |
65 | */ |
66 | public function getParsedConfig(): array { |
67 | if ( $this->parsedConfig === null ) { |
68 | $this->parseConfig(); |
69 | } |
70 | return $this->parsedConfig; |
71 | } |
72 | |
73 | /** |
74 | * Returns the templates used in this config |
75 | * |
76 | * @return array [ ns => [ dbk => [ page_id, rev_id ] ] ] |
77 | */ |
78 | public function getTemplates(): array { |
79 | if ( $this->parsedConfig === null ) { |
80 | $this->parseConfig(); |
81 | } |
82 | return $this->templates; |
83 | } |
84 | |
85 | /** |
86 | * Does the actual parsing work. |
87 | * |
88 | * @return void |
89 | */ |
90 | private function parseConfig(): void { |
91 | $parsedConfig = []; |
92 | foreach ( $this->unparsedConfig as $key => $value ) { |
93 | switch ( $key ) { |
94 | case 'title': |
95 | case 'description': |
96 | $parsedConfig[$key] = $this->parseValue( $value ); |
97 | break; |
98 | case 'display': |
99 | foreach ( $value as $option => $optionValue ) { |
100 | if ( is_array( $optionValue ) ) { |
101 | $parsedConfig['display'][$option] = $this->parseArrayValues( |
102 | $optionValue, |
103 | [ 'label' ] |
104 | ); |
105 | } else { |
106 | $parsedConfig['display'][$option] = $this->parseValue( $optionValue ); |
107 | } |
108 | } |
109 | break; |
110 | case 'tutorial': |
111 | if ( ( $value['enabled'] ?? true ) && ( $value['wikitext'] ?? false ) ) { |
112 | // Parse tutorial wikitext |
113 | $parsedConfig['tutorial'] = [ |
114 | 'enabled' => true, |
115 | 'skip' => $value['skip'] ?? false, |
116 | 'html' => $this->parseValue( $value['wikitext'] ), |
117 | ]; |
118 | } else { |
119 | // The tutorial is not present or is disabled |
120 | $parsedConfig['tutorial'] = [ |
121 | 'enabled' => false, |
122 | 'skip' => true, |
123 | 'html' => '', |
124 | ]; |
125 | } |
126 | break; |
127 | case 'fields': |
128 | $parsedConfig['fields'] = []; |
129 | foreach ( $value as $fieldName => $field ) { |
130 | $parsedConfig['fields'][$fieldName] = $this->parseArrayValues( |
131 | $field, |
132 | [ 'label', 'help', 'options' ] |
133 | ); |
134 | } |
135 | break; |
136 | case 'whileActive': |
137 | case 'afterActive': |
138 | case 'beforeActive': |
139 | if ( array_key_exists( 'display', $value ) ) { |
140 | $value['display'] = $this->parseArrayValues( $value['display'] ); |
141 | } |
142 | $parsedConfig[$key] = $value; |
143 | break; |
144 | default: |
145 | $parsedConfig[$key] = $value; |
146 | break; |
147 | } |
148 | } |
149 | |
150 | $this->parsedConfig = $parsedConfig; |
151 | } |
152 | |
153 | /** |
154 | * Parses the values in an associative array as wikitext |
155 | * |
156 | * @param array $array |
157 | * @param array|null $forKeys Array of keys whose values should be parsed |
158 | * |
159 | * @return array |
160 | */ |
161 | private function parseArrayValues( array $array, $forKeys = null ): array { |
162 | $parsed = []; |
163 | foreach ( $array as $key => $value ) { |
164 | if ( $forKeys !== null ) { |
165 | if ( in_array( $key, $forKeys ) ) { |
166 | if ( is_array( $value ) ) { |
167 | $parsed[$key] = $this->parseArrayValues( $value ); |
168 | } else { |
169 | $parsed[$key] = $this->parseValue( $value ); |
170 | } |
171 | } else { |
172 | $parsed[$key] = $value; |
173 | } |
174 | } else { |
175 | $parsed[$key] = $this->parseValue( $value ); |
176 | } |
177 | } |
178 | return $parsed; |
179 | } |
180 | |
181 | /** |
182 | * Parses a wikitext fragment to HTML |
183 | * |
184 | * @param string $value Wikitext to parse |
185 | * |
186 | * @return string HTML |
187 | */ |
188 | private function parseValue( string $value ): string { |
189 | $output = $this->parserFactory->create()->parse( |
190 | $value, $this->pageRef, $this->parserOptions |
191 | ); |
192 | $parsed = $output->getText( [ |
193 | 'enableSectionEditLinks' => false, |
194 | ] ); |
195 | |
196 | $this->updateTemplates( $output ); |
197 | |
198 | return Parser::stripOuterParagraph( $parsed ); |
199 | } |
200 | |
201 | /** |
202 | * Update internal list of templates used in parsing this config |
203 | * |
204 | * @param ParserOutput $parserOutput |
205 | * |
206 | * @return void |
207 | */ |
208 | private function updateTemplates( ParserOutput $parserOutput ): void { |
209 | $templateIds = $parserOutput->getTemplateIds(); |
210 | foreach ( $parserOutput->getTemplates() as $ns => $templates ) { |
211 | foreach ( $templates as $dbk => $id ) { |
212 | $this->templates[$ns][$dbk] = [ $id, $templateIds[$ns][$dbk] ]; |
213 | } |
214 | } |
215 | } |
216 | } |