X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fspecialpage%2FChangesListSpecialPage.php;h=9074d3014513b233fbc4730b5700bfb6a477c0e2;hb=393798a0b3e16181e4df0789c0bba0232b838bc1;hp=8e9629dca4ecb16d42d0d541cd97f42b5fbdc01b;hpb=97fb2ab408f4bba361b5cc87bb9a42bdcb9a370e;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/specialpage/ChangesListSpecialPage.php b/includes/specialpage/ChangesListSpecialPage.php index 8e9629dca4..9074d30145 100644 --- a/includes/specialpage/ChangesListSpecialPage.php +++ b/includes/specialpage/ChangesListSpecialPage.php @@ -22,6 +22,7 @@ */ use MediaWiki\Logger\LoggerFactory; use Wikimedia\Rdbms\ResultWrapper; +use Wikimedia\Rdbms\IDatabase; /** * Special page which uses a ChangesList to show query results. @@ -58,6 +59,13 @@ abstract class ChangesListSpecialPage extends SpecialPage { */ private $filterGroupDefinitions; + // Same format as filterGroupDefinitions, but for a single group (reviewStatus) + // that is registered conditionally. + private $reviewStatusFilterGroupDefinition; + + // Single filter registered conditionally + private $hideCategorizationFilterDefinition; + /** * Filter groups, and their contained filters * This is an associative array (with group name as key) of ChangesListFilterGroup objects. @@ -177,11 +185,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { &$query_options, &$join_conds ) { $user = $ctx->getUser(); - if ( $user->getId() ) { - $conds[] = 'rc_user != ' . $dbr->addQuotes( $user->getId() ); - } else { - $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() ); - } + $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() ); }, 'cssClassSuffix' => 'self', 'isRowApplicableCallable' => function ( $ctx, $rc ) { @@ -197,11 +201,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { &$query_options, &$join_conds ) { $user = $ctx->getUser(); - if ( $user->getId() ) { - $conds[] = 'rc_user = ' . $dbr->addQuotes( $user->getId() ); - } else { - $conds[] = 'rc_user_text = ' . $dbr->addQuotes( $user->getName() ); - } + $conds[] = 'rc_user_text = ' . $dbr->addQuotes( $user->getName() ); }, 'cssClassSuffix' => 'others', 'isRowApplicableCallable' => function ( $ctx, $rc ) { @@ -252,57 +252,50 @@ abstract class ChangesListSpecialPage extends SpecialPage { ] ], + // reviewStatus (conditional) + [ - 'name' => 'reviewStatus', - 'title' => 'rcfilters-filtergroup-reviewstatus', + 'name' => 'lastRevision', + 'title' => 'rcfilters-filtergroup-lastRevision', 'class' => ChangesListBooleanFilterGroup::class, + 'priority' => -7, 'filters' => [ [ - 'name' => 'hidepatrolled', - 'label' => 'rcfilters-filter-patrolled-label', - 'description' => 'rcfilters-filter-patrolled-description', - // rcshowhidepatr-show, rcshowhidepatr-hide - // wlshowhidepatr - 'showHideSuffix' => 'showhidepatr', + 'name' => 'hidelastrevision', + 'label' => 'rcfilters-filter-lastrevision-label', + 'description' => 'rcfilters-filter-lastrevision-description', 'default' => false, - 'isAllowedCallable' => function ( $pageClassName, $context ) { - return $context->getUser()->useRCPatrol(); - }, 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds ) { - - $conds[] = 'rc_patrolled = 0'; + $conds[] = 'rc_this_oldid <> page_latest'; }, - 'cssClassSuffix' => 'patrolled', + 'cssClassSuffix' => 'last', 'isRowApplicableCallable' => function ( $ctx, $rc ) { - return $rc->getAttribute( 'rc_patrolled' ); - }, + return $rc->getAttribute( 'rc_this_oldid' ) === $rc->getAttribute( 'page_latest' ); + } ], [ - 'name' => 'hideunpatrolled', - 'label' => 'rcfilters-filter-unpatrolled-label', - 'description' => 'rcfilters-filter-unpatrolled-description', + 'name' => 'hidepreviousrevisions', + 'label' => 'rcfilters-filter-previousrevision-label', + 'description' => 'rcfilters-filter-previousrevision-description', 'default' => false, - 'isAllowedCallable' => function ( $pageClassName, $context ) { - return $context->getUser()->useRCPatrol(); - }, 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds ) { - - $conds[] = 'rc_patrolled = 1'; + $conds[] = 'rc_this_oldid = page_latest'; }, - 'cssClassSuffix' => 'unpatrolled', + 'cssClassSuffix' => 'previous', 'isRowApplicableCallable' => function ( $ctx, $rc ) { - return !$rc->getAttribute( 'rc_patrolled' ); - }, - ], - ], + return $rc->getAttribute( 'rc_this_oldid' ) !== $rc->getAttribute( 'page_latest' ); + } + ] + ] ], [ 'name' => 'significance', 'title' => 'rcfilters-filtergroup-significance', 'class' => ChangesListBooleanFilterGroup::class, + 'priority' => -6, 'filters' => [ [ 'name' => 'hideminor', @@ -351,6 +344,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { 'label' => 'rcfilters-filter-pageedits-label', 'description' => 'rcfilters-filter-pageedits-description', 'default' => false, + 'priority' => -2, 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds ) { @@ -366,6 +360,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { 'label' => 'rcfilters-filter-newpages-label', 'description' => 'rcfilters-filter-newpages-description', 'default' => false, + 'priority' => -3, 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds ) { @@ -376,47 +371,227 @@ abstract class ChangesListSpecialPage extends SpecialPage { return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_NEW; }, ], + + // hidecategorization + + [ + 'name' => 'hidelog', + 'label' => 'rcfilters-filter-logactions-label', + 'description' => 'rcfilters-filter-logactions-description', + 'default' => false, + 'priority' => -5, + 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, + &$query_options, &$join_conds ) { + + $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_LOG ); + }, + 'cssClassSuffix' => 'src-mw-log', + 'isRowApplicableCallable' => function ( $ctx, $rc ) { + return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_LOG; + } + ], + ], + ], + + [ + 'name' => 'watchlist', + 'title' => 'rcfilters-filtergroup-watchlist', + 'class' => ChangesListStringOptionsFilterGroup::class, + 'isFullCoverage' => true, + 'filters' => [ [ - 'name' => 'hidecategorization', - 'label' => 'rcfilters-filter-categorization-label', - 'description' => 'rcfilters-filter-categorization-description', - // rcshowhidecategorization-show, rcshowhidecategorization-hide. - // wlshowhidecategorization - 'showHideSuffix' => 'showhidecategorization', - 'isAllowedCallable' => function ( $pageClassName, $context ) { - return $context->getConfig()->get( 'RCWatchCategoryMembership' ); + 'name' => 'watched', + 'label' => 'rcfilters-filter-watchlist-watched-label', + 'description' => 'rcfilters-filter-watchlist-watched-description', + 'cssClassSuffix' => 'watched', + 'isRowApplicableCallable' => function ( $ctx, $rc ) { + return $rc->getAttribute( 'wl_user' ); + } + ], + [ + 'name' => 'watchednew', + 'label' => 'rcfilters-filter-watchlist-watchednew-label', + 'description' => 'rcfilters-filter-watchlist-watchednew-description', + 'cssClassSuffix' => 'watchednew', + 'isRowApplicableCallable' => function ( $ctx, $rc ) { + return $rc->getAttribute( 'wl_user' ) && + $rc->getAttribute( 'rc_timestamp' ) > $rc->getAttribute( 'wl_notificationtimestamp' ); }, + ], + [ + 'name' => 'notwatched', + 'label' => 'rcfilters-filter-watchlist-notwatched-label', + 'description' => 'rcfilters-filter-watchlist-notwatched-description', + 'cssClassSuffix' => 'notwatched', + 'isRowApplicableCallable' => function ( $ctx, $rc ) { + return $rc->getAttribute( 'wl_user' ) === null; + }, + ] + ], + 'default' => ChangesListStringOptionsFilterGroup::NONE, + 'queryCallable' => function ( $specialPageClassName, $context, $dbr, + &$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedValues ) { + sort( $selectedValues ); + $notwatchedCond = 'wl_user IS NULL'; + $watchedCond = 'wl_user IS NOT NULL'; + $newCond = 'rc_timestamp >= wl_notificationtimestamp'; + + if ( $selectedValues === [ 'notwatched' ] ) { + $conds[] = $notwatchedCond; + return; + } + + if ( $selectedValues === [ 'watched' ] ) { + $conds[] = $watchedCond; + return; + } + + if ( $selectedValues === [ 'watchednew' ] ) { + $conds[] = $dbr->makeList( [ + $watchedCond, + $newCond + ], LIST_AND ); + return; + } + + if ( $selectedValues === [ 'notwatched', 'watched' ] ) { + // no filters + return; + } + + if ( $selectedValues === [ 'notwatched', 'watchednew' ] ) { + $conds[] = $dbr->makeList( [ + $notwatchedCond, + $dbr->makeList( [ + $watchedCond, + $newCond + ], LIST_AND ) + ], LIST_OR ); + return; + } + + if ( $selectedValues === [ 'watched', 'watchednew' ] ) { + $conds[] = $watchedCond; + return; + } + + if ( $selectedValues === [ 'notwatched', 'watched', 'watchednew' ] ) { + // no filters + return; + } + }, + ], + ]; + + $this->reviewStatusFilterGroupDefinition = [ + [ + 'name' => 'reviewStatus', + 'title' => 'rcfilters-filtergroup-reviewstatus', + 'class' => ChangesListBooleanFilterGroup::class, + 'priority' => -5, + 'filters' => [ + [ + 'name' => 'hidepatrolled', + 'label' => 'rcfilters-filter-patrolled-label', + 'description' => 'rcfilters-filter-patrolled-description', + // rcshowhidepatr-show, rcshowhidepatr-hide + // wlshowhidepatr + 'showHideSuffix' => 'showhidepatr', 'default' => false, 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds ) { - $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE ); + $conds[] = 'rc_patrolled = 0'; }, - 'cssClassSuffix' => 'src-mw-categorize', + 'cssClassSuffix' => 'patrolled', 'isRowApplicableCallable' => function ( $ctx, $rc ) { - return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_CATEGORIZE; + return $rc->getAttribute( 'rc_patrolled' ); }, ], [ - 'name' => 'hidelog', - 'label' => 'rcfilters-filter-logactions-label', - 'description' => 'rcfilters-filter-logactions-description', + 'name' => 'hideunpatrolled', + 'label' => 'rcfilters-filter-unpatrolled-label', + 'description' => 'rcfilters-filter-unpatrolled-description', 'default' => false, 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds ) { - $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_LOG ); + $conds[] = 'rc_patrolled = 1'; }, - 'cssClassSuffix' => 'src-mw-log', + 'cssClassSuffix' => 'unpatrolled', 'isRowApplicableCallable' => function ( $ctx, $rc ) { - return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_LOG; - } + return !$rc->getAttribute( 'rc_patrolled' ); + }, ], ], - ], + ] + ]; + + $this->hideCategorizationFilterDefinition = [ + 'name' => 'hidecategorization', + 'label' => 'rcfilters-filter-categorization-label', + 'description' => 'rcfilters-filter-categorization-description', + // rcshowhidecategorization-show, rcshowhidecategorization-hide. + // wlshowhidecategorization + 'showHideSuffix' => 'showhidecategorization', + 'default' => false, + 'priority' => -4, + 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, + &$query_options, &$join_conds ) { + + $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE ); + }, + 'cssClassSuffix' => 'src-mw-categorize', + 'isRowApplicableCallable' => function ( $ctx, $rc ) { + return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_CATEGORIZE; + }, ]; } + /** + * Check if filters are in conflict and guaranteed to return no results. + * + * @return bool + */ + protected function areFiltersInConflict() { + $opts = $this->getOptions(); + /** @var ChangesListFilterGroup $group */ + foreach ( $this->getFilterGroups() as $group ) { + + if ( $group->getConflictingGroups() ) { + wfLogWarning( + $group->getName() . + " specifies conflicts with other groups but these are not supported yet." + ); + } + + /** @var ChangesListFilter $conflictingFilter */ + foreach ( $group->getConflictingFilters() as $conflictingFilter ) { + if ( $conflictingFilter->activelyInConflictWithGroup( $group, $opts ) ) { + return true; + } + } + + /** @var ChangesListFilter $filter */ + foreach ( $group->getFilters() as $filter ) { + + /** @var ChangesListFilter $conflictingFilter */ + foreach ( $filter->getConflictingFilters() as $conflictingFilter ) { + if ( + $conflictingFilter->activelyInConflictWithFilter( $filter, $opts ) && + $filter->activelyInConflictWithFilter( $conflictingFilter, $opts ) + ) { + return true; + } + } + + } + + } + + return false; + } + /** * Main execution point * @@ -434,6 +609,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { if ( $rows === false ) { if ( !$this->including() ) { $this->doHeader( $opts, 0 ); + $this->outputNoResults(); $this->getOutput()->setStatusCode( 404 ); } @@ -453,7 +629,6 @@ abstract class ChangesListSpecialPage extends SpecialPage { } } $batch->execute(); - $this->webOutput( $rows, $opts ); $rows->free(); @@ -467,6 +642,17 @@ abstract class ChangesListSpecialPage extends SpecialPage { } } + /** + * Add the "no results" message to the output + */ + protected function outputNoResults() { + $this->getOutput()->addHTML( + '