X-Git-Url: https://git.heureux-cyclage.org/?a=blobdiff_plain;f=includes%2Fspecialpage%2FChangesListSpecialPage.php;h=5f544042f9709a7592b73d3949e8bb4de652005f;hb=1d7a1bf8bddf0908e4f572c82268733f63126a13;hp=b85d272c445c02daf7870f887e8c3c74949603a5;hpb=10d1b7d12b5d097413cd507740c5c71781c2580b;p=lhc%2Fweb%2Fwiklou.git diff --git a/includes/specialpage/ChangesListSpecialPage.php b/includes/specialpage/ChangesListSpecialPage.php index b85d272c44..5f544042f9 100644 --- a/includes/specialpage/ChangesListSpecialPage.php +++ b/includes/specialpage/ChangesListSpecialPage.php @@ -86,8 +86,6 @@ abstract class ChangesListSpecialPage extends SpecialPage { 'filters' => [ [ 'name' => 'hideliu', - 'label' => 'rcfilters-filter-registered-label', - 'description' => 'rcfilters-filter-registered-description', // rcshowhideliu-show, rcshowhideliu-hide, // wlshowhideliu 'showHideSuffix' => 'showhideliu', @@ -97,16 +95,11 @@ abstract class ChangesListSpecialPage extends SpecialPage { ) { $conds[] = 'rc_user = 0'; }, - 'cssClassSuffix' => 'liu', - 'isRowApplicableCallable' => function ( $ctx, $rc ) { - return $rc->getAttribute( 'rc_user' ); - }, + 'isReplacedInStructuredUi' => true, ], [ 'name' => 'hideanons', - 'label' => 'rcfilters-filter-unregistered-label', - 'description' => 'rcfilters-filter-unregistered-description', // rcshowhideanons-show, rcshowhideanons-hide, // wlshowhideanons 'showHideSuffix' => 'showhideanons', @@ -116,10 +109,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { ) { $conds[] = 'rc_user != 0'; }, - 'cssClassSuffix' => 'anon', - 'isRowApplicableCallable' => function ( $ctx, $rc ) { - return !$rc->getAttribute( 'rc_user' ); - }, + 'isReplacedInStructuredUi' => true, ] ], ], @@ -128,9 +118,26 @@ abstract class ChangesListSpecialPage extends SpecialPage { 'name' => 'userExpLevel', 'title' => 'rcfilters-filtergroup-userExpLevel', 'class' => ChangesListStringOptionsFilterGroup::class, - // Excludes unregistered users - 'isFullCoverage' => false, + 'isFullCoverage' => true, 'filters' => [ + [ + 'name' => 'unregistered', + 'label' => 'rcfilters-filter-user-experience-level-unregistered-label', + 'description' => 'rcfilters-filter-user-experience-level-unregistered-description', + 'cssClassSuffix' => 'user-unregistered', + 'isRowApplicableCallable' => function ( $ctx, $rc ) { + return !$rc->getAttribute( 'rc_user' ); + } + ], + [ + 'name' => 'registered', + 'label' => 'rcfilters-filter-user-experience-level-registered-label', + 'description' => 'rcfilters-filter-user-experience-level-registered-description', + 'cssClassSuffix' => 'user-registered', + 'isRowApplicableCallable' => function ( $ctx, $rc ) { + return $rc->getAttribute( 'rc_user' ); + } + ], [ 'name' => 'newcomer', 'label' => 'rcfilters-filter-user-experience-level-newcomer-label', @@ -546,6 +553,107 @@ abstract class ChangesListSpecialPage extends SpecialPage { LoggerFactory::getInstance( 'objectcache' ) ) ); } + + $this->includeRcFiltersApp(); + } + + /** + * Include the modules and configuration for the RCFilters app. + * Conditional on the user having the feature enabled. + */ + protected function includeRcFiltersApp() { + if ( $this->isStructuredFilterUiEnabled() ) { + $out = $this->getOutput(); + $jsData = $this->getStructuredFilterJsData(); + + $messages = []; + foreach ( $jsData['messageKeys'] as $key ) { + $messages[$key] = $this->msg( $key )->plain(); + } + + $out->addHTML( + ResourceLoader::makeInlineScript( + ResourceLoader::makeMessageSetScript( $messages ) + ) + ); + + $experimentalStructuredChangeFilters = + $this->getConfig()->get( 'StructuredChangeFiltersEnableExperimentalViews' ); + + $out->addJsConfigVars( 'wgStructuredChangeFilters', $jsData['groups'] ); + $out->addJsConfigVars( + 'wgStructuredChangeFiltersEnableExperimentalViews', + $experimentalStructuredChangeFilters + ); + $out->addJsConfigVars( + 'wgStructuredChangeFiltersEnableLiveUpdate', + $this->getConfig()->get( 'StructuredChangeFiltersEnableLiveUpdate' ) + ); + $out->addJsConfigVars( + 'wgRCFiltersChangeTags', + $this->buildChangeTagList() + ); + $out->addJsConfigVars( + 'StructuredChangeFiltersDisplayConfig', + [ + 'maxDays' => (int)$this->getConfig()->get( 'RCMaxAge' ) / ( 24 * 3600 ), // Translate to days + 'limitArray' => $this->getConfig()->get( 'RCLinkLimits' ), + 'limitDefault' => $this->getDefaultLimit(), + 'daysArray' => $this->getConfig()->get( 'RCLinkDays' ), + 'daysDefault' => $this->getDefaultDays(), + ] + ); + } + } + + /** + * Fetch the change tags list for the front end + * + * @return Array Tag data + */ + protected function buildChangeTagList() { + $explicitlyDefinedTags = array_fill_keys( ChangeTags::listExplicitlyDefinedTags(), 0 ); + $softwareActivatedTags = array_fill_keys( ChangeTags::listSoftwareActivatedTags(), 0 ); + + // Hit counts disabled for perf reasons, see T169997 + /* + $tagStats = ChangeTags::tagUsageStatistics(); + $tagHitCounts = array_merge( $explicitlyDefinedTags, $softwareActivatedTags, $tagStats ); + + // Sort by hits + arsort( $tagHitCounts ); + */ + $tagHitCounts = array_merge( $explicitlyDefinedTags, $softwareActivatedTags ); + + // Build the list and data + $result = []; + foreach ( $tagHitCounts as $tagName => $hits ) { + if ( + // Only get active tags + isset( $explicitlyDefinedTags[ $tagName ] ) || + isset( $softwareActivatedTags[ $tagName ] ) + ) { + // Parse description + $desc = ChangeTags::tagLongDescriptionMessage( $tagName, $this->getContext() ); + + $result[] = [ + 'name' => $tagName, + 'label' => Sanitizer::stripAllTags( + ChangeTags::tagDescription( $tagName, $this->getContext() ) + ), + 'description' => $desc ? Sanitizer::stripAllTags( $desc->parse() ) : '', + 'cssClass' => Sanitizer::escapeClass( 'mw-tag-' . $tagName ), + 'hits' => $hits, + ]; + } + } + + // Instead of sorting by hit count (disabled, see above), sort by display name + usort( $result, function ( $a, $b ) { + return strcasecmp( $a['label'], $b['label'] ); + } ); + + return $result; } /** @@ -632,19 +740,10 @@ abstract class ChangesListSpecialPage extends SpecialPage { $this->registerFiltersFromDefinitions( [ $unstructuredGroupDefinition ] ); $userExperienceLevel = $this->getFilterGroup( 'userExpLevel' ); - - $registration = $this->getFilterGroup( 'registration' ); - $anons = $registration->getFilter( 'hideanons' ); - - // This means there is a conflict between any item in user experience level - // being checked and only anons being *shown* (hideliu=1&hideanons=0 in the - // URL, or equivalent). - $userExperienceLevel->conflictsWith( - $anons, - 'rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global', - 'rcfilters-filtergroup-user-experience-level-conflicts-unregistered', - 'rcfilters-filter-unregistered-conflicts-user-experience-level' - ); + $registered = $userExperienceLevel->getFilter( 'registered' ); + $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'newcomer' ) ); + $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'learner' ) ); + $registered->setAsSupersetOf( $userExperienceLevel->getFilter( 'experienced' ) ); $categoryFilter = $changeTypeGroup->getFilter( 'hidecategorization' ); $logactionsFilter = $changeTypeGroup->getFilter( 'hidelog' ); @@ -781,19 +880,20 @@ abstract class ChangesListSpecialPage extends SpecialPage { * @return FormOptions */ public function getDefaultOptions() { - $config = $this->getConfig(); $opts = new FormOptions(); - $structuredUI = $this->getUser()->getOption( 'rcenhancedfilters' ); + $structuredUI = $this->isStructuredFilterUiEnabled(); // If urlversion=2 is set, ignore the filter defaults and set them all to false/empty $useDefaults = $this->getRequest()->getInt( 'urlversion' ) !== 2; // Add all filters + /** @var ChangesListFilterGroup $filterGroup */ foreach ( $this->filterGroups as $filterGroup ) { // URL parameters can be per-group, like 'userExpLevel', // or per-filter, like 'hideminor'. if ( $filterGroup->isPerGroupRequestParameter() ) { $opts->add( $filterGroup->getName(), $useDefaults ? $filterGroup->getDefault() : '' ); } else { + /** @var ChangesListBooleanFilter $filter */ foreach ( $filterGroup->getFilters() as $filter ) { $opts->add( $filter->getName(), $useDefaults ? $filter->getDefault( $structuredUI ) : false ); } @@ -804,6 +904,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { $opts->add( 'invert', false ); $opts->add( 'associated', false ); $opts->add( 'urlversion', 1 ); + $opts->add( 'tagfilter', '' ); return $opts; } @@ -859,8 +960,6 @@ abstract class ChangesListSpecialPage extends SpecialPage { 'messageKeys' => [], ]; - $context = $this->getContext(); - usort( $this->filterGroups, function ( $a, $b ) { return $b->getPriority() - $a->getPriority(); } ); @@ -1057,9 +1156,8 @@ abstract class ChangesListSpecialPage extends SpecialPage { &$join_conds, FormOptions $opts ) { $dbr = $this->getDB(); - $user = $this->getUser(); + $isStructuredUI = $this->isStructuredFilterUiEnabled(); - $context = $this->getContext(); foreach ( $this->filterGroups as $filterGroup ) { // URL parameters can be per-group, like 'userExpLevel', // or per-filter, like 'hideminor'. @@ -1068,7 +1166,7 @@ abstract class ChangesListSpecialPage extends SpecialPage { $query_options, $join_conds, $opts[$filterGroup->getName()] ); } else { foreach ( $filterGroup->getFilters() as $filter ) { - if ( $opts[$filter->getName()] ) { + if ( $filter->isActive( $opts, $isStructuredUI ) ) { $filter->modifyQuery( $dbr, $this, $tables, $fields, $conds, $query_options, $join_conds ); } @@ -1281,12 +1379,11 @@ abstract class ChangesListSpecialPage extends SpecialPage { ) . "\n"; $legend .= Html::closeElement( 'dl' ) . "\n"; - # Collapsibility - $legendHeading = $this->getUser()->getOption( - 'rcenhancedfilters' - ) ? + $legendHeading = $this->isStructuredFilterUiEnabled() ? $context->msg( 'rcfilters-legend-heading' )->parse() : $context->msg( 'recentchanges-legend-heading' )->parse(); + + # Collapsible $legend = '
' . $legendHeading . @@ -1307,6 +1404,11 @@ abstract class ChangesListSpecialPage extends SpecialPage { 'mediawiki.special.changeslist', ] ); $out->addModules( 'mediawiki.special.changeslist.legend.js' ); + + if ( $this->isStructuredFilterUiEnabled() ) { + $out->addModules( 'mediawiki.rcfilters.filters.ui' ); + $out->addModuleStyles( 'mediawiki.rcfilters.filters.base.styles' ); + } } protected function getGroupName() { @@ -1337,15 +1439,35 @@ abstract class ChangesListSpecialPage extends SpecialPage { $wgLearnerMemberSince, $wgExperiencedUserMemberSince; - $LEVEL_COUNT = 3; + $LEVEL_COUNT = 5; - // If all levels are selected, all logged-in users are included (but no - // anons), so we can short-circuit. + // If all levels are selected, don't filter if ( count( $selectedExpLevels ) === $LEVEL_COUNT ) { + return; + } + + // both 'registered' and 'unregistered', experience levels, if any, are included in 'registered' + if ( + in_array( 'registered', $selectedExpLevels ) && + in_array( 'unregistered', $selectedExpLevels ) + ) { + return; + } + + // 'registered' but not 'unregistered', experience levels, if any, are included in 'registered' + if ( + in_array( 'registered', $selectedExpLevels ) && + !in_array( 'unregistered', $selectedExpLevels ) + ) { $conds[] = 'rc_user != 0'; return; } + if ( $selectedExpLevels === [ 'unregistered' ] ) { + $conds[] = 'rc_user = 0'; + return; + } + $tables[] = 'user'; $join_conds['user'] = [ 'LEFT JOIN', 'rc_user = user_id' ]; @@ -1373,24 +1495,52 @@ abstract class ChangesListSpecialPage extends SpecialPage { IDatabase::LIST_AND ); + $conditions = []; + + if ( in_array( 'unregistered', $selectedExpLevels ) ) { + $selectedExpLevels = array_diff( $selectedExpLevels, [ 'unregistered' ] ); + $conditions[] = 'rc_user = 0'; + } + if ( $selectedExpLevels === [ 'newcomer' ] ) { - $conds[] = "NOT ( $aboveNewcomer )"; + $conditions[] = "NOT ( $aboveNewcomer )"; } elseif ( $selectedExpLevels === [ 'learner' ] ) { - $conds[] = $dbr->makeList( + $conditions[] = $dbr->makeList( [ $aboveNewcomer, "NOT ( $aboveLearner )" ], IDatabase::LIST_AND ); } elseif ( $selectedExpLevels === [ 'experienced' ] ) { - $conds[] = $aboveLearner; + $conditions[] = $aboveLearner; } elseif ( $selectedExpLevels === [ 'learner', 'newcomer' ] ) { - $conds[] = "NOT ( $aboveLearner )"; + $conditions[] = "NOT ( $aboveLearner )"; } elseif ( $selectedExpLevels === [ 'experienced', 'newcomer' ] ) { - $conds[] = $dbr->makeList( + $conditions[] = $dbr->makeList( [ "NOT ( $aboveNewcomer )", $aboveLearner ], IDatabase::LIST_OR ); } elseif ( $selectedExpLevels === [ 'experienced', 'learner' ] ) { - $conds[] = $aboveNewcomer; + $conditions[] = $aboveNewcomer; + } elseif ( $selectedExpLevels === [ 'experienced', 'learner', 'newcomer' ] ) { + $conditions[] = 'rc_user != 0'; + } + + if ( count( $conditions ) > 1 ) { + $conds[] = $dbr->makeList( $conditions, IDatabase::LIST_OR ); + } elseif ( count( $conditions ) === 1 ) { + $conds[] = reset( $conditions ); } } + + /** + * Check whether the structured filter UI is enabled + * + * @return bool + */ + public function isStructuredFilterUiEnabled() { + return $this->getUser()->getOption( 'rcenhancedfilters' ); + } + + abstract function getDefaultLimit(); + + abstract function getDefaultDays(); }