MediaWiki  REL1_23
SpecialListfiles.php
Go to the documentation of this file.
00001 <?php
00024 class SpecialListFiles extends IncludableSpecialPage {
00025     public function __construct() {
00026         parent::__construct( 'Listfiles' );
00027     }
00028 
00029     public function execute( $par ) {
00030         $this->setHeaders();
00031         $this->outputHeader();
00032 
00033         if ( $this->including() ) {
00034             $userName = $par;
00035             $search = '';
00036             $showAll = false;
00037         } else {
00038             $userName = $this->getRequest()->getText( 'user', $par );
00039             $search = $this->getRequest()->getText( 'ilsearch', '' );
00040             $showAll = $this->getRequest()->getBool( 'ilshowall', false );
00041         }
00042 
00043         $pager = new ImageListPager(
00044             $this->getContext(),
00045             $userName,
00046             $search,
00047             $this->including(),
00048             $showAll
00049         );
00050 
00051         if ( $this->including() ) {
00052             $html = $pager->getBody();
00053         } else {
00054             $form = $pager->getForm();
00055             $body = $pager->getBody();
00056             $nav = $pager->getNavigationBar();
00057             $html = "$form<br />\n$body<br />\n$nav";
00058         }
00059         $this->getOutput()->addHTML( $html );
00060     }
00061 
00062     protected function getGroupName() {
00063         return 'media';
00064     }
00065 }
00066 
00070 class ImageListPager extends TablePager {
00071     var $mFieldNames = null;
00072     // Subclasses should override buildQueryConds instead of using $mQueryConds variable.
00073     var $mQueryConds = array();
00074     var $mUserName = null;
00075     var $mSearch = '';
00076     var $mIncluding = false;
00077     var $mShowAll = false;
00078     var $mTableName = 'image';
00079 
00080     function __construct( IContextSource $context, $userName = null, $search = '',
00081         $including = false, $showAll = false
00082     ) {
00083         global $wgMiserMode;
00084 
00085         $this->mIncluding = $including;
00086         $this->mShowAll = $showAll;
00087 
00088         if ( $userName ) {
00089             $nt = Title::newFromText( $userName, NS_USER );
00090             if ( !is_null( $nt ) ) {
00091                 $this->mUserName = $nt->getText();
00092             }
00093         }
00094 
00095         if ( $search !== '' && !$wgMiserMode ) {
00096             $this->mSearch = $search;
00097             $nt = Title::newFromURL( $this->mSearch );
00098 
00099             if ( $nt ) {
00100                 $dbr = wfGetDB( DB_SLAVE );
00101                 $this->mQueryConds[] = 'LOWER(img_name)' .
00102                     $dbr->buildLike( $dbr->anyString(),
00103                         strtolower( $nt->getDBkey() ), $dbr->anyString() );
00104             }
00105         }
00106 
00107         if ( !$including ) {
00108             if ( $context->getRequest()->getText( 'sort', 'img_date' ) == 'img_date' ) {
00109                 $this->mDefaultDirection = true;
00110             } else {
00111                 $this->mDefaultDirection = false;
00112             }
00113         } else {
00114             $this->mDefaultDirection = true;
00115         }
00116 
00117         parent::__construct( $context );
00118     }
00119 
00127     protected function buildQueryConds( $table ) {
00128         $prefix = $table === 'image' ? 'img' : 'oi';
00129         $conds = array();
00130 
00131         if ( !is_null( $this->mUserName ) ) {
00132             $conds[$prefix . '_user_text'] = $this->mUserName;
00133         }
00134 
00135         if ( $this->mSearch !== '' ) {
00136             $nt = Title::newFromURL( $this->mSearch );
00137             if ( $nt ) {
00138                 $dbr = wfGetDB( DB_SLAVE );
00139                 $conds[] = 'LOWER(' . $prefix . '_name)' .
00140                     $dbr->buildLike( $dbr->anyString(),
00141                         strtolower( $nt->getDBkey() ), $dbr->anyString() );
00142             }
00143         }
00144 
00145         if ( $table === 'oldimage' ) {
00146             // Don't want to deal with revdel.
00147             // Future fixme: Show partial information as appropriate.
00148             // Would have to be careful about filtering by username when username is deleted.
00149             $conds['oi_deleted'] = 0;
00150         }
00151 
00152         // Add mQueryConds in case anyone was subclassing and using the old variable.
00153         return $conds + $this->mQueryConds;
00154     }
00155 
00159     function getFieldNames() {
00160         if ( !$this->mFieldNames ) {
00161             global $wgMiserMode;
00162             $this->mFieldNames = array(
00163                 'img_timestamp' => $this->msg( 'listfiles_date' )->text(),
00164                 'img_name' => $this->msg( 'listfiles_name' )->text(),
00165                 'thumb' => $this->msg( 'listfiles_thumb' )->text(),
00166                 'img_size' => $this->msg( 'listfiles_size' )->text(),
00167                 'img_user_text' => $this->msg( 'listfiles_user' )->text(),
00168                 'img_description' => $this->msg( 'listfiles_description' )->text(),
00169             );
00170             if ( !$wgMiserMode && !$this->mShowAll ) {
00171                 $this->mFieldNames['count'] = $this->msg( 'listfiles_count' )->text();
00172             }
00173             if ( $this->mShowAll ) {
00174                 $this->mFieldNames['top'] = $this->msg( 'listfiles-latestversion' )->text();
00175             }
00176         }
00177 
00178         return $this->mFieldNames;
00179     }
00180 
00181     function isFieldSortable( $field ) {
00182         global $wgMiserMode;
00183         if ( $this->mIncluding ) {
00184             return false;
00185         }
00186         $sortable = array( 'img_timestamp', 'img_name', 'img_size' );
00187         /* For reference, the indicies we can use for sorting are:
00188          * On the image table: img_usertext_timestamp, img_size, img_timestamp
00189          * On oldimage: oi_usertext_timestamp, oi_name_timestamp
00190          *
00191          * In particular that means we cannot sort by timestamp when not filtering
00192          * by user and including old images in the results. Which is sad.
00193          */
00194         if ( $wgMiserMode && !is_null( $this->mUserName ) ) {
00195             // If we're sorting by user, the index only supports sorting by time.
00196             if ( $field === 'img_timestamp' ) {
00197                 return true;
00198             } else {
00199                 return false;
00200             }
00201         } elseif ( $wgMiserMode && $this->mShowAll /* && mUserName === null */ ) {
00202             // no oi_timestamp index, so only alphabetical sorting in this case.
00203             if ( $field === 'img_name' ) {
00204                 return true;
00205             } else {
00206                 return false;
00207             }
00208         }
00209 
00210         return in_array( $field, $sortable );
00211     }
00212 
00213     function getQueryInfo() {
00214         // Hacky Hacky Hacky - I want to get query info
00215         // for two different tables, without reimplementing
00216         // the pager class.
00217         $qi = $this->getQueryInfoReal( $this->mTableName );
00218 
00219         return $qi;
00220     }
00221 
00232     protected function getQueryInfoReal( $table ) {
00233         $prefix = $table === 'oldimage' ? 'oi' : 'img';
00234 
00235         $tables = array( $table );
00236         $fields = array_keys( $this->getFieldNames() );
00237 
00238         if ( $table === 'oldimage' ) {
00239             foreach ( $fields as $id => &$field ) {
00240                 if ( substr( $field, 0, 4 ) !== 'img_' ) {
00241                     continue;
00242                 }
00243                 $field = $prefix . substr( $field, 3 ) . ' AS ' . $field;
00244             }
00245             $fields[array_search( 'top', $fields )] = "'no' AS top";
00246         } else {
00247             if ( $this->mShowAll ) {
00248                 $fields[array_search( 'top', $fields )] = "'yes' AS top";
00249             }
00250         }
00251         $fields[] = $prefix . '_user AS img_user';
00252         $fields[array_search( 'thumb', $fields )] = $prefix . '_name AS thumb';
00253 
00254         $options = $join_conds = array();
00255 
00256         # Depends on $wgMiserMode
00257         # Will also not happen if mShowAll is true.
00258         if ( isset( $this->mFieldNames['count'] ) ) {
00259             $tables[] = 'oldimage';
00260 
00261             # Need to rewrite this one
00262             foreach ( $fields as &$field ) {
00263                 if ( $field == 'count' ) {
00264                     $field = 'COUNT(oi_archive_name) AS count';
00265                 }
00266             }
00267             unset( $field );
00268 
00269             $dbr = wfGetDB( DB_SLAVE );
00270             if ( $dbr->implicitGroupby() ) {
00271                 $options = array( 'GROUP BY' => 'img_name' );
00272             } else {
00273                 $columnlist = preg_grep( '/^img/', array_keys( $this->getFieldNames() ) );
00274                 $options = array( 'GROUP BY' => array_merge( array( 'img_user' ), $columnlist ) );
00275             }
00276             $join_conds = array( 'oldimage' => array( 'LEFT JOIN', 'oi_name = img_name' ) );
00277         }
00278 
00279         return array(
00280             'tables' => $tables,
00281             'fields' => $fields,
00282             'conds' => $this->buildQueryConds( $table ),
00283             'options' => $options,
00284             'join_conds' => $join_conds
00285         );
00286     }
00287 
00295     function reallyDoQuery( $offset, $limit, $asc ) {
00296         $prevTableName = $this->mTableName;
00297         $this->mTableName = 'image';
00298         list( $tables, $fields, $conds, $fname, $options, $join_conds ) = $this->buildQueryInfo( $offset, $limit, $asc );
00299         $imageRes = $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds );
00300         $this->mTableName = $prevTableName;
00301 
00302         if ( !$this->mShowAll ) {
00303             return $imageRes;
00304         }
00305 
00306         $this->mTableName = 'oldimage';
00307 
00308         # Hacky...
00309         $oldIndex = $this->mIndexField;
00310         if ( substr( $this->mIndexField, 0, 4 ) !== 'img_' ) {
00311             throw new MWException( "Expected to be sorting on an image table field" );
00312         }
00313         $this->mIndexField = 'oi_' . substr( $this->mIndexField, 4 );
00314 
00315         list( $tables, $fields, $conds, $fname, $options, $join_conds ) = $this->buildQueryInfo( $offset, $limit, $asc );
00316         $oldimageRes = $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds );
00317 
00318         $this->mTableName = $prevTableName;
00319         $this->mIndexField = $oldIndex;
00320 
00321         return $this->combineResult( $imageRes, $oldimageRes, $limit, $asc );
00322     }
00323 
00335     protected function combineResult( $res1, $res2, $limit, $ascending ) {
00336         $res1->rewind();
00337         $res2->rewind();
00338         $topRes1 = $res1->next();
00339         $topRes2 = $res2->next();
00340         $resultArray = array();
00341         for ( $i = 0; $i < $limit && $topRes1 && $topRes2; $i++ ) {
00342             if ( strcmp( $topRes1->{$this->mIndexField}, $topRes2->{$this->mIndexField} ) > 0 ) {
00343                 if ( !$ascending ) {
00344                     $resultArray[] = $topRes1;
00345                     $topRes1 = $res1->next();
00346                 } else {
00347                     $resultArray[] = $topRes2;
00348                     $topRes2 = $res2->next();
00349                 }
00350             } else {
00351                 if ( !$ascending ) {
00352                     $resultArray[] = $topRes2;
00353                     $topRes2 = $res2->next();
00354                 } else {
00355                     $resultArray[] = $topRes1;
00356                     $topRes1 = $res1->next();
00357                 }
00358             }
00359         }
00360         for ( ; $i < $limit && $topRes1; $i++ ) {
00361             $resultArray[] = $topRes1;
00362             $topRes1 = $res1->next();
00363         }
00364         for ( ; $i < $limit && $topRes2; $i++ ) {
00365             $resultArray[] = $topRes2;
00366             $topRes2 = $res2->next();
00367         }
00368 
00369         return new FakeResultWrapper( $resultArray );
00370     }
00371 
00372     function getDefaultSort() {
00373         global $wgMiserMode;
00374         if ( $this->mShowAll && $wgMiserMode && is_null( $this->mUserName ) ) {
00375             // Unfortunately no index on oi_timestamp.
00376             return 'img_name';
00377         } else {
00378             return 'img_timestamp';
00379         }
00380     }
00381 
00382     function doBatchLookups() {
00383         $userIds = array();
00384         $this->mResult->seek( 0 );
00385         foreach ( $this->mResult as $row ) {
00386             $userIds[] = $row->img_user;
00387         }
00388         # Do a link batch query for names and userpages
00389         UserCache::singleton()->doQuery( $userIds, array( 'userpage' ), __METHOD__ );
00390     }
00391 
00406     function formatValue( $field, $value ) {
00407         switch ( $field ) {
00408             case 'thumb':
00409                 $opt = array( 'time' => $this->mCurrentRow->img_timestamp );
00410                 $file = RepoGroup::singleton()->getLocalRepo()->findFile( $value, $opt );
00411                 // If statement for paranoia
00412                 if ( $file ) {
00413                     $thumb = $file->transform( array( 'width' => 180, 'height' => 360 ) );
00414 
00415                     return $thumb->toHtml( array( 'desc-link' => true ) );
00416                 } else {
00417                     return htmlspecialchars( $value );
00418                 }
00419             case 'img_timestamp':
00420                 // We may want to make this a link to the "old" version when displaying old files
00421                 return htmlspecialchars( $this->getLanguage()->userTimeAndDate( $value, $this->getUser() ) );
00422             case 'img_name':
00423                 static $imgfile = null;
00424                 if ( $imgfile === null ) {
00425                     $imgfile = $this->msg( 'imgfile' )->text();
00426                 }
00427 
00428                 // Weird files can maybe exist? Bug 22227
00429                 $filePage = Title::makeTitleSafe( NS_FILE, $value );
00430                 if ( $filePage ) {
00431                     $link = Linker::linkKnown(
00432                         $filePage,
00433                         htmlspecialchars( $filePage->getText() )
00434                     );
00435                     $download = Xml::element( 'a',
00436                         array( 'href' => wfLocalFile( $filePage )->getURL() ),
00437                         $imgfile
00438                     );
00439                     $download = $this->msg( 'parentheses' )->rawParams( $download )->escaped();
00440 
00441                     return "$link $download";
00442                 } else {
00443                     return htmlspecialchars( $value );
00444                 }
00445             case 'img_user_text':
00446                 if ( $this->mCurrentRow->img_user ) {
00447                     $name = User::whoIs( $this->mCurrentRow->img_user );
00448                     $link = Linker::link(
00449                         Title::makeTitle( NS_USER, $name ),
00450                         htmlspecialchars( $name )
00451                     );
00452                 } else {
00453                     $link = htmlspecialchars( $value );
00454                 }
00455 
00456                 return $link;
00457             case 'img_size':
00458                 return htmlspecialchars( $this->getLanguage()->formatSize( $value ) );
00459             case 'img_description':
00460                 return Linker::formatComment( $value );
00461             case 'count':
00462                 return intval( $value ) + 1;
00463             case 'top':
00464                 // Messages: listfiles-latestversion-yes, listfiles-latestversion-no
00465                 return $this->msg( 'listfiles-latestversion-' . $value );
00466             default:
00467                 throw new MWException( "Unknown field '$field'" );
00468         }
00469     }
00470 
00471     function getForm() {
00472         global $wgScript, $wgMiserMode;
00473         $inputForm = array();
00474         $inputForm['table_pager_limit_label'] = $this->getLimitSelect( array( 'tabindex' => 1 ) );
00475         if ( !$wgMiserMode ) {
00476             $inputForm['listfiles_search_for'] = Html::input(
00477                 'ilsearch',
00478                 $this->mSearch,
00479                 'text',
00480                 array(
00481                     'size' => '40',
00482                     'maxlength' => '255',
00483                     'id' => 'mw-ilsearch',
00484                     'tabindex' => 2,
00485                 )
00486             );
00487         }
00488         $inputForm['username'] = Html::input( 'user', $this->mUserName, 'text', array(
00489             'size' => '40',
00490             'maxlength' => '255',
00491             'id' => 'mw-listfiles-user',
00492             'tabindex' => 3,
00493         ) );
00494 
00495         $inputForm['listfiles-show-all'] = Html::input( 'ilshowall', 1, 'checkbox', array(
00496             'checked' => $this->mShowAll,
00497             'tabindex' => 4,
00498         ) );
00499 
00500         return Html::openElement( 'form',
00501             array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-listfiles-form' )
00502         ) .
00503             Xml::fieldset( $this->msg( 'listfiles' )->text() ) .
00504             Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) .
00505             Xml::buildForm( $inputForm, 'table_pager_limit_submit', array( 'tabindex' => 5 ) ) .
00506             $this->getHiddenFields( array( 'limit', 'ilsearch', 'user', 'title', 'ilshowall' ) ) .
00507             Html::closeElement( 'fieldset' ) .
00508             Html::closeElement( 'form' ) . "\n";
00509     }
00510 
00511     function getTableClass() {
00512         return 'listfiles ' . parent::getTableClass();
00513     }
00514 
00515     function getNavClass() {
00516         return 'listfiles_nav ' . parent::getNavClass();
00517     }
00518 
00519     function getSortHeaderClass() {
00520         return 'listfiles_sort ' . parent::getSortHeaderClass();
00521     }
00522 
00523     function getPagingQueries() {
00524         $queries = parent::getPagingQueries();
00525         if ( !is_null( $this->mUserName ) ) {
00526             # Append the username to the query string
00527             foreach ( $queries as &$query ) {
00528                 $query['user'] = $this->mUserName;
00529             }
00530         }
00531 
00532         return $queries;
00533     }
00534 
00535     function getDefaultQuery() {
00536         $queries = parent::getDefaultQuery();
00537         if ( !isset( $queries['user'] ) && !is_null( $this->mUserName ) ) {
00538             $queries['user'] = $this->mUserName;
00539         }
00540 
00541         return $queries;
00542     }
00543 
00544     function getTitle() {
00545         return SpecialPage::getTitleFor( 'Listfiles' );
00546     }
00547 }