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