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