MediaWiki
REL1_22
|
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 }