From 4ac9fa081a7c045f6a9f1cfc529d82423f485b2e Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sun, 8 Dec 2013 09:55:49 +0100 Subject: Update to MediaWiki 1.22.0 --- includes/specials/SpecialListfiles.php | 301 +++++++++++++++++++++++++++++---- 1 file changed, 265 insertions(+), 36 deletions(-) (limited to 'includes/specials/SpecialListfiles.php') diff --git a/includes/specials/SpecialListfiles.php b/includes/specials/SpecialListfiles.php index c864ae2a..dff1cf70 100644 --- a/includes/specials/SpecialListfiles.php +++ b/includes/specials/SpecialListfiles.php @@ -22,7 +22,6 @@ */ class SpecialListFiles extends IncludableSpecialPage { - public function __construct() { parent::__construct( 'Listfiles' ); } @@ -37,9 +36,16 @@ class SpecialListFiles extends IncludableSpecialPage { } else { $userName = $this->getRequest()->getText( 'user', $par ); $search = $this->getRequest()->getText( 'ilsearch', '' ); + $showAll = $this->getRequest()->getBool( 'ilshowall', false ); } - $pager = new ImageListPager( $this->getContext(), $userName, $search, $this->including() ); + $pager = new ImageListPager( + $this->getContext(), + $userName, + $search, + $this->including(), + $showAll + ); if ( $this->including() ) { $html = $pager->getBody(); @@ -62,27 +68,33 @@ class SpecialListFiles extends IncludableSpecialPage { */ class ImageListPager extends TablePager { var $mFieldNames = null; + // Subclasses should override buildQueryConds instead of using $mQueryConds variable. var $mQueryConds = array(); var $mUserName = null; var $mSearch = ''; var $mIncluding = false; + var $mShowAll = false; + var $mTableName = 'image'; - function __construct( IContextSource $context, $userName = null, $search = '', $including = false ) { + function __construct( IContextSource $context, $userName = null, $search = '', + $including = false, $showAll = false + ) { global $wgMiserMode; $this->mIncluding = $including; + $this->mShowAll = $showAll; if ( $userName ) { $nt = Title::newFromText( $userName, NS_USER ); if ( !is_null( $nt ) ) { $this->mUserName = $nt->getText(); - $this->mQueryConds['img_user_text'] = $this->mUserName; } } - if ( $search != '' && !$wgMiserMode ) { + if ( $search !== '' && !$wgMiserMode ) { $this->mSearch = $search; $nt = Title::newFromURL( $this->mSearch ); + if ( $nt ) { $dbr = wfGetDB( DB_SLAVE ); $this->mQueryConds[] = 'LOWER(img_name)' . @@ -104,6 +116,42 @@ class ImageListPager extends TablePager { parent::__construct( $context ); } + /** + * Build the where clause of the query. + * + * Replaces the older mQueryConds member variable. + * @param $table String Either "image" or "oldimage" + * @return array The query conditions. + */ + protected function buildQueryConds( $table ) { + $prefix = $table === 'image' ? 'img' : 'oi'; + $conds = array(); + + if ( !is_null( $this->mUserName ) ) { + $conds[ $prefix . '_user_text' ] = $this->mUserName; + } + + if ( $this->mSearch !== '' ) { + $nt = Title::newFromURL( $this->mSearch ); + if ( $nt ) { + $dbr = wfGetDB( DB_SLAVE ); + $conds[] = 'LOWER(' . $prefix . '_name)' . + $dbr->buildLike( $dbr->anyString(), + strtolower( $nt->getDBkey() ), $dbr->anyString() ); + } + } + + if ( $table === 'oldimage' ) { + // Don't want to deal with revdel. + // Future fixme: Show partial information as appropriate. + // Would have to be careful about filtering by username when username is deleted. + $conds['oi_deleted'] = 0; + } + + // Add mQueryConds in case anyone was subclassing and using the old variable. + return $conds + $this->mQueryConds; + } + /** * @return Array */ @@ -118,34 +166,94 @@ class ImageListPager extends TablePager { 'img_user_text' => $this->msg( 'listfiles_user' )->text(), 'img_description' => $this->msg( 'listfiles_description' )->text(), ); - if( !$wgMiserMode ) { + if ( !$wgMiserMode && !$this->mShowAll ) { $this->mFieldNames['count'] = $this->msg( 'listfiles_count' )->text(); } + if ( $this->mShowAll ) { + $this->mFieldNames['top'] = $this->msg( 'listfiles-latestversion' )->text(); + } } + return $this->mFieldNames; } function isFieldSortable( $field ) { + global $wgMiserMode; if ( $this->mIncluding ) { return false; } - static $sortable = array( 'img_timestamp', 'img_name' ); - if ( $field == 'img_size' ) { - # No index for both img_size and img_user_text - return !isset( $this->mQueryConds['img_user_text'] ); + $sortable = array( 'img_timestamp', 'img_name', 'img_size' ); + /* For reference, the indicies we can use for sorting are: + * On the image table: img_usertext_timestamp, img_size, img_timestamp + * On oldimage: oi_usertext_timestamp, oi_name_timestamp + * + * In particular that means we cannot sort by timestamp when not filtering + * by user and including old images in the results. Which is sad. + */ + if ( $wgMiserMode && !is_null( $this->mUserName ) ) { + // If we're sorting by user, the index only supports sorting by time. + if ( $field === 'img_timestamp' ) { + return true; + } else { + return false; + } + } elseif ( $wgMiserMode && $this->mShowAll /* && mUserName === null */ ) { + // no oi_timestamp index, so only alphabetical sorting in this case. + if ( $field === 'img_name' ) { + return true; + } else { + return false; + } } + return in_array( $field, $sortable ); } function getQueryInfo() { - $tables = array( 'image' ); + // Hacky Hacky Hacky - I want to get query info + // for two different tables, without reimplementing + // the pager class. + $qi = $this->getQueryInfoReal( $this->mTableName ); + return $qi; + } + + /** + * Actually get the query info. + * + * This is to allow displaying both stuff from image and oldimage table. + * + * This is a bit hacky. + * + * @param $table String Either 'image' or 'oldimage' + * @return array Query info + */ + protected function getQueryInfoReal( $table ) { + $prefix = $table === 'oldimage' ? 'oi' : 'img'; + + $tables = array( $table ); $fields = array_keys( $this->getFieldNames() ); - $fields[] = 'img_user'; - $fields[array_search( 'thumb', $fields )] = 'img_name AS thumb'; + + if ( $table === 'oldimage' ) { + foreach ( $fields as $id => &$field ) { + if ( substr( $field, 0, 4 ) !== 'img_' ) { + continue; + } + $field = $prefix . substr( $field, 3 ) . ' AS ' . $field; + } + $fields[array_search('top', $fields)] = "'no' AS top"; + } else { + if ( $this->mShowAll ) { + $fields[array_search( 'top', $fields )] = "'yes' AS top"; + } + } + $fields[] = $prefix . '_user AS img_user'; + $fields[array_search( 'thumb', $fields )] = $prefix . '_name AS thumb'; + $options = $join_conds = array(); # Depends on $wgMiserMode - if( isset( $this->mFieldNames['count'] ) ) { + # Will also not happen if mShowAll is true. + if ( isset( $this->mFieldNames['count'] ) ) { $tables[] = 'oldimage'; # Need to rewrite this one @@ -157,7 +265,7 @@ class ImageListPager extends TablePager { unset( $field ); $dbr = wfGetDB( DB_SLAVE ); - if( $dbr->implicitGroupby() ) { + if ( $dbr->implicitGroupby() ) { $options = array( 'GROUP BY' => 'img_name' ); } else { $columnlist = preg_grep( '/^img/', array_keys( $this->getFieldNames() ) ); @@ -165,17 +273,107 @@ class ImageListPager extends TablePager { } $join_conds = array( 'oldimage' => array( 'LEFT JOIN', 'oi_name = img_name' ) ); } + return array( - 'tables' => $tables, - 'fields' => $fields, - 'conds' => $this->mQueryConds, - 'options' => $options, + 'tables' => $tables, + 'fields' => $fields, + 'conds' => $this->buildQueryConds( $table ), + 'options' => $options, 'join_conds' => $join_conds ); } + /** + * Override reallyDoQuery to mix together two queries. + * + * @note $asc is named $descending in IndexPager base class. However + * it is true when the order is ascending, and false when the order + * is descending, so I renamed it to $asc here. + */ + function reallyDoQuery( $offset, $limit, $asc ) { + $prevTableName = $this->mTableName; + $this->mTableName = 'image'; + list( $tables, $fields, $conds, $fname, $options, $join_conds ) = $this->buildQueryInfo( $offset, $limit, $asc ); + $imageRes = $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds ); + $this->mTableName = $prevTableName; + + if ( !$this->mShowAll ) { + return $imageRes; + } + + $this->mTableName = 'oldimage'; + + # Hacky... + $oldIndex = $this->mIndexField; + if ( substr( $this->mIndexField, 0, 4 ) !== 'img_' ) { + throw new MWException( "Expected to be sorting on an image table field" ); + } + $this->mIndexField = 'oi_' . substr( $this->mIndexField, 4 ); + + list( $tables, $fields, $conds, $fname, $options, $join_conds ) = $this->buildQueryInfo( $offset, $limit, $asc ); + $oldimageRes = $this->mDb->select( $tables, $fields, $conds, $fname, $options, $join_conds ); + + $this->mTableName = $prevTableName; + $this->mIndexField = $oldIndex; + + return $this->combineResult( $imageRes, $oldimageRes, $limit, $asc ); + } + + /** + * Combine results from 2 tables. + * + * Note: This will throw away some results + * + * @param $res1 ResultWrapper + * @param $res2 ResultWrapper + * @param $limit int + * @param $ascending boolean See note about $asc in $this->reallyDoQuery + * @return FakeResultWrapper $res1 and $res2 combined + */ + protected function combineResult( $res1, $res2, $limit, $ascending ) { + $res1->rewind(); + $res2->rewind(); + $topRes1 = $res1->next(); + $topRes2 = $res2->next(); + $resultArray = array(); + for ( $i = 0; $i < $limit && $topRes1 && $topRes2; $i++ ) { + if ( strcmp( $topRes1->{ $this->mIndexField }, $topRes2->{ $this->mIndexField } ) > 0 ) { + if ( !$ascending ) { + $resultArray[] = $topRes1; + $topRes1 = $res1->next(); + } else { + $resultArray[] = $topRes2; + $topRes2 = $res2->next(); + } + } else { + if ( !$ascending ) { + $resultArray[] = $topRes2; + $topRes2 = $res2->next(); + } else { + $resultArray[] = $topRes1; + $topRes1 = $res1->next(); + } + } + } + for ( ; $i < $limit && $topRes1; $i++ ) { + $resultArray[] = $topRes1; + $topRes1 = $res1->next(); + } + for ( ; $i < $limit && $topRes2; $i++ ) { + $resultArray[] = $topRes2; + $topRes2 = $res2->next(); + } + return new FakeResultWrapper( $resultArray ); + } + function getDefaultSort() { - return 'img_timestamp'; + global $wgMiserMode; + if ( $this->mShowAll && $wgMiserMode && is_null( $this->mUserName ) ) { + // Unfortunately no index on oi_timestamp. + return 'img_name'; + } else { + return 'img_timestamp'; + } } function doBatchLookups() { @@ -191,24 +389,37 @@ class ImageListPager extends TablePager { function formatValue( $field, $value ) { switch ( $field ) { case 'thumb': - $file = wfLocalFile( $value ); - $thumb = $file->transform( array( 'width' => 180, 'height' => 360 ) ); - return $thumb->toHtml( array( 'desc-link' => true ) ); + $opt = array( 'time' => $this->mCurrentRow->img_timestamp ); + $file = RepoGroup::singleton()->getLocalRepo()->findFile( $value, $opt ); + // If statement for paranoia + if ( $file ) { + $thumb = $file->transform( array( 'width' => 180, 'height' => 360 ) ); + return $thumb->toHtml( array( 'desc-link' => true ) ); + } else { + return htmlspecialchars( $value ); + } case 'img_timestamp': + // We may want to make this a link to the "old" version when displaying old files return htmlspecialchars( $this->getLanguage()->userTimeAndDate( $value, $this->getUser() ) ); case 'img_name': static $imgfile = null; - if ( $imgfile === null ) $imgfile = $this->msg( 'imgfile' )->text(); + if ( $imgfile === null ) { + $imgfile = $this->msg( 'imgfile' )->text(); + } // Weird files can maybe exist? Bug 22227 $filePage = Title::makeTitleSafe( NS_FILE, $value ); - if( $filePage ) { - $link = Linker::linkKnown( $filePage, htmlspecialchars( $filePage->getText() ) ); + if ( $filePage ) { + $link = Linker::linkKnown( + $filePage, + htmlspecialchars( $filePage->getText() ) + ); $download = Xml::element( 'a', array( 'href' => wfLocalFile( $filePage )->getURL() ), $imgfile ); $download = $this->msg( 'parentheses' )->rawParams( $download )->escaped(); + return "$link $download"; } else { return htmlspecialchars( $value ); @@ -223,6 +434,7 @@ class ImageListPager extends TablePager { } else { $link = htmlspecialchars( $value ); } + return $link; case 'img_size': return htmlspecialchars( $this->getLanguage()->formatSize( $value ) ); @@ -230,32 +442,47 @@ class ImageListPager extends TablePager { return Linker::formatComment( $value ); case 'count': return intval( $value ) + 1; + case 'top': + // Messages: listfiles-latestversion-yes, listfiles-latestversion-no + return $this->msg( 'listfiles-latestversion-' . $value ); } } function getForm() { global $wgScript, $wgMiserMode; $inputForm = array(); - $inputForm['table_pager_limit_label'] = $this->getLimitSelect(); + $inputForm['table_pager_limit_label'] = $this->getLimitSelect( array( 'tabindex' => 1 ) ); if ( !$wgMiserMode ) { - $inputForm['listfiles_search_for'] = Html::input( 'ilsearch', $this->mSearch, 'text', + $inputForm['listfiles_search_for'] = Html::input( + 'ilsearch', + $this->mSearch, + 'text', array( - 'size' => '40', + 'size' => '40', 'maxlength' => '255', - 'id' => 'mw-ilsearch', - ) ); + 'id' => 'mw-ilsearch', + 'tabindex' => 2, + ) + ); } $inputForm['username'] = Html::input( 'user', $this->mUserName, 'text', array( - 'size' => '40', + 'size' => '40', 'maxlength' => '255', - 'id' => 'mw-listfiles-user', + 'id' => 'mw-listfiles-user', + 'tabindex' => 3, + ) ); + + $inputForm['listfiles-show-all'] = Html::input( 'ilshowall', 1, 'checkbox', array( + 'checked' => $this->mShowAll, + 'tabindex' => 4, ) ); return Html::openElement( 'form', - array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-listfiles-form' ) ) . + array( 'method' => 'get', 'action' => $wgScript, 'id' => 'mw-listfiles-form' ) + ) . Xml::fieldset( $this->msg( 'listfiles' )->text() ) . Html::hidden( 'title', $this->getTitle()->getPrefixedText() ) . - Xml::buildForm( $inputForm, 'table_pager_limit_submit' ) . - $this->getHiddenFields( array( 'limit', 'ilsearch', 'user', 'title' ) ) . + Xml::buildForm( $inputForm, 'table_pager_limit_submit', array( 'tabindex' => 5 ) ) . + $this->getHiddenFields( array( 'limit', 'ilsearch', 'user', 'title', 'ilshowall' ) ) . Html::closeElement( 'fieldset' ) . Html::closeElement( 'form' ) . "\n"; } @@ -280,6 +507,7 @@ class ImageListPager extends TablePager { $query['user'] = $this->mUserName; } } + return $queries; } @@ -288,6 +516,7 @@ class ImageListPager extends TablePager { if ( !isset( $queries['user'] ) && !is_null( $this->mUserName ) ) { $queries['user'] = $this->mUserName; } + return $queries; } -- cgit v1.2.2