[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

/includes/specials/ -> SpecialDeletedContributions.php (source)

   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  }


Generated: Fri Nov 28 14:03:12 2014 Cross-referenced by PHPXref 0.7.1