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