[ Index ]

PHP Cross Reference of MediaWiki-1.24.0

title

Body

[close]

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

   1  <?php
   2  /**
   3   * Implements Special:Listfiles
   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  class SpecialListFiles extends IncludableSpecialPage {
  25  	public function __construct() {
  26          parent::__construct( 'Listfiles' );
  27      }
  28  
  29  	public function execute( $par ) {
  30          $this->setHeaders();
  31          $this->outputHeader();
  32  
  33          if ( $this->including() ) {
  34              $userName = $par;
  35              $search = '';
  36              $showAll = false;
  37          } else {
  38              $userName = $this->getRequest()->getText( 'user', $par );
  39              $search = $this->getRequest()->getText( 'ilsearch', '' );
  40              $showAll = $this->getRequest()->getBool( 'ilshowall', false );
  41          }
  42  
  43          $pager = new ImageListPager(
  44              $this->getContext(),
  45              $userName,
  46              $search,
  47              $this->including(),
  48              $showAll
  49          );
  50  
  51          $out = $this->getOutput();
  52          if ( $this->including() ) {
  53              $out->addParserOutputContent( $pager->getBodyOutput() );
  54          } else {
  55              $out->addHTML( $pager->getForm() );
  56              $out->addParserOutputContent( $pager->getFullOutput() );
  57          }
  58      }
  59  
  60  	protected function getGroupName() {
  61          return 'media';
  62      }
  63  }
  64  
  65  /**
  66   * @ingroup SpecialPage Pager
  67   */
  68  class ImageListPager extends TablePager {
  69      protected $mFieldNames = null;
  70  
  71      // Subclasses should override buildQueryConds instead of using $mQueryConds variable.
  72      protected $mQueryConds = array();
  73  
  74      protected $mUserName = null;
  75  
  76      protected $mSearch = '';
  77  
  78      protected $mIncluding = false;
  79  
  80      protected $mShowAll = false;
  81  
  82      protected $mTableName = 'image';
  83  
  84  	function __construct( IContextSource $context, $userName = null, $search = '',
  85          $including = false, $showAll = false
  86      ) {
  87          $this->mIncluding = $including;
  88          $this->mShowAll = $showAll;
  89  
  90          if ( $userName ) {
  91              $nt = Title::newFromText( $userName, NS_USER );
  92              if ( !is_null( $nt ) ) {
  93                  $this->mUserName = $nt->getText();
  94              }
  95          }
  96  
  97          if ( $search !== '' && !$this->getConfig()->get( 'MiserMode' ) ) {
  98              $this->mSearch = $search;
  99              $nt = Title::newFromURL( $this->mSearch );
 100  
 101              if ( $nt ) {
 102                  $dbr = wfGetDB( DB_SLAVE );
 103                  $this->mQueryConds[] = 'LOWER(img_name)' .
 104                      $dbr->buildLike( $dbr->anyString(),
 105                          strtolower( $nt->getDBkey() ), $dbr->anyString() );
 106              }
 107          }
 108  
 109          if ( !$including ) {
 110              if ( $context->getRequest()->getText( 'sort', 'img_date' ) == 'img_date' ) {
 111                  $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
 112              } else {
 113                  $this->mDefaultDirection = IndexPager::DIR_ASCENDING;
 114              }
 115          } else {
 116              $this->mDefaultDirection = IndexPager::DIR_DESCENDING;
 117          }
 118  
 119          parent::__construct( $context );
 120      }
 121  
 122      /**
 123       * Build the where clause of the query.
 124       *
 125       * Replaces the older mQueryConds member variable.
 126       * @param string $table Either "image" or "oldimage"
 127       * @return array The query conditions.
 128       */
 129  	protected function buildQueryConds( $table ) {
 130          $prefix = $table === 'image' ? 'img' : 'oi';
 131          $conds = array();
 132  
 133          if ( !is_null( $this->mUserName ) ) {
 134              $conds[$prefix . '_user_text'] = $this->mUserName;
 135          }
 136  
 137          if ( $this->mSearch !== '' ) {
 138              $nt = Title::newFromURL( $this->mSearch );
 139              if ( $nt ) {
 140                  $dbr = wfGetDB( DB_SLAVE );
 141                  $conds[] = 'LOWER(' . $prefix . '_name)' .
 142                      $dbr->buildLike( $dbr->anyString(),
 143                          strtolower( $nt->getDBkey() ), $dbr->anyString() );
 144              }
 145          }
 146  
 147          if ( $table === 'oldimage' ) {
 148              // Don't want to deal with revdel.
 149              // Future fixme: Show partial information as appropriate.
 150              // Would have to be careful about filtering by username when username is deleted.
 151              $conds['oi_deleted'] = 0;
 152          }
 153  
 154          // Add mQueryConds in case anyone was subclassing and using the old variable.
 155          return $conds + $this->mQueryConds;
 156      }
 157  
 158      /**
 159       * @return array
 160       */
 161  	function getFieldNames() {
 162          if ( !$this->mFieldNames ) {
 163              $this->mFieldNames = array(
 164                  'img_timestamp' => $this->msg( 'listfiles_date' )->text(),
 165                  'img_name' => $this->msg( 'listfiles_name' )->text(),
 166                  'thumb' => $this->msg( 'listfiles_thumb' )->text(),
 167                  'img_size' => $this->msg( 'listfiles_size' )->text(),
 168              );
 169              if ( is_null( $this->mUserName ) ) {
 170                  // Do not show username if filtering by username
 171                  $this->mFieldNames['img_user_text'] = $this->msg( 'listfiles_user' )->text();
 172              }
 173              // img_description down here, in order so that its still after the username field.
 174              $this->mFieldNames['img_description'] = $this->msg( 'listfiles_description' )->text();
 175  
 176              if ( !$this->getConfig()->get( 'MiserMode' ) && !$this->mShowAll ) {
 177                  $this->mFieldNames['count'] = $this->msg( 'listfiles_count' )->text();
 178              }
 179              if ( $this->mShowAll ) {
 180                  $this->mFieldNames['top'] = $this->msg( 'listfiles-latestversion' )->text();
 181              }
 182          }
 183  
 184          return $this->mFieldNames;
 185      }
 186  
 187  	function isFieldSortable( $field ) {
 188          if ( $this->mIncluding ) {
 189              return false;
 190          }
 191          $sortable = array( 'img_timestamp', 'img_name', 'img_size' );
 192          /* For reference, the indicies we can use for sorting are:
 193           * On the image table: img_usertext_timestamp, img_size, img_timestamp
 194           * On oldimage: oi_usertext_timestamp, oi_name_timestamp
 195           *
 196           * In particular that means we cannot sort by timestamp when not filtering
 197           * by user and including old images in the results. Which is sad.
 198           */
 199          if ( $this->getConfig()->get( 'MiserMode' ) && !is_null( $this->mUserName ) ) {
 200              // If we're sorting by user, the index only supports sorting by time.
 201              if ( $field === 'img_timestamp' ) {
 202                  return true;
 203              } else {
 204                  return false;
 205              }
 206          } elseif ( $this->getConfig()->get( 'MiserMode' ) && $this->mShowAll /* && mUserName === null */ ) {
 207              // no oi_timestamp index, so only alphabetical sorting in this case.
 208              if ( $field === 'img_name' ) {
 209                  return true;
 210              } else {
 211                  return false;
 212              }
 213          }
 214  
 215          return in_array( $field, $sortable );
 216      }
 217  
 218  	function getQueryInfo() {
 219          // Hacky Hacky Hacky - I want to get query info
 220          // for two different tables, without reimplementing
 221          // the pager class.
 222          $qi = $this->getQueryInfoReal( $this->mTableName );
 223  
 224          return $qi;
 225      }
 226  
 227      /**
 228       * Actually get the query info.
 229       *
 230       * This is to allow displaying both stuff from image and oldimage table.
 231       *
 232       * This is a bit hacky.
 233       *
 234       * @param string $table Either 'image' or 'oldimage'
 235       * @return array Query info
 236       */
 237  	protected function getQueryInfoReal( $table ) {
 238          $prefix = $table === 'oldimage' ? 'oi' : 'img';
 239  
 240          $tables = array( $table );
 241          $fields = array_keys( $this->getFieldNames() );
 242  
 243          if ( $table === 'oldimage' ) {
 244              foreach ( $fields as $id => &$field ) {
 245                  if ( substr( $field, 0, 4 ) !== 'img_' ) {
 246                      continue;
 247                  }
 248                  $field = $prefix . substr( $field, 3 ) . ' AS ' . $field;
 249              }
 250              $fields[array_search( 'top', $fields )] = "'no' AS top";
 251          } else {
 252              if ( $this->mShowAll ) {
 253                  $fields[array_search( 'top', $fields )] = "'yes' AS top";
 254              }
 255          }
 256          $fields[] = $prefix . '_user AS img_user';
 257          $fields[array_search( 'thumb', $fields )] = $prefix . '_name AS thumb';
 258  
 259          $options = $join_conds = array();
 260  
 261          # Depends on $wgMiserMode
 262          # Will also not happen if mShowAll is true.
 263          if ( isset( $this->mFieldNames['count'] ) ) {
 264              $tables[] = 'oldimage';
 265  
 266              # Need to rewrite this one
 267              foreach ( $fields as &$field ) {
 268                  if ( $field == 'count' ) {
 269                      $field = 'COUNT(oi_archive_name) AS count';
 270                  }
 271              }
 272              unset( $field );
 273  
 274              $dbr = wfGetDB( DB_SLAVE );
 275              if ( $dbr->implicitGroupby() ) {
 276                  $options = array( 'GROUP BY' => 'img_name' );
 277              } else {
 278                  $columnlist = preg_grep( '/^img/', array_keys( $this->getFieldNames() ) );
 279                  $options = array( 'GROUP BY' => array_merge( array( 'img_user' ), $columnlist ) );
 280              }
 281              $join_conds = array( 'oldimage' => array( 'LEFT JOIN', 'oi_name = img_name' ) );
 282          }
 283  
 284          return array(
 285              'tables' => $tables,
 286              'fields' => $fields,
 287              'conds' => $this->buildQueryConds( $table ),
 288              'options' => $options,
 289              'join_conds' => $join_conds
 290          );
 291      }
 292  
 293      /**
 294       * Override reallyDoQuery to mix together two queries.
 295       *
 296       * @note $asc is named $descending in IndexPager base class. However
 297       *   it is true when the order is ascending, and false when the order
 298       *   is descending, so I renamed it to $asc here.
 299       * @param int $offset
 300       * @param int $limit
 301       * @param bool $asc
 302       * @return array
 303       */
 304  	function reallyDoQuery( $offset, $limit, $asc ) {
 305          $prevTableName = $this->mTableName;
 306          $this->mTableName = 'image';
 307          list( $tables, $fields, $conds, $fname, $options, $join_conds ) =
 308              $this->buildQueryInfo( $offset, $limit, $asc );
 309          $imageRes = $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds );
 310          $this->mTableName = $prevTableName;
 311  
 312          if ( !$this->mShowAll ) {
 313              return $imageRes;
 314          }
 315  
 316          $this->mTableName = 'oldimage';
 317  
 318          # Hacky...
 319          $oldIndex = $this->mIndexField;
 320          if ( substr( $this->mIndexField, 0, 4 ) !== 'img_' ) {
 321              throw new MWException( "Expected to be sorting on an image table field" );
 322          }
 323          $this->mIndexField = 'oi_' . substr( $this->mIndexField, 4 );
 324  
 325          list( $tables, $fields, $conds, $fname, $options, $join_conds ) =
 326              $this->buildQueryInfo( $offset, $limit, $asc );
 327          $oldimageRes = $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds );
 328  
 329          $this->mTableName = $prevTableName;
 330          $this->mIndexField = $oldIndex;
 331  
 332          return $this->combineResult( $imageRes, $oldimageRes, $limit, $asc );
 333      }
 334  
 335      /**
 336       * Combine results from 2 tables.
 337       *
 338       * Note: This will throw away some results
 339       *
 340       * @param ResultWrapper $res1
 341       * @param ResultWrapper $res2
 342       * @param int $limit
 343       * @param bool $ascending See note about $asc in $this->reallyDoQuery
 344       * @return FakeResultWrapper $res1 and $res2 combined
 345       */
 346  	protected function combineResult( $res1, $res2, $limit, $ascending ) {
 347          $res1->rewind();
 348          $res2->rewind();
 349          $topRes1 = $res1->next();
 350          $topRes2 = $res2->next();
 351          $resultArray = array();
 352          for ( $i = 0; $i < $limit && $topRes1 && $topRes2; $i++ ) {
 353              if ( strcmp( $topRes1->{$this->mIndexField}, $topRes2->{$this->mIndexField} ) > 0 ) {
 354                  if ( !$ascending ) {
 355                      $resultArray[] = $topRes1;
 356                      $topRes1 = $res1->next();
 357                  } else {
 358                      $resultArray[] = $topRes2;
 359                      $topRes2 = $res2->next();
 360                  }
 361              } else {
 362                  if ( !$ascending ) {
 363                      $resultArray[] = $topRes2;
 364                      $topRes2 = $res2->next();
 365                  } else {
 366                      $resultArray[] = $topRes1;
 367                      $topRes1 = $res1->next();
 368                  }
 369              }
 370          }
 371  
 372          // @codingStandardsIgnoreStart Squiz.WhiteSpace.SemicolonSpacing.Incorrect
 373          for ( ; $i < $limit && $topRes1; $i++ ) {
 374              // @codingStandardsIgnoreEnd
 375              $resultArray[] = $topRes1;
 376              $topRes1 = $res1->next();
 377          }
 378  
 379          // @codingStandardsIgnoreStart Squiz.WhiteSpace.SemicolonSpacing.Incorrect
 380          for ( ; $i < $limit && $topRes2; $i++ ) {
 381              // @codingStandardsIgnoreEnd
 382              $resultArray[] = $topRes2;
 383              $topRes2 = $res2->next();
 384          }
 385  
 386          return new FakeResultWrapper( $resultArray );
 387      }
 388  
 389  	function getDefaultSort() {
 390          if ( $this->mShowAll && $this->getConfig()->get( 'MiserMode' ) && is_null( $this->mUserName ) ) {
 391              // Unfortunately no index on oi_timestamp.
 392              return 'img_name';
 393          } else {
 394              return 'img_timestamp';
 395          }
 396      }
 397  
 398  	function doBatchLookups() {
 399          $userIds = array();
 400          $this->mResult->seek( 0 );
 401          foreach ( $this->mResult as $row ) {
 402              $userIds[] = $row->img_user;
 403          }
 404          # Do a link batch query for names and userpages
 405          UserCache::singleton()->doQuery( $userIds, array( 'userpage' ), __METHOD__ );
 406      }
 407  
 408      /**
 409       * @param string $field
 410       * @param string $value
 411       * @return Message|string|int The return type depends on the value of $field:
 412       *   - thumb: string
 413       *   - img_timestamp: string
 414       *   - img_name: string
 415       *   - img_user_text: string
 416       *   - img_size: string
 417       *   - img_description: string
 418       *   - count: int
 419       *   - top: Message
 420       * @throws MWException
 421       */
 422  	function formatValue( $field, $value ) {
 423          switch ( $field ) {
 424              case 'thumb':
 425                  $opt = array( 'time' => $this->mCurrentRow->img_timestamp );
 426                  $file = RepoGroup::singleton()->getLocalRepo()->findFile( $value, $opt );
 427                  // If statement for paranoia
 428                  if ( $file ) {
 429                      $thumb = $file->transform( array( 'width' => 180, 'height' => 360 ) );
 430  
 431                      return $thumb->toHtml( array( 'desc-link' => true ) );
 432                  } else {
 433                      return htmlspecialchars( $value );
 434                  }
 435              case 'img_timestamp':
 436                  // We may want to make this a link to the "old" version when displaying old files
 437                  return htmlspecialchars( $this->getLanguage()->userTimeAndDate( $value, $this->getUser() ) );
 438              case 'img_name':
 439                  static $imgfile = null;
 440                  if ( $imgfile === null ) {
 441                      $imgfile = $this->msg( 'imgfile' )->text();
 442                  }
 443  
 444                  // Weird files can maybe exist? Bug 22227
 445                  $filePage = Title::makeTitleSafe( NS_FILE, $value );
 446                  if ( $filePage ) {
 447                      $link = Linker::linkKnown(
 448                          $filePage,
 449                          htmlspecialchars( $filePage->getText() )
 450                      );
 451                      $download = Xml::element( 'a',
 452                          array( 'href' => wfLocalFile( $filePage )->getURL() ),
 453                          $imgfile
 454                      );
 455                      $download = $this->msg( 'parentheses' )->rawParams( $download )->escaped();
 456  
 457                      // Add delete links if allowed
 458                      // From https://github.com/Wikia/app/pull/3859
 459                      if ( $filePage->userCan( 'delete', $this->getUser() ) ) {
 460                          $deleteMsg = $this->msg( 'listfiles-delete' )->escaped();
 461  
 462                          $delete = Linker::linkKnown(
 463                              $filePage, $deleteMsg, array(), array( 'action' => 'delete' )
 464                          );
 465                          $delete = $this->msg( 'parentheses' )->rawParams( $delete )->escaped();
 466  
 467                          return "$link $download $delete";
 468                      }
 469  
 470                      return "$link $download";
 471                  } else {
 472                      return htmlspecialchars( $value );
 473                  }
 474              case 'img_user_text':
 475                  if ( $this->mCurrentRow->img_user ) {
 476                      $name = User::whoIs( $this->mCurrentRow->img_user );
 477                      $link = Linker::link(
 478                          Title::makeTitle( NS_USER, $name ),
 479                          htmlspecialchars( $name )
 480                      );
 481                  } else {
 482                      $link = htmlspecialchars( $value );
 483                  }
 484  
 485                  return $link;
 486              case 'img_size':
 487                  return htmlspecialchars( $this->getLanguage()->formatSize( $value ) );
 488              case 'img_description':
 489                  return Linker::formatComment( $value );
 490              case 'count':
 491                  return intval( $value ) + 1;
 492              case 'top':
 493                  // Messages: listfiles-latestversion-yes, listfiles-latestversion-no
 494                  return $this->msg( 'listfiles-latestversion-' . $value );
 495              default:
 496                  throw new MWException( "Unknown field '$field'" );
 497          }
 498      }
 499  
 500  	function getForm() {
 501          $fields = array();
 502          $fields['limit'] = array(
 503              'type' => 'select',
 504              'name' => 'limit',
 505              'label-message' => 'table_pager_limit_label',
 506              'options' => $this->getLimitSelectList(),
 507              'default' => $this->mLimit,
 508          );
 509  
 510          if ( !$this->getConfig()->get( 'MiserMode' ) ) {
 511              $fields['ilsearch'] = array(
 512                  'type' => 'text',
 513                  'name' => 'ilsearch',
 514                  'id' => 'mw-ilsearch',
 515                  'label-message' => 'listfiles_search_for',
 516                  'default' => $this->mSearch,
 517                  'size' => '40',
 518                  'maxlength' => '255',
 519              );
 520          }
 521  
 522          $fields['user'] = array(
 523              'type' => 'text',
 524              'name' => 'user',
 525              'id' => 'mw-listfiles-user',
 526              'label-message' => 'username',
 527              'default' => $this->mUserName,
 528              'size' => '40',
 529              'maxlength' => '255',
 530          );
 531  
 532          $fields['ilshowall'] = array(
 533              'type' => 'check',
 534              'name' => 'ilshowall',
 535              'id' => 'mw-listfiles-show-all',
 536              'label-message' => 'listfiles-show-all',
 537              'default' => $this->mShowAll,
 538          );
 539  
 540          $query = $this->getRequest()->getQueryValues();
 541          unset( $query['title'] );
 542          unset( $query['limit'] );
 543          unset( $query['ilsearch'] );
 544          unset( $query['user'] );
 545  
 546          $form = new HTMLForm( $fields, $this->getContext() );
 547  
 548          $form->setMethod( 'get' );
 549          $form->setTitle( $this->getTitle() );
 550          $form->setId( 'mw-listfiles-form' );
 551          $form->setWrapperLegendMsg( 'listfiles' );
 552          $form->setSubmitTextMsg( 'table_pager_limit_submit' );
 553          $form->addHiddenFields( $query );
 554  
 555          $form->prepareForm();
 556          $form->displayForm( '' );
 557      }
 558  
 559  	function getTableClass() {
 560          return parent::getTableClass() . ' listfiles';
 561      }
 562  
 563  	function getNavClass() {
 564          return parent::getNavClass() . ' listfiles_nav';
 565      }
 566  
 567  	function getSortHeaderClass() {
 568          return parent::getSortHeaderClass() . ' listfiles_sort';
 569      }
 570  
 571  	function getPagingQueries() {
 572          $queries = parent::getPagingQueries();
 573          if ( !is_null( $this->mUserName ) ) {
 574              # Append the username to the query string
 575              foreach ( $queries as &$query ) {
 576                  if ( $query !== false ) {
 577                      $query['user'] = $this->mUserName;
 578                  }
 579              }
 580          }
 581  
 582          return $queries;
 583      }
 584  
 585  	function getDefaultQuery() {
 586          $queries = parent::getDefaultQuery();
 587          if ( !isset( $queries['user'] ) && !is_null( $this->mUserName ) ) {
 588              $queries['user'] = $this->mUserName;
 589          }
 590  
 591          return $queries;
 592      }
 593  
 594  	function getTitle() {
 595          return SpecialPage::getTitleFor( 'Listfiles' );
 596      }
 597  }


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