[ Index ] |
PHP Cross Reference of MediaWiki-1.24.0 |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Implements Special:DeletedContributions 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License along 16 * with this program; if not, write to the Free Software Foundation, Inc., 17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 18 * http://www.gnu.org/copyleft/gpl.html 19 * 20 * @file 21 * @ingroup SpecialPage 22 */ 23 24 /** 25 * Implements Special:DeletedContributions to display archived revisions 26 * @ingroup SpecialPage 27 */ 28 class DeletedContribsPager extends IndexPager { 29 public $mDefaultDirection = IndexPager::DIR_DESCENDING; 30 public $messages; 31 public $target; 32 public $namespace = ''; 33 public $mDb; 34 35 /** 36 * @var string Navigation bar with paging links. 37 */ 38 protected $mNavigationBar; 39 40 function __construct( IContextSource $context, $target, $namespace = false ) { 41 parent::__construct( $context ); 42 $msgs = array( 'deletionlog', 'undeleteviewlink', 'diff' ); 43 foreach ( $msgs as $msg ) { 44 $this->messages[$msg] = $this->msg( $msg )->escaped(); 45 } 46 $this->target = $target; 47 $this->namespace = $namespace; 48 $this->mDb = wfGetDB( DB_SLAVE, 'contributions' ); 49 } 50 51 function getDefaultQuery() { 52 $query = parent::getDefaultQuery(); 53 $query['target'] = $this->target; 54 55 return $query; 56 } 57 58 function getQueryInfo() { 59 list( $index, $userCond ) = $this->getUserCond(); 60 $conds = array_merge( $userCond, $this->getNamespaceCond() ); 61 $user = $this->getUser(); 62 // Paranoia: avoid brute force searches (bug 17792) 63 if ( !$user->isAllowed( 'deletedhistory' ) ) { 64 $conds[] = $this->mDb->bitAnd( 'ar_deleted', Revision::DELETED_USER ) . ' = 0'; 65 } elseif ( !$user->isAllowedAny( 'suppressrevision', 'viewsuppressed' ) ) { 66 $conds[] = $this->mDb->bitAnd( 'ar_deleted', Revision::SUPPRESSED_USER ) . 67 ' != ' . Revision::SUPPRESSED_USER; 68 } 69 70 return array( 71 'tables' => array( 'archive' ), 72 'fields' => array( 73 'ar_rev_id', 'ar_namespace', 'ar_title', 'ar_timestamp', 'ar_comment', 74 'ar_minor_edit', 'ar_user', 'ar_user_text', 'ar_deleted' 75 ), 76 'conds' => $conds, 77 'options' => array( 'USE INDEX' => $index ) 78 ); 79 } 80 81 function getUserCond() { 82 $condition = array(); 83 84 $condition['ar_user_text'] = $this->target; 85 $index = 'usertext_timestamp'; 86 87 return array( $index, $condition ); 88 } 89 90 function getIndexField() { 91 return 'ar_timestamp'; 92 } 93 94 function getStartBody() { 95 return "<ul>\n"; 96 } 97 98 function getEndBody() { 99 return "</ul>\n"; 100 } 101 102 function getNavigationBar() { 103 if ( isset( $this->mNavigationBar ) ) { 104 return $this->mNavigationBar; 105 } 106 107 $linkTexts = array( 108 'prev' => $this->msg( 'pager-newer-n' )->numParams( $this->mLimit )->escaped(), 109 'next' => $this->msg( 'pager-older-n' )->numParams( $this->mLimit )->escaped(), 110 'first' => $this->msg( 'histlast' )->escaped(), 111 'last' => $this->msg( 'histfirst' )->escaped() 112 ); 113 114 $pagingLinks = $this->getPagingLinks( $linkTexts ); 115 $limitLinks = $this->getLimitLinks(); 116 $lang = $this->getLanguage(); 117 $limits = $lang->pipeList( $limitLinks ); 118 119 $firstLast = $lang->pipeList( array( $pagingLinks['first'], $pagingLinks['last'] ) ); 120 $firstLast = $this->msg( 'parentheses' )->rawParams( $firstLast )->escaped(); 121 $prevNext = $this->msg( 'viewprevnext' ) 122 ->rawParams( 123 $pagingLinks['prev'], 124 $pagingLinks['next'], 125 $limits 126 )->escaped(); 127 $separator = $this->msg( 'word-separator' )->escaped(); 128 $this->mNavigationBar = $firstLast . $separator . $prevNext; 129 130 return $this->mNavigationBar; 131 } 132 133 function getNamespaceCond() { 134 if ( $this->namespace !== '' ) { 135 return array( 'ar_namespace' => (int)$this->namespace ); 136 } else { 137 return array(); 138 } 139 } 140 141 /** 142 * Generates each row in the contributions list. 143 * 144 * Contributions which are marked "top" are currently on top of the history. 145 * For these contributions, a [rollback] link is shown for users with sysop 146 * privileges. The rollback link restores the most recent version that was not 147 * written by the target user. 148 * 149 * @todo This would probably look a lot nicer in a table. 150 * @param stdClass $row 151 * @return string 152 */ 153 function formatRow( $row ) { 154 wfProfileIn( __METHOD__ ); 155 156 $page = Title::makeTitle( $row->ar_namespace, $row->ar_title ); 157 158 $rev = new Revision( array( 159 'title' => $page, 160 'id' => $row->ar_rev_id, 161 'comment' => $row->ar_comment, 162 'user' => $row->ar_user, 163 'user_text' => $row->ar_user_text, 164 'timestamp' => $row->ar_timestamp, 165 'minor_edit' => $row->ar_minor_edit, 166 'deleted' => $row->ar_deleted, 167 ) ); 168 169 $undelete = SpecialPage::getTitleFor( 'Undelete' ); 170 171 $logs = SpecialPage::getTitleFor( 'Log' ); 172 $dellog = Linker::linkKnown( 173 $logs, 174 $this->messages['deletionlog'], 175 array(), 176 array( 177 'type' => 'delete', 178 'page' => $page->getPrefixedText() 179 ) 180 ); 181 182 $reviewlink = Linker::linkKnown( 183 SpecialPage::getTitleFor( 'Undelete', $page->getPrefixedDBkey() ), 184 $this->messages['undeleteviewlink'] 185 ); 186 187 $user = $this->getUser(); 188 189 if ( $user->isAllowed( 'deletedtext' ) ) { 190 $last = Linker::linkKnown( 191 $undelete, 192 $this->messages['diff'], 193 array(), 194 array( 195 'target' => $page->getPrefixedText(), 196 'timestamp' => $rev->getTimestamp(), 197 'diff' => 'prev' 198 ) 199 ); 200 } else { 201 $last = $this->messages['diff']; 202 } 203 204 $comment = Linker::revComment( $rev ); 205 $date = $this->getLanguage()->userTimeAndDate( $rev->getTimestamp(), $user ); 206 $date = htmlspecialchars( $date ); 207 208 if ( !$user->isAllowed( 'undelete' ) || !$rev->userCan( Revision::DELETED_TEXT, $user ) ) { 209 $link = $date; // unusable link 210 } else { 211 $link = Linker::linkKnown( 212 $undelete, 213 $date, 214 array( 'class' => 'mw-changeslist-date' ), 215 array( 216 'target' => $page->getPrefixedText(), 217 'timestamp' => $rev->getTimestamp() 218 ) 219 ); 220 } 221 // Style deleted items 222 if ( $rev->isDeleted( Revision::DELETED_TEXT ) ) { 223 $link = '<span class="history-deleted">' . $link . '</span>'; 224 } 225 226 $pagelink = Linker::link( 227 $page, 228 null, 229 array( 'class' => 'mw-changeslist-title' ) 230 ); 231 232 if ( $rev->isMinor() ) { 233 $mflag = ChangesList::flag( 'minor' ); 234 } else { 235 $mflag = ''; 236 } 237 238 // Revision delete link 239 $del = Linker::getRevDeleteLink( $user, $rev, $page ); 240 if ( $del ) { 241 $del .= ' '; 242 } 243 244 $tools = Html::rawElement( 245 'span', 246 array( 'class' => 'mw-deletedcontribs-tools' ), 247 $this->msg( 'parentheses' )->rawParams( $this->getLanguage()->pipeList( 248 array( $last, $dellog, $reviewlink ) ) )->escaped() 249 ); 250 251 $separator = '<span class="mw-changeslist-separator">. .</span>'; 252 $ret = "{$del}{$link} {$tools} {$separator} {$mflag} {$pagelink} {$comment}"; 253 254 # Denote if username is redacted for this edit 255 if ( $rev->isDeleted( Revision::DELETED_USER ) ) { 256 $ret .= " <strong>" . $this->msg( 'rev-deleted-user-contribs' )->escaped() . "</strong>"; 257 } 258 259 $ret = Html::rawElement( 'li', array(), $ret ) . "\n"; 260 261 wfProfileOut( __METHOD__ ); 262 263 return $ret; 264 } 265 266 /** 267 * Get the Database object in use 268 * 269 * @return DatabaseBase 270 */ 271 public function getDatabase() { 272 return $this->mDb; 273 } 274 } 275 276 class DeletedContributionsPage extends SpecialPage { 277 function __construct() { 278 parent::__construct( 'DeletedContributions', 'deletedhistory', 279 /*listed*/true, /*function*/false, /*file*/false ); 280 } 281 282 /** 283 * Special page "deleted user contributions". 284 * Shows a list of the deleted contributions of a user. 285 * 286 * @param string $par (optional) user name of the user for which to show the contributions 287 */ 288 function execute( $par ) { 289 $this->setHeaders(); 290 $this->outputHeader(); 291 292 $user = $this->getUser(); 293 294 if ( !$this->userCanExecute( $user ) ) { 295 $this->displayRestrictionError(); 296 297 return; 298 } 299 300 $request = $this->getRequest(); 301 $out = $this->getOutput(); 302 $out->setPageTitle( $this->msg( 'deletedcontributions-title' ) ); 303 304 $options = array(); 305 306 if ( $par !== null ) { 307 $target = $par; 308 } else { 309 $target = $request->getVal( 'target' ); 310 } 311 312 if ( !strlen( $target ) ) { 313 $out->addHTML( $this->getForm( '' ) ); 314 315 return; 316 } 317 318 $options['limit'] = $request->getInt( 'limit', $this->getConfig()->get( 'QueryPageDefaultLimit' ) ); 319 $options['target'] = $target; 320 321 $userObj = User::newFromName( $target, false ); 322 if ( !$userObj ) { 323 $out->addHTML( $this->getForm( '' ) ); 324 325 return; 326 } 327 $this->getSkin()->setRelevantUser( $userObj ); 328 329 $target = $userObj->getName(); 330 $out->addSubtitle( $this->getSubTitle( $userObj ) ); 331 332 if ( ( $ns = $request->getVal( 'namespace', null ) ) !== null && $ns !== '' ) { 333 $options['namespace'] = intval( $ns ); 334 } else { 335 $options['namespace'] = ''; 336 } 337 338 $out->addHTML( $this->getForm( $options ) ); 339 340 $pager = new DeletedContribsPager( $this->getContext(), $target, $options['namespace'] ); 341 if ( !$pager->getNumRows() ) { 342 $out->addWikiMsg( 'nocontribs' ); 343 344 return; 345 } 346 347 # Show a message about slave lag, if applicable 348 $lag = wfGetLB()->safeGetLag( $pager->getDatabase() ); 349 if ( $lag > 0 ) { 350 $out->showLagWarning( $lag ); 351 } 352 353 $out->addHTML( 354 '<p>' . $pager->getNavigationBar() . '</p>' . 355 $pager->getBody() . 356 '<p>' . $pager->getNavigationBar() . '</p>' ); 357 358 # If there were contributions, and it was a valid user or IP, show 359 # the appropriate "footer" message - WHOIS tools, etc. 360 if ( $target != 'newbies' ) { 361 $message = IP::isIPAddress( $target ) ? 362 'sp-contributions-footer-anon' : 363 'sp-contributions-footer'; 364 365 if ( !$this->msg( $message )->isDisabled() ) { 366 $out->wrapWikiMsg( 367 "<div class='mw-contributions-footer'>\n$1\n</div>", 368 array( $message, $target ) 369 ); 370 } 371 } 372 } 373 374 /** 375 * Generates the subheading with links 376 * @param User $userObj User object for the target 377 * @return string Appropriately-escaped HTML to be output literally 378 * @todo FIXME: Almost the same as contributionsSub in SpecialContributions.php. Could be combined. 379 */ 380 function getSubTitle( $userObj ) { 381 if ( $userObj->isAnon() ) { 382 $user = htmlspecialchars( $userObj->getName() ); 383 } else { 384 $user = Linker::link( $userObj->getUserPage(), htmlspecialchars( $userObj->getName() ) ); 385 } 386 $links = ''; 387 $nt = $userObj->getUserPage(); 388 $id = $userObj->getID(); 389 $talk = $nt->getTalkPage(); 390 if ( $talk ) { 391 # Talk page link 392 $tools[] = Linker::link( $talk, $this->msg( 'sp-contributions-talk' )->escaped() ); 393 if ( ( $id !== null ) || ( $id === null && IP::isIPAddress( $nt->getText() ) ) ) { 394 # Block / Change block / Unblock links 395 if ( $this->getUser()->isAllowed( 'block' ) ) { 396 if ( $userObj->isBlocked() ) { 397 $tools[] = Linker::linkKnown( # Change block link 398 SpecialPage::getTitleFor( 'Block', $nt->getDBkey() ), 399 $this->msg( 'change-blocklink' )->escaped() 400 ); 401 $tools[] = Linker::linkKnown( # Unblock link 402 SpecialPage::getTitleFor( 'BlockList' ), 403 $this->msg( 'unblocklink' )->escaped(), 404 array(), 405 array( 406 'action' => 'unblock', 407 'ip' => $nt->getDBkey() 408 ) 409 ); 410 } else { 411 # User is not blocked 412 $tools[] = Linker::linkKnown( # Block link 413 SpecialPage::getTitleFor( 'Block', $nt->getDBkey() ), 414 $this->msg( 'blocklink' )->escaped() 415 ); 416 } 417 } 418 # Block log link 419 $tools[] = Linker::linkKnown( 420 SpecialPage::getTitleFor( 'Log' ), 421 $this->msg( 'sp-contributions-blocklog' )->escaped(), 422 array(), 423 array( 424 'type' => 'block', 425 'page' => $nt->getPrefixedText() 426 ) 427 ); 428 # Suppression log link (bug 59120) 429 if ( $this->getUser()->isAllowed( 'suppressionlog' ) ) { 430 $tools[] = Linker::linkKnown( 431 SpecialPage::getTitleFor( 'Log', 'suppress' ), 432 $this->msg( 'sp-contributions-suppresslog' )->escaped(), 433 array(), 434 array( 'offender' => $userObj->getName() ) 435 ); 436 } 437 } 438 439 # Uploads 440 $tools[] = Linker::linkKnown( 441 SpecialPage::getTitleFor( 'Listfiles', $userObj->getName() ), 442 $this->msg( 'sp-contributions-uploads' )->escaped() 443 ); 444 445 # Other logs link 446 $tools[] = Linker::linkKnown( 447 SpecialPage::getTitleFor( 'Log' ), 448 $this->msg( 'sp-contributions-logs' )->escaped(), 449 array(), 450 array( 'user' => $nt->getText() ) 451 ); 452 # Link to contributions 453 $tools[] = Linker::linkKnown( 454 SpecialPage::getTitleFor( 'Contributions', $nt->getDBkey() ), 455 $this->msg( 'sp-deletedcontributions-contribs' )->escaped() 456 ); 457 458 # Add a link to change user rights for privileged users 459 $userrightsPage = new UserrightsPage(); 460 $userrightsPage->setContext( $this->getContext() ); 461 if ( $userrightsPage->userCanChangeRights( $userObj ) ) { 462 $tools[] = Linker::linkKnown( 463 SpecialPage::getTitleFor( 'Userrights', $nt->getDBkey() ), 464 $this->msg( 'sp-contributions-userrights' )->escaped() 465 ); 466 } 467 468 wfRunHooks( 'ContributionsToolLinks', array( $id, $nt, &$tools ) ); 469 470 $links = $this->getLanguage()->pipeList( $tools ); 471 472 // Show a note if the user is blocked and display the last block log entry. 473 $block = Block::newFromTarget( $userObj, $userObj ); 474 if ( !is_null( $block ) && $block->getType() != Block::TYPE_AUTO ) { 475 if ( $block->getType() == Block::TYPE_RANGE ) { 476 $nt = MWNamespace::getCanonicalName( NS_USER ) . ':' . $block->getTarget(); 477 } 478 479 // LogEventsList::showLogExtract() wants the first parameter by ref 480 $out = $this->getOutput(); 481 LogEventsList::showLogExtract( 482 $out, 483 'block', 484 $nt, 485 '', 486 array( 487 'lim' => 1, 488 'showIfEmpty' => false, 489 'msgKey' => array( 490 'sp-contributions-blocked-notice', 491 $userObj->getName() # Support GENDER in 'sp-contributions-blocked-notice' 492 ), 493 'offset' => '' # don't use $this->getRequest() parameter offset 494 ) 495 ); 496 } 497 } 498 499 return $this->msg( 'contribsub2' )->rawParams( $user, $links )->params( $userObj->getName() ); 500 } 501 502 /** 503 * Generates the namespace selector form with hidden attributes. 504 * @param array $options The options to be included. 505 * @return string 506 */ 507 function getForm( $options ) { 508 $options['title'] = $this->getPageTitle()->getPrefixedText(); 509 if ( !isset( $options['target'] ) ) { 510 $options['target'] = ''; 511 } else { 512 $options['target'] = str_replace( '_', ' ', $options['target'] ); 513 } 514 515 if ( !isset( $options['namespace'] ) ) { 516 $options['namespace'] = ''; 517 } 518 519 if ( !isset( $options['contribs'] ) ) { 520 $options['contribs'] = 'user'; 521 } 522 523 if ( $options['contribs'] == 'newbie' ) { 524 $options['target'] = ''; 525 } 526 527 $f = Xml::openElement( 'form', array( 'method' => 'get', 'action' => wfScript() ) ); 528 529 foreach ( $options as $name => $value ) { 530 if ( in_array( $name, array( 'namespace', 'target', 'contribs' ) ) ) { 531 continue; 532 } 533 $f .= "\t" . Html::hidden( $name, $value ) . "\n"; 534 } 535 536 $f .= Xml::openElement( 'fieldset' ); 537 $f .= Xml::element( 'legend', array(), $this->msg( 'sp-contributions-search' )->text() ); 538 $f .= Xml::tags( 539 'label', 540 array( 'for' => 'target' ), 541 $this->msg( 'sp-contributions-username' )->parse() 542 ) . ' '; 543 $f .= Html::input( 544 'target', 545 $options['target'], 546 'text', 547 array( 548 'size' => '20', 549 'required' => '' 550 ) + ( $options['target'] ? array() : array( 'autofocus' ) ) 551 ) . ' '; 552 $f .= Html::namespaceSelector( 553 array( 554 'selected' => $options['namespace'], 555 'all' => '', 556 'label' => $this->msg( 'namespace' )->text() 557 ), 558 array( 559 'name' => 'namespace', 560 'id' => 'namespace', 561 'class' => 'namespaceselector', 562 ) 563 ) . ' '; 564 $f .= Xml::submitButton( $this->msg( 'sp-contributions-submit' )->text() ); 565 $f .= Xml::closeElement( 'fieldset' ); 566 $f .= Xml::closeElement( 'form' ); 567 568 return $f; 569 } 570 571 protected function getGroupName() { 572 return 'users'; 573 } 574 }
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 |