Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 398 |
|
0.00% |
0 / 12 |
CRAP | |
0.00% |
0 / 1 |
SpecialInterwiki | |
0.00% |
0 / 398 |
|
0.00% |
0 / 12 |
6480 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
doesWrites | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getDescription | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 | |||
getSubpagesForPrefixSearch | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
execute | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
canModify | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
30 | |||
showForm | |
0.00% |
0 / 107 |
|
0.00% |
0 / 1 |
132 | |||
onSubmit | |
0.00% |
0 / 89 |
|
0.00% |
0 / 1 |
342 | |||
showList | |
0.00% |
0 / 110 |
|
0.00% |
0 / 1 |
650 | |||
makeTable | |
0.00% |
0 / 67 |
|
0.00% |
0 / 1 |
110 | |||
error | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getGroupName | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\Interwiki; |
4 | |
5 | use LogPage; |
6 | use MediaWiki\Html\Html; |
7 | use MediaWiki\HTMLForm\HTMLForm; |
8 | use MediaWiki\MediaWikiServices; |
9 | use MediaWiki\Message\Message; |
10 | use MediaWiki\Output\OutputPage; |
11 | use MediaWiki\SpecialPage\SpecialPage; |
12 | use MediaWiki\Status\Status; |
13 | use MediaWiki\Title\Title; |
14 | use MediaWiki\WikiMap\WikiMap; |
15 | use PermissionsError; |
16 | use ReadOnlyError; |
17 | |
18 | /** |
19 | * Implements Special:Interwiki |
20 | * @ingroup SpecialPage |
21 | */ |
22 | class SpecialInterwiki extends SpecialPage { |
23 | /** |
24 | * Constructor - sets up the new special page |
25 | */ |
26 | public function __construct() { |
27 | parent::__construct( 'Interwiki' ); |
28 | } |
29 | |
30 | public function doesWrites() { |
31 | return true; |
32 | } |
33 | |
34 | /** |
35 | * Different description will be shown on Special:SpecialPage depending on |
36 | * whether the user can modify the data. |
37 | * |
38 | * @return Message |
39 | */ |
40 | public function getDescription() { |
41 | return $this->msg( $this->canModify() ? 'interwiki' : 'interwiki-title-norights' ); |
42 | } |
43 | |
44 | public function getSubpagesForPrefixSearch() { |
45 | // delete, edit both require the prefix parameter. |
46 | return [ 'add' ]; |
47 | } |
48 | |
49 | /** |
50 | * Show the special page |
51 | * |
52 | * @param string|null $par parameter passed to the page or null |
53 | */ |
54 | public function execute( $par ) { |
55 | $this->setHeaders(); |
56 | $this->outputHeader(); |
57 | |
58 | $out = $this->getOutput(); |
59 | $request = $this->getRequest(); |
60 | |
61 | $out->addModuleStyles( 'ext.interwiki.specialpage' ); |
62 | |
63 | $action = $par ?: $request->getVal( 'action', $par ); |
64 | |
65 | if ( !in_array( $action, [ 'add', 'edit', 'delete' ] ) || !$this->canModify( $out ) ) { |
66 | $this->showList(); |
67 | } else { |
68 | $this->showForm( $action ); |
69 | } |
70 | } |
71 | |
72 | /** |
73 | * Returns boolean whether the user can modify the data. |
74 | * @param OutputPage|bool $out If $wgOut object given, it adds the respective error message. |
75 | * @return bool |
76 | * @throws PermissionsError|ReadOnlyError |
77 | */ |
78 | public function canModify( $out = false ) { |
79 | if ( !$this->getUser()->isAllowed( 'interwiki' ) ) { |
80 | // Check permissions |
81 | if ( $out ) { |
82 | throw new PermissionsError( 'interwiki' ); |
83 | } |
84 | |
85 | return false; |
86 | } elseif ( $this->getConfig()->get( 'InterwikiCache' ) ) { |
87 | // Editing the interwiki cache is not supported |
88 | if ( $out ) { |
89 | $out->addWikiMsg( 'interwiki-cached' ); |
90 | } |
91 | |
92 | return false; |
93 | } else { |
94 | $this->checkReadOnly(); |
95 | } |
96 | |
97 | return true; |
98 | } |
99 | |
100 | /** |
101 | * @param string $action The action of the form |
102 | */ |
103 | protected function showForm( $action ) { |
104 | $formDescriptor = []; |
105 | $hiddenFields = [ |
106 | 'action' => $action, |
107 | ]; |
108 | |
109 | $status = Status::newGood(); |
110 | $request = $this->getRequest(); |
111 | $prefix = $request->getVal( 'prefix', $request->getVal( 'hiddenPrefix' ) ); |
112 | |
113 | switch ( $action ) { |
114 | case 'add': |
115 | case 'edit': |
116 | $formDescriptor = [ |
117 | 'prefix' => [ |
118 | 'type' => 'text', |
119 | 'label-message' => 'interwiki-prefix-label', |
120 | 'name' => 'prefix', |
121 | 'autofocus' => true, |
122 | ], |
123 | |
124 | 'local' => [ |
125 | 'type' => 'check', |
126 | 'id' => 'mw-interwiki-local', |
127 | 'label-message' => 'interwiki-local-label', |
128 | 'name' => 'local', |
129 | ], |
130 | |
131 | 'trans' => [ |
132 | 'type' => 'check', |
133 | 'id' => 'mw-interwiki-trans', |
134 | 'label-message' => 'interwiki-trans-label', |
135 | 'name' => 'trans', |
136 | ], |
137 | |
138 | 'url' => [ |
139 | 'type' => 'url', |
140 | 'id' => 'mw-interwiki-url', |
141 | 'label-message' => 'interwiki-url-label', |
142 | 'maxlength' => 200, |
143 | 'name' => 'wpInterwikiURL', |
144 | 'size' => 60, |
145 | ], |
146 | |
147 | 'api' => [ |
148 | 'type' => 'url', |
149 | 'id' => 'mw-interwiki-api', |
150 | 'label-message' => 'interwiki-api-label', |
151 | 'maxlength' => 200, |
152 | 'name' => 'wpInterwikiAPI', |
153 | 'size' => 60, |
154 | ], |
155 | |
156 | 'reason' => [ |
157 | 'type' => 'text', |
158 | 'id' => "mw-interwiki-{$action}reason", |
159 | 'label-message' => 'interwiki_reasonfield', |
160 | 'maxlength' => 200, |
161 | 'name' => 'wpInterwikiReason', |
162 | 'size' => 60, |
163 | ], |
164 | ]; |
165 | |
166 | break; |
167 | case 'delete': |
168 | $formDescriptor = [ |
169 | 'prefix' => [ |
170 | 'type' => 'hidden', |
171 | 'name' => 'prefix', |
172 | 'default' => $prefix, |
173 | ], |
174 | |
175 | 'reason' => [ |
176 | 'type' => 'text', |
177 | 'name' => 'reason', |
178 | 'label-message' => 'interwiki_reasonfield', |
179 | ], |
180 | ]; |
181 | |
182 | break; |
183 | } |
184 | |
185 | $formDescriptor['hiddenPrefix'] = [ |
186 | 'type' => 'hidden', |
187 | 'name' => 'hiddenPrefix', |
188 | 'default' => $prefix, |
189 | ]; |
190 | |
191 | if ( $action === 'edit' ) { |
192 | $dbr = MediaWikiServices::getInstance()->getConnectionProvider()->getReplicaDatabase(); |
193 | $row = $dbr->newSelectQueryBuilder() |
194 | ->select( '*' ) |
195 | ->from( 'interwiki' ) |
196 | ->where( [ 'iw_prefix' => $prefix ] ) |
197 | ->caller( __METHOD__ ) |
198 | ->fetchRow(); |
199 | |
200 | $formDescriptor['prefix']['disabled'] = true; |
201 | $formDescriptor['prefix']['default'] = $prefix; |
202 | $hiddenFields['prefix'] = $prefix; |
203 | |
204 | if ( !$row ) { |
205 | $status->fatal( 'interwiki_editerror', $prefix ); |
206 | } else { |
207 | $formDescriptor['url']['default'] = $row->iw_url; |
208 | $formDescriptor['api']['default'] = $row->iw_api; |
209 | $formDescriptor['trans']['default'] = $row->iw_trans; |
210 | $formDescriptor['local']['default'] = $row->iw_local; |
211 | } |
212 | } |
213 | |
214 | if ( !$status->isOK() ) { |
215 | $formDescriptor = []; |
216 | } |
217 | |
218 | $htmlForm = HTMLForm::factory( 'ooui', $formDescriptor, $this->getContext() ); |
219 | $htmlForm |
220 | ->addHiddenFields( $hiddenFields ) |
221 | ->setSubmitCallback( [ $this, 'onSubmit' ] ); |
222 | |
223 | if ( $status->isOK() ) { |
224 | if ( $action === 'delete' ) { |
225 | $htmlForm->setSubmitDestructive(); |
226 | } |
227 | |
228 | $htmlForm->setSubmitTextMsg( $action !== 'add' ? $action : 'interwiki_addbutton' ) |
229 | ->setPreHtml( $this->msg( $action !== 'delete' ? "interwiki_{$action}intro" : |
230 | 'interwiki_deleting', $prefix )->escaped() ) |
231 | ->show(); |
232 | } else { |
233 | $htmlForm->suppressDefaultSubmit() |
234 | ->prepareForm() |
235 | ->displayForm( $status ); |
236 | } |
237 | |
238 | $this->getOutput()->addBacklinkSubtitle( $this->getPageTitle() ); |
239 | } |
240 | |
241 | public function onSubmit( array $data ) { |
242 | $services = MediaWikiServices::getInstance(); |
243 | |
244 | $status = Status::newGood(); |
245 | $request = $this->getRequest(); |
246 | $config = $this->getConfig(); |
247 | $prefix = $this->getRequest()->getVal( 'prefix' ) ?? ''; |
248 | $do = $request->getVal( 'action' ); |
249 | // Show an error if the prefix is invalid (only when adding one). |
250 | // Invalid characters for a title should also be invalid for a prefix. |
251 | // Whitespace, ':', '&' and '=' are invalid, too. |
252 | // (Bug 30599). |
253 | $validPrefixChars = preg_replace( '/[ :&=]/', '', Title::legalChars() ); |
254 | if ( $do === 'add' && preg_match( "/\s|[^$validPrefixChars]/", $prefix ) ) { |
255 | $status->fatal( 'interwiki-badprefix', htmlspecialchars( $prefix ) ); |
256 | return $status; |
257 | } |
258 | // Disallow adding local interlanguage definitions if using global |
259 | $interwikiCentralInterlanguageDB = $config->get( 'InterwikiCentralInterlanguageDB' ); |
260 | if ( |
261 | $do === 'add' && $services->getLanguageNameUtils()->getLanguageName( $prefix ) |
262 | && $interwikiCentralInterlanguageDB !== WikiMap::getCurrentWikiId() |
263 | && $interwikiCentralInterlanguageDB !== null |
264 | ) { |
265 | $status->fatal( 'interwiki-cannotaddlocallanguage', htmlspecialchars( $prefix ) ); |
266 | return $status; |
267 | } |
268 | $reason = $data['reason']; |
269 | $selfTitle = $this->getPageTitle(); |
270 | $lookup = $services->getInterwikiLookup(); |
271 | $dbw = $services->getConnectionProvider()->getPrimaryDatabase(); |
272 | switch ( $do ) { |
273 | case 'delete': |
274 | $dbw->newDeleteQueryBuilder() |
275 | ->deleteFrom( 'interwiki' ) |
276 | ->where( [ 'iw_prefix' => $prefix ] ) |
277 | ->caller( __METHOD__ ) |
278 | ->execute(); |
279 | |
280 | if ( $dbw->affectedRows() === 0 ) { |
281 | $status->fatal( 'interwiki_delfailed', $prefix ); |
282 | } else { |
283 | $this->getOutput()->addWikiMsg( 'interwiki_deleted', $prefix ); |
284 | $log = new LogPage( 'interwiki' ); |
285 | $log->addEntry( |
286 | 'iw_delete', |
287 | $selfTitle, |
288 | $reason, |
289 | [ $prefix ], |
290 | $this->getUser() |
291 | ); |
292 | $lookup->invalidateCache( $prefix ); |
293 | } |
294 | break; |
295 | /** @noinspection PhpMissingBreakStatementInspection */ |
296 | case 'add': |
297 | $contLang = $services->getContentLanguage(); |
298 | $prefix = $contLang->lc( $prefix ); |
299 | // Fall through |
300 | case 'edit': |
301 | // T374771: Trim the URL and API URLs to reduce confusion when |
302 | // the URLs are accidentally provided with extra whitespace |
303 | $theurl = trim( $data['url'] ); |
304 | $api = trim( $data['api'] ?? '' ); |
305 | $local = $data['local'] ? 1 : 0; |
306 | $trans = $data['trans'] ? 1 : 0; |
307 | $rows = [ |
308 | 'iw_prefix' => $prefix, |
309 | 'iw_url' => $theurl, |
310 | 'iw_api' => $api, |
311 | 'iw_wikiid' => '', |
312 | 'iw_local' => $local, |
313 | 'iw_trans' => $trans |
314 | ]; |
315 | |
316 | if ( $prefix === '' || $theurl === '' ) { |
317 | $status->fatal( 'interwiki-submit-empty' ); |
318 | break; |
319 | } |
320 | |
321 | // Simple URL validation: check that the protocol is one of |
322 | // the supported protocols for this wiki. |
323 | // (T32600) |
324 | if ( !$services->getUrlUtils()->parse( $theurl ) ) { |
325 | $status->fatal( 'interwiki-submit-invalidurl' ); |
326 | break; |
327 | } |
328 | |
329 | if ( $do === 'add' ) { |
330 | $dbw->newInsertQueryBuilder() |
331 | ->insertInto( 'interwiki' ) |
332 | ->ignore() |
333 | ->row( $rows ) |
334 | ->caller( __METHOD__ ) |
335 | ->execute(); |
336 | } else { // $do === 'edit' |
337 | $dbw->newUpdateQueryBuilder() |
338 | ->update( 'interwiki' ) |
339 | ->ignore() |
340 | ->set( $rows ) |
341 | ->where( [ 'iw_prefix' => $prefix ] ) |
342 | ->caller( __METHOD__ ) |
343 | ->execute(); |
344 | } |
345 | |
346 | // used here: interwiki_addfailed, interwiki_added, interwiki_edited |
347 | if ( $dbw->affectedRows() === 0 ) { |
348 | $status->fatal( "interwiki_{$do}failed", $prefix ); |
349 | } else { |
350 | $this->getOutput()->addWikiMsg( "interwiki_{$do}ed", $prefix ); |
351 | $log = new LogPage( 'interwiki' ); |
352 | $log->addEntry( |
353 | 'iw_' . $do, |
354 | $selfTitle, |
355 | $reason, |
356 | [ $prefix, $theurl, $trans, $local ], |
357 | $this->getUser() |
358 | ); |
359 | $lookup->invalidateCache( $prefix ); |
360 | } |
361 | break; |
362 | } |
363 | |
364 | return $status; |
365 | } |
366 | |
367 | protected function showList() { |
368 | $canModify = $this->canModify(); |
369 | |
370 | // Build lists |
371 | $services = MediaWikiServices::getInstance(); |
372 | |
373 | $lookup = $services->getInterwikiLookup(); |
374 | $iwPrefixes = $lookup->getAllPrefixes( null ); |
375 | $iwGlobalPrefixes = []; |
376 | $iwGlobalLanguagePrefixes = []; |
377 | |
378 | $config = $this->getConfig(); |
379 | $interwikiCentralDB = $config->get( 'InterwikiCentralDB' ); |
380 | |
381 | $languageNameUtils = $services->getLanguageNameUtils(); |
382 | |
383 | $connectionProvider = $services->getConnectionProvider(); |
384 | |
385 | if ( $interwikiCentralDB !== null && $interwikiCentralDB !== WikiMap::getCurrentWikiId() ) { |
386 | // Fetch list from global table |
387 | $dbrCentralDB = $connectionProvider->getReplicaDatabase( $interwikiCentralDB ); |
388 | |
389 | $res = $dbrCentralDB->newSelectQueryBuilder() |
390 | ->select( '*' ) |
391 | ->from( 'interwiki' ) |
392 | ->caller( __METHOD__ ) |
393 | ->fetchResultSet(); |
394 | $retval = []; |
395 | foreach ( $res as $row ) { |
396 | $row = (array)$row; |
397 | if ( !$languageNameUtils->getLanguageName( $row['iw_prefix'] ) ) { |
398 | $retval[] = $row; |
399 | } |
400 | } |
401 | $iwGlobalPrefixes = $retval; |
402 | } |
403 | |
404 | // Almost the same loop as above, but for global inter*language* links, whereas the above is for |
405 | // global inter*wiki* links |
406 | $interwikiCentralInterlanguageDB = $config->get( 'InterwikiCentralInterlanguageDB' ); |
407 | $usingGlobalInterlangLinks = ( $interwikiCentralInterlanguageDB !== null ); |
408 | $isGlobalInterlanguageDB = ( $interwikiCentralInterlanguageDB === WikiMap::getCurrentWikiId() ); |
409 | $usingGlobalLanguages = $usingGlobalInterlangLinks && !$isGlobalInterlanguageDB; |
410 | if ( $usingGlobalLanguages ) { |
411 | // Fetch list from global table |
412 | $dbrCentralLangDB = $connectionProvider->getReplicaDatabase( $interwikiCentralInterlanguageDB ); |
413 | |
414 | $res = $dbrCentralLangDB->newSelectQueryBuilder() |
415 | ->select( '*' ) |
416 | ->from( 'interwiki' ) |
417 | ->caller( __METHOD__ ) |
418 | ->fetchResultSet(); |
419 | $retval2 = []; |
420 | foreach ( $res as $row ) { |
421 | $row = (array)$row; |
422 | // Note that the above DB query explicitly *excludes* interlang ones |
423 | // (which makes sense), whereas here we _only_ care about interlang ones! |
424 | if ( $languageNameUtils->getLanguageName( $row['iw_prefix'] ) ) { |
425 | $retval2[] = $row; |
426 | } |
427 | } |
428 | $iwGlobalLanguagePrefixes = $retval2; |
429 | } |
430 | |
431 | // Split out language links |
432 | $iwLocalPrefixes = []; |
433 | $iwLanguagePrefixes = []; |
434 | foreach ( $iwPrefixes as $iwPrefix ) { |
435 | if ( $languageNameUtils->getLanguageName( $iwPrefix['iw_prefix'] ) ) { |
436 | $iwLanguagePrefixes[] = $iwPrefix; |
437 | } else { |
438 | $iwLocalPrefixes[] = $iwPrefix; |
439 | } |
440 | } |
441 | |
442 | // If using global interlanguage links, just ditch the data coming from the |
443 | // local table and overwrite it with the global data |
444 | if ( $usingGlobalInterlangLinks ) { |
445 | unset( $iwLanguagePrefixes ); |
446 | $iwLanguagePrefixes = $iwGlobalLanguagePrefixes; |
447 | } |
448 | |
449 | // Page intro content |
450 | $this->getOutput()->addWikiMsg( 'interwiki_intro' ); |
451 | |
452 | // Add 'view log' link when possible |
453 | if ( !$config->get( 'InterwikiViewOnly' ) ) { |
454 | $logLink = $this->getLinkRenderer()->makeLink( |
455 | SpecialPage::getTitleFor( 'Log', 'interwiki' ), |
456 | $this->msg( 'interwiki-logtext' )->text() |
457 | ); |
458 | $this->getOutput()->addHTML( '<p class="mw-interwiki-log">' . $logLink . '</p>' ); |
459 | } |
460 | |
461 | // Add 'add' link |
462 | if ( $canModify ) { |
463 | if ( count( $iwGlobalPrefixes ) !== 0 ) { |
464 | if ( $usingGlobalLanguages ) { |
465 | $addtext = 'interwiki-addtext-local-nolang'; |
466 | } else { |
467 | $addtext = 'interwiki-addtext-local'; |
468 | } |
469 | } else { |
470 | if ( $usingGlobalLanguages ) { |
471 | $addtext = 'interwiki-addtext-nolang'; |
472 | } else { |
473 | $addtext = 'interwiki_addtext'; |
474 | } |
475 | } |
476 | $addtext = $this->msg( $addtext )->text(); |
477 | $addlink = $this->getLinkRenderer()->makeKnownLink( |
478 | $this->getPageTitle( 'add' ), $addtext ); |
479 | $this->getOutput()->addHTML( |
480 | '<p class="mw-interwiki-addlink">' . $addlink . '</p>' ); |
481 | } |
482 | |
483 | $this->getOutput()->addWikiMsg( 'interwiki-legend' ); |
484 | |
485 | if ( $iwPrefixes === [] && $iwGlobalPrefixes === [] ) { |
486 | // If the interwiki table(s) are empty, display an error message |
487 | $this->error( 'interwiki_error' ); |
488 | return; |
489 | } |
490 | |
491 | // Add the global table |
492 | if ( count( $iwGlobalPrefixes ) !== 0 ) { |
493 | $this->getOutput()->addHTML( |
494 | '<h2 id="interwikitable-global">' . |
495 | $this->msg( 'interwiki-global-links' )->parse() . |
496 | '</h2>' |
497 | ); |
498 | $this->getOutput()->addWikiMsg( 'interwiki-global-description' ); |
499 | |
500 | // $canModify is false here because this is just a display of remote data |
501 | $this->makeTable( false, $iwGlobalPrefixes ); |
502 | } |
503 | |
504 | // Add the local table |
505 | if ( count( $iwLocalPrefixes ) !== 0 ) { |
506 | if ( count( $iwGlobalPrefixes ) !== 0 ) { |
507 | $this->getOutput()->addHTML( |
508 | '<h2 id="interwikitable-local">' . |
509 | $this->msg( 'interwiki-local-links' )->parse() . |
510 | '</h2>' |
511 | ); |
512 | $this->getOutput()->addWikiMsg( 'interwiki-local-description' ); |
513 | } else { |
514 | $this->getOutput()->addHTML( |
515 | '<h2 id="interwikitable-local">' . |
516 | $this->msg( 'interwiki-links' )->parse() . |
517 | '</h2>' |
518 | ); |
519 | $this->getOutput()->addWikiMsg( 'interwiki-description' ); |
520 | } |
521 | $this->makeTable( $canModify, $iwLocalPrefixes ); |
522 | } |
523 | |
524 | // Add the language table |
525 | if ( count( $iwLanguagePrefixes ) !== 0 ) { |
526 | if ( $usingGlobalLanguages ) { |
527 | $header = 'interwiki-global-language-links'; |
528 | $description = 'interwiki-global-language-description'; |
529 | } else { |
530 | $header = 'interwiki-language-links'; |
531 | $description = 'interwiki-language-description'; |
532 | } |
533 | |
534 | $this->getOutput()->addHTML( |
535 | '<h2 id="interwikitable-language">' . |
536 | $this->msg( $header )->parse() . |
537 | '</h2>' |
538 | ); |
539 | $this->getOutput()->addWikiMsg( $description ); |
540 | |
541 | // When using global interlanguage links, don't allow them to be modified |
542 | // except on the source wiki |
543 | $canModify = ( $usingGlobalLanguages ? false : $canModify ); |
544 | $this->makeTable( $canModify, $iwLanguagePrefixes ); |
545 | } |
546 | } |
547 | |
548 | protected function makeTable( $canModify, $iwPrefixes ) { |
549 | // Output the existing Interwiki prefixes table header |
550 | $out = Html::openElement( |
551 | 'table', |
552 | [ 'class' => 'mw-interwikitable wikitable sortable body' ] |
553 | ) . "\n"; |
554 | $out .= Html::openElement( 'thead' ) . |
555 | Html::openElement( 'tr', [ 'class' => 'interwikitable-header' ] ) . |
556 | Html::element( 'th', [], $this->msg( 'interwiki_prefix' )->text() ) . |
557 | Html::element( 'th', [], $this->msg( 'interwiki_url' )->text() ) . |
558 | Html::element( 'th', [], $this->msg( 'interwiki_local' )->text() ) . |
559 | Html::element( 'th', [], $this->msg( 'interwiki_trans' )->text() ) . |
560 | ( $canModify ? |
561 | Html::element( |
562 | 'th', |
563 | [ 'class' => 'unsortable' ], |
564 | $this->msg( 'interwiki_edit' )->text() |
565 | ) : |
566 | '' |
567 | ); |
568 | $out .= Html::closeElement( 'tr' ) . |
569 | Html::closeElement( 'thead' ) . "\n" . |
570 | Html::openElement( 'tbody' ); |
571 | |
572 | $selfTitle = $this->getPageTitle(); |
573 | |
574 | // Output the existing Interwiki prefixes table rows |
575 | foreach ( $iwPrefixes as $iwPrefix ) { |
576 | $out .= Html::openElement( 'tr', [ 'class' => 'mw-interwikitable-row' ] ); |
577 | $out .= Html::element( 'td', [ 'class' => 'mw-interwikitable-prefix' ], |
578 | $iwPrefix['iw_prefix'] ); |
579 | $out .= Html::element( |
580 | 'td', |
581 | [ 'class' => 'mw-interwikitable-url' ], |
582 | $iwPrefix['iw_url'] |
583 | ); |
584 | $attribs = [ 'class' => 'mw-interwikitable-local' ]; |
585 | // Green background for cells with "yes". |
586 | if ( isset( $iwPrefix['iw_local'] ) && $iwPrefix['iw_local'] ) { |
587 | $attribs['class'] .= ' mw-interwikitable-local-yes'; |
588 | } |
589 | // The messages interwiki_0 and interwiki_1 are used here. |
590 | $contents = isset( $iwPrefix['iw_local'] ) ? |
591 | $this->msg( 'interwiki_' . $iwPrefix['iw_local'] )->text() : |
592 | '-'; |
593 | $out .= Html::element( 'td', $attribs, $contents ); |
594 | $attribs = [ 'class' => 'mw-interwikitable-trans' ]; |
595 | // Green background for cells with "yes". |
596 | if ( isset( $iwPrefix['iw_trans'] ) && $iwPrefix['iw_trans'] ) { |
597 | $attribs['class'] .= ' mw-interwikitable-trans-yes'; |
598 | } |
599 | // The messages interwiki_0 and interwiki_1 are used here. |
600 | $contents = isset( $iwPrefix['iw_trans'] ) ? |
601 | $this->msg( 'interwiki_' . $iwPrefix['iw_trans'] )->text() : |
602 | '-'; |
603 | $out .= Html::element( 'td', $attribs, $contents ); |
604 | |
605 | // Additional column when the interwiki table can be modified. |
606 | if ( $canModify ) { |
607 | $out .= Html::rawElement( 'td', [ 'class' => 'mw-interwikitable-modify' ], |
608 | $this->getLinkRenderer()->makeKnownLink( |
609 | $selfTitle, |
610 | $this->msg( 'edit' )->text(), |
611 | [], |
612 | [ 'action' => 'edit', 'prefix' => $iwPrefix['iw_prefix'] ] |
613 | ) . |
614 | $this->msg( 'comma-separator' )->escaped() . |
615 | $this->getLinkRenderer()->makeKnownLink( |
616 | $selfTitle, |
617 | $this->msg( 'delete' )->text(), |
618 | [], |
619 | [ 'action' => 'delete', 'prefix' => $iwPrefix['iw_prefix'] ] |
620 | ) |
621 | ); |
622 | } |
623 | $out .= Html::closeElement( 'tr' ) . "\n"; |
624 | } |
625 | $out .= Html::closeElement( 'tbody' ) . |
626 | Html::closeElement( 'table' ); |
627 | |
628 | $this->getOutput()->addHTML( $out ); |
629 | $this->getOutput()->addModuleStyles( 'jquery.tablesorter.styles' ); |
630 | $this->getOutput()->addModules( 'jquery.tablesorter' ); |
631 | } |
632 | |
633 | /** |
634 | * @param string ...$args |
635 | */ |
636 | protected function error( ...$args ) { |
637 | $this->getOutput()->wrapWikiMsg( "<p class='error'>$1</p>", $args ); |
638 | } |
639 | |
640 | protected function getGroupName() { |
641 | return 'wiki'; |
642 | } |
643 | } |