[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Implements Special:Interwiki 4 * @ingroup SpecialPage 5 */ 6 class SpecialInterwiki extends SpecialPage { 7 /** 8 * Constructor - sets up the new special page 9 */ 10 public function __construct() { 11 parent::__construct( 'Interwiki' ); 12 } 13 14 /** 15 * Different description will be shown on Special:SpecialPage depending on 16 * whether the user can modify the data. 17 * @return String 18 */ 19 function getDescription() { 20 return $this->msg( $this->canModify() ? 21 'interwiki' : 'interwiki-title-norights' )->plain(); 22 } 23 24 /** 25 * Show the special page 26 * 27 * @param $par Mixed: parameter passed to the page or null 28 */ 29 public function execute( $par ) { 30 $this->setHeaders(); 31 $this->outputHeader(); 32 33 $out = $this->getOutput(); 34 $request = $this->getRequest(); 35 36 $out->addModules( 'ext.interwiki.specialpage' ); 37 38 $action = $par ? $par : $request->getVal( 'action', $par ); 39 $return = $this->getPageTitle(); 40 41 switch( $action ) { 42 case 'delete': 43 case 'edit': 44 case 'add': 45 if ( $this->canModify( $out ) ) { 46 $this->showForm( $action ); 47 } 48 $out->returnToMain( false, $return ); 49 break; 50 case 'submit': 51 if ( !$this->canModify( $out ) ) { 52 // Error msg added by canModify() 53 } elseif ( !$request->wasPosted() || 54 !$this->getUser()->matchEditToken( $request->getVal( 'wpEditToken' ) ) 55 ) { 56 // Prevent cross-site request forgeries 57 $out->addWikiMsg( 'sessionfailure' ); 58 } else { 59 $this->doSubmit(); 60 } 61 $out->returnToMain( false, $return ); 62 break; 63 default: 64 $this->showList(); 65 break; 66 } 67 } 68 69 /** 70 * Returns boolean whether the user can modify the data. 71 * @param $out OutputPage|bool If $wgOut object given, it adds the respective error message. 72 * @throws PermissionsError 73 * @return bool 74 */ 75 public function canModify( $out = false ) { 76 global $wgInterwikiCache; 77 if ( !$this->getUser()->isAllowed( 'interwiki' ) ) { 78 // Check permissions 79 if ( $out ) { 80 throw new PermissionsError( 'interwiki' ); 81 } 82 83 return false; 84 } elseif ( $wgInterwikiCache ) { 85 // Editing the interwiki cache is not supported 86 if ( $out ) { 87 $out->addWikiMsg( 'interwiki-cached' ); 88 } 89 90 return false; 91 } elseif ( wfReadOnly() ) { 92 // Is the database in read-only mode? 93 if ( $out ) { 94 $out->readOnlyPage(); 95 } 96 return false; 97 } 98 return true; 99 } 100 101 /** 102 * @param $action string 103 */ 104 function showForm( $action ) { 105 $request = $this->getRequest(); 106 107 $prefix = $request->getVal( 'prefix' ); 108 $wpPrefix = ''; 109 $label = array( 'class' => 'mw-label' ); 110 $input = array( 'class' => 'mw-input' ); 111 112 if ( $action === 'delete' ) { 113 $topmessage = $this->msg( 'interwiki_delquestion', $prefix )->text(); 114 $intromessage = $this->msg( 'interwiki_deleting', $prefix )->escaped(); 115 $wpPrefix = Html::hidden( 'wpInterwikiPrefix', $prefix ); 116 $button = 'delete'; 117 $formContent = ''; 118 } elseif ( $action === 'edit' ) { 119 $dbr = wfGetDB( DB_SLAVE ); 120 $row = $dbr->selectRow( 'interwiki', '*', array( 'iw_prefix' => $prefix ), __METHOD__ ); 121 122 if ( !$row ) { 123 $this->error( 'interwiki_editerror', $prefix ); 124 return; 125 } 126 127 $prefix = $prefixElement = $row->iw_prefix; 128 $defaulturl = $row->iw_url; 129 $trans = $row->iw_trans; 130 $local = $row->iw_local; 131 $wpPrefix = Html::hidden( 'wpInterwikiPrefix', $row->iw_prefix ); 132 $topmessage = $this->msg( 'interwiki_edittext' )->text(); 133 $intromessage = $this->msg( 'interwiki_editintro' )->escaped(); 134 $button = 'edit'; 135 } elseif ( $action === 'add' ) { 136 $prefix = $request->getVal( 'wpInterwikiPrefix', $request->getVal( 'prefix' ) ); 137 $prefixElement = Xml::input( 'wpInterwikiPrefix', 20, $prefix, 138 array( 'tabindex' => 1, 'id' => 'mw-interwiki-prefix', 'maxlength' => 20 ) ); 139 $local = $request->getCheck( 'wpInterwikiLocal' ); 140 $trans = $request->getCheck( 'wpInterwikiTrans' ); 141 $defaulturl = $request->getVal( 'wpInterwikiURL', $this->msg( 'interwiki-defaulturl' )->text() ); 142 $topmessage = $this->msg( 'interwiki_addtext' )->text(); 143 $intromessage = $this->msg( 'interwiki_addintro' )->escaped(); 144 $button = 'interwiki_addbutton'; 145 } 146 147 if ( $action === 'add' || $action === 'edit' ) { 148 $formContent = Html::rawElement( 'tr', null, 149 Html::element( 'td', $label, $this->msg( 'interwiki-prefix-label' )->text() ) . 150 Html::rawElement( 'td', null, '<tt>' . $prefixElement . '</tt>' ) 151 ) . Html::rawElement( 152 'tr', 153 null, 154 Html::rawElement( 155 'td', 156 $label, 157 Xml::label( $this->msg( 'interwiki-local-label' )->text(), 'mw-interwiki-local' ) 158 ) . 159 Html::rawElement( 160 'td', 161 $input, 162 Xml::check( 'wpInterwikiLocal', $local, array( 'id' => 'mw-interwiki-local' ) ) 163 ) 164 ) . Html::rawElement( 'tr', null, 165 Html::rawElement( 166 'td', 167 $label, 168 Xml::label( $this->msg( 'interwiki-trans-label' )->text(), 'mw-interwiki-trans' ) 169 ) . 170 Html::rawElement( 171 'td', 172 $input, Xml::check( 'wpInterwikiTrans', $trans, array( 'id' => 'mw-interwiki-trans' ) ) ) 173 ) . Html::rawElement( 'tr', null, 174 Html::rawElement( 175 'td', 176 $label, 177 Xml::label( $this->msg( 'interwiki-url-label' )->text(), 'mw-interwiki-url' ) 178 ) . 179 Html::rawElement( 'td', $input, Xml::input( 'wpInterwikiURL', 60, $defaulturl, 180 array( 'tabindex' => 1, 'maxlength' => 200, 'id' => 'mw-interwiki-url' ) ) ) 181 ); 182 } 183 184 $form = Xml::fieldset( $topmessage, Html::rawElement( 185 'form', 186 array( 187 'id' => "mw-interwiki-{$action}form", 188 'method' => 'post', 189 'action' => $this->getPageTitle()->getLocalUrl( array( 190 'action' => 'submit', 191 'prefix' => $prefix 192 ) ) 193 ), 194 Html::rawElement( 'p', null, $intromessage ) . 195 Html::rawElement( 'table', array( 'id' => "mw-interwiki-{$action}" ), 196 $formContent . Html::rawElement( 'tr', null, 197 Html::rawElement( 'td', $label, Xml::label( $this->msg( 'interwiki_reasonfield' )->text(), 198 "mw-interwiki-{$action}reason" ) ) . 199 Html::rawElement( 'td', $input, Xml::input( 'wpInterwikiReason', 60, '', 200 array( 'tabindex' => 1, 'id' => "mw-interwiki-{$action}reason", 'maxlength' => 200 ) ) ) 201 ) . Html::rawElement( 'tr', null, 202 Html::rawElement( 'td', null, '' ) . 203 Html::rawElement( 'td', array( 'class' => 'mw-submit' ), 204 Xml::submitButton( $this->msg( $button )->text(), array( 'id' => 'mw-interwiki-submit' ) ) ) 205 ) . $wpPrefix . 206 Html::hidden( 'wpEditToken', $this->getUser()->getEditToken() ) . 207 Html::hidden( 'wpInterwikiAction', $action ) 208 ) 209 ) ); 210 $this->getOutput()->addHTML( $form ); 211 return; 212 } 213 214 function doSubmit() { 215 global $wgMemc, $wgContLang; 216 217 $request = $this->getRequest(); 218 $prefix = $request->getVal( 'wpInterwikiPrefix' ); 219 $do = $request->getVal( 'wpInterwikiAction' ); 220 // Show an error if the prefix is invalid (only when adding one). 221 // Invalid characters for a title should also be invalid for a prefix. 222 // Whitespace, ':', '&' and '=' are invalid, too. 223 // (Bug 30599). 224 global $wgLegalTitleChars; 225 $validPrefixChars = preg_replace( '/[ :&=]/', '', $wgLegalTitleChars ); 226 if ( preg_match( "/\s|[^$validPrefixChars]/", $prefix ) && $do === 'add' ) { 227 $this->error( 'interwiki-badprefix', htmlspecialchars( $prefix ) ); 228 $this->showForm( $do ); 229 return; 230 } 231 $reason = $request->getText( 'wpInterwikiReason' ); 232 $selfTitle = $this->getPageTitle(); 233 $dbw = wfGetDB( DB_MASTER ); 234 switch( $do ) { 235 case 'delete': 236 $dbw->delete( 'interwiki', array( 'iw_prefix' => $prefix ), __METHOD__ ); 237 238 if ( $dbw->affectedRows() === 0 ) { 239 $this->error( 'interwiki_delfailed', $prefix ); 240 $this->showForm( $do ); 241 } else { 242 $this->getOutput()->addWikiMsg( 'interwiki_deleted', $prefix ); 243 $log = new LogPage( 'interwiki' ); 244 $log->addEntry( 'iw_delete', $selfTitle, $reason, array( $prefix ) ); 245 $wgMemc->delete( wfMemcKey( 'interwiki', $prefix ) ); 246 } 247 break; 248 case 'add': 249 $prefix = $wgContLang->lc( $prefix ); 250 // N.B.: no break! 251 case 'edit': 252 $theurl = $request->getVal( 'wpInterwikiURL' ); 253 $local = $request->getCheck( 'wpInterwikiLocal' ) ? 1 : 0; 254 $trans = $request->getCheck( 'wpInterwikiTrans' ) ? 1 : 0; 255 $data = array( 256 'iw_prefix' => $prefix, 257 'iw_url' => $theurl, 258 'iw_local' => $local, 259 'iw_trans' => $trans 260 ); 261 262 if ( $prefix === '' || $theurl === '' ) { 263 $this->error( 'interwiki-submit-empty' ); 264 $this->showForm( $do ); 265 return; 266 } 267 268 // Simple URL validation: check that the protocol is one of 269 // the supported protocols for this wiki. 270 // (bug 30600) 271 if ( !wfParseUrl( $theurl ) ) { 272 $this->error( 'interwiki-submit-invalidurl' ); 273 $this->showForm( $do ); 274 return; 275 } 276 277 if ( $do === 'add' ) { 278 $dbw->insert( 'interwiki', $data, __METHOD__, 'IGNORE' ); 279 } else { // $do === 'edit' 280 $dbw->update( 'interwiki', $data, array( 'iw_prefix' => $prefix ), __METHOD__, 'IGNORE' ); 281 } 282 283 // used here: interwiki_addfailed, interwiki_added, interwiki_edited 284 if ( $dbw->affectedRows() === 0 ) { 285 $this->error( "interwiki_{$do}failed", $prefix ); 286 $this->showForm( $do ); 287 } else { 288 $this->getOutput()->addWikiMsg( "interwiki_{$do}ed", $prefix ); 289 $log = new LogPage( 'interwiki' ); 290 $log->addEntry( 'iw_' . $do, $selfTitle, $reason, array( $prefix, $theurl, $trans, $local ) ); 291 $wgMemc->delete( wfMemcKey( 'interwiki', $prefix ) ); 292 } 293 break; 294 } 295 } 296 297 function showList() { 298 global $wgInterwikiCentralDB; 299 $canModify = $this->canModify(); 300 301 // Build lists 302 if ( !method_exists( 'Interwiki', 'getAllPrefixes' ) ) { 303 // version 2.0 is not backwards compatible (but will still display a nice error) 304 $this->error( 'interwiki_error' ); 305 return; 306 } 307 $iwPrefixes = Interwiki::getAllPrefixes( null ); 308 $iwGlobalPrefixes = array(); 309 if ( $wgInterwikiCentralDB !== null && $wgInterwikiCentralDB !== wfWikiId() ) { 310 // Fetch list from global table 311 $dbrCentralDB = wfGetDB( DB_SLAVE, array(), $wgInterwikiCentralDB ); 312 $res = $dbrCentralDB->select( 'interwiki', '*', false, __METHOD__ ); 313 $retval = array(); 314 foreach ( $res as $row ) { 315 $row = (array)$row; 316 if ( !Language::fetchLanguageName( $row['iw_prefix'] ) ) { 317 $retval[] = $row; 318 } 319 } 320 $iwGlobalPrefixes = $retval; 321 } 322 323 // Split out language links 324 $iwLocalPrefixes = array(); 325 $iwLanguagePrefixes = array(); 326 foreach ( $iwPrefixes as $iwPrefix ) { 327 if ( Language::fetchLanguageName( $iwPrefix['iw_prefix'] ) ) { 328 $iwLanguagePrefixes[] = $iwPrefix; 329 } else { 330 $iwLocalPrefixes[] = $iwPrefix; 331 } 332 } 333 334 // Page intro content 335 $this->getOutput()->addWikiMsg( 'interwiki_intro' ); 336 $logLink = Linker::link( 337 SpecialPage::getTitleFor( 'log', 'interwiki' ), 338 $this->msg( 'interwiki-logtext' )->escaped() 339 ); 340 $this->getOutput()->addHTML( '<p class="mw-interwiki-log">' . $logLink . '</p>' ); 341 342 // Add 'add' link 343 if ( $canModify ) { 344 if ( count( $iwGlobalPrefixes ) !== 0 ) { 345 $addtext = $this->msg( 'interwiki-addtext-local' )->escaped(); 346 } else { 347 $addtext = $this->msg( 'interwiki_addtext' )->escaped(); 348 } 349 $addlink = Linker::linkKnown( $this->getPageTitle( 'add' ), $addtext ); 350 $this->getOutput()->addHTML( '<p class="mw-interwiki-addlink">' . $addlink . '</p>' ); 351 } 352 353 $this->getOutput()->addWikiMsg( 'interwiki-legend' ); 354 355 if ( !is_array( $iwPrefixes ) || count( $iwPrefixes ) === 0 ) { 356 if ( !is_array( $iwGlobalPrefixes ) || count( $iwGlobalPrefixes ) === 0 ) { 357 // If the interwiki table(s) are empty, display an error message 358 $this->error( 'interwiki_error' ); 359 return; 360 } 361 } 362 363 // Add the global table 364 if ( count( $iwGlobalPrefixes ) !== 0 ) { 365 $this->getOutput()->addHTML( 366 '<h2 id="interwikitable-global">' . 367 $this->msg( 'interwiki-global-links' )->parse() . 368 '</h2>' 369 ); 370 $this->getOutput()->addWikiMsg( 'interwiki-global-description' ); 371 372 // $canModify is false here because this is just a display of remote data 373 $this->makeTable( false, $iwGlobalPrefixes ); 374 } 375 376 // Add the local table 377 if ( count( $iwLocalPrefixes ) !== 0 ) { 378 if ( count( $iwGlobalPrefixes ) !== 0 ) { 379 $this->getOutput()->addHTML( 380 '<h2 id="interwikitable-local">' . 381 $this->msg( 'interwiki-local-links' )->parse() . 382 '</h2>' 383 ); 384 $this->getOutput()->addWikiMsg( 'interwiki-local-description' ); 385 } else { 386 $this->getOutput()->addHTML( 387 '<h2 id="interwikitable-local">' . 388 $this->msg( 'interwiki-links' )->parse() . 389 '</h2>' 390 ); 391 $this->getOutput()->addWikiMsg( 'interwiki-description' ); 392 } 393 $this->makeTable( $canModify, $iwLocalPrefixes ); 394 } 395 396 // Add the language table 397 if ( count( $iwLanguagePrefixes ) !== 0 ) { 398 $this->getOutput()->addHTML( 399 '<h2 id="interwikitable-language">' . 400 $this->msg( 'interwiki-language-links' )->parse() . 401 '</h2>' 402 ); 403 $this->getOutput()->addWikiMsg( 'interwiki-language-description' ); 404 405 $this->makeTable( $canModify, $iwLanguagePrefixes ); 406 } 407 } 408 409 function makeTable( $canModify, $iwPrefixes ) { 410 // Output the existing Interwiki prefixes table header 411 $out = ''; 412 $out .= Html::openElement( 413 'table', 414 array( 'class' => 'mw-interwikitable wikitable sortable body' ) 415 ) . "\n"; 416 $out .= Html::openElement( 'tr', array( 'class' => 'interwikitable-header' ) ) . 417 Html::element( 'th', null, $this->msg( 'interwiki_prefix' )->text() ) . 418 Html::element( 'th', null, $this->msg( 'interwiki_url' )->text() ) . 419 Html::element( 'th', null, $this->msg( 'interwiki_local' )->text() ) . 420 Html::element( 'th', null, $this->msg( 'interwiki_trans' )->text() ) . 421 ( $canModify ? 422 Html::element( 423 'th', 424 array( 'class' => 'unsortable' ), 425 $this->msg( 'interwiki_edit' )->text() 426 ) : 427 '' 428 ); 429 $out .= Html::closeElement( 'tr' ) . "\n"; 430 431 $selfTitle = $this->getPageTitle(); 432 433 // Output the existing Interwiki prefixes table rows 434 foreach ( $iwPrefixes as $iwPrefix ) { 435 $out .= Html::openElement( 'tr', array( 'class' => 'mw-interwikitable-row' ) ); 436 $out .= Html::element( 'td', array( 'class' => 'mw-interwikitable-prefix' ), 437 $iwPrefix['iw_prefix'] ); 438 $out .= Html::element( 439 'td', 440 array( 'class' => 'mw-interwikitable-url' ), 441 $iwPrefix['iw_url'] 442 ); 443 $attribs = array( 'class' => 'mw-interwikitable-local' ); 444 // Green background for cells with "yes". 445 if( $iwPrefix['iw_local'] ) { 446 $attribs['class'] .= ' mw-interwikitable-local-yes'; 447 } 448 // The messages interwiki_0 and interwiki_1 are used here. 449 $contents = isset( $iwPrefix['iw_local'] ) ? 450 $this->msg( 'interwiki_' . $iwPrefix['iw_local'] )->text() : 451 '-'; 452 $out .= Html::element( 'td', $attribs, $contents ); 453 $attribs = array( 'class' => 'mw-interwikitable-trans' ); 454 // Green background for cells with "yes". 455 if( $iwPrefix['iw_trans'] ) { 456 $attribs['class'] .= ' mw-interwikitable-trans-yes'; 457 } 458 // The messages interwiki_0 and interwiki_1 are used here. 459 $contents = isset( $iwPrefix['iw_trans'] ) ? 460 $this->msg( 'interwiki_' . $iwPrefix['iw_trans'] )->text() : 461 '-'; 462 $out .= Html::element( 'td', $attribs, $contents ); 463 464 // Additional column when the interwiki table can be modified. 465 if ( $canModify ) { 466 $out .= Html::rawElement( 'td', array( 'class' => 'mw-interwikitable-modify' ), 467 Linker::linkKnown( $selfTitle, $this->msg( 'edit' )->escaped(), array(), 468 array( 'action' => 'edit', 'prefix' => $iwPrefix['iw_prefix'] ) ) . 469 $this->msg( 'comma-separator' ) . 470 Linker::linkKnown( $selfTitle, $this->msg( 'delete' )->escaped(), array(), 471 array( 'action' => 'delete', 'prefix' => $iwPrefix['iw_prefix'] ) ) 472 ); 473 } 474 $out .= Html::closeElement( 'tr' ) . "\n"; 475 } 476 $out .= Html::closeElement( 'table' ); 477 478 $this->getOutput()->addHTML( $out ); 479 } 480 481 function error() { 482 $args = func_get_args(); 483 $this->getOutput()->wrapWikiMsg( "<p class='error'>$1</p>", $args ); 484 } 485 } 486 487 /** 488 * Needed to pass the URL as a raw parameter, because it contains $1 489 */ 490 class InterwikiLogFormatter extends LogFormatter { 491 /** 492 * @return array 493 */ 494 protected function getMessageParameters() { 495 $params = parent::getMessageParameters(); 496 if ( isset( $params[4] ) ) { 497 $params[4] = Message::rawParam( htmlspecialchars( $params[4] ) ); 498 } 499 return $params; 500 } 501 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Nov 28 14:03:12 2014 | Cross-referenced by PHPXref 0.7.1 |