X-Git-Url: https://git.heureux-cyclage.org/?p=lhc%2Fweb%2Fwiklou.git;a=blobdiff_plain;f=includes%2Fspecialpage%2FChangesListSpecialPage.php;h=3f452505086939ca1cb1a759723e467ca4bcbe61;hp=dd0dd92a986628177d294f38b4b2936cbc966935;hb=cb615a80599a409976518e7564cc6d993b772714;hpb=8a463e8ea5c73b7ac8ee70e6ca07ba89cce2c0a9 diff --git a/includes/specialpage/ChangesListSpecialPage.php b/includes/specialpage/ChangesListSpecialPage.php index dd0dd92a98..3f45250508 100644 --- a/includes/specialpage/ChangesListSpecialPage.php +++ b/includes/specialpage/ChangesListSpecialPage.php @@ -84,6 +84,9 @@ abstract class ChangesListSpecialPage extends SpecialPage { public function __construct( $name, $restriction ) { parent::__construct( $name, $restriction ); + $nonRevisionTypes = [ RC_LOG ]; + Hooks::run( 'SpecialWatchlistGetNonRevisionTypes', [ &$nonRevisionTypes ] ); + $this->filterGroupDefinitions = [ [ 'name' => 'registration', @@ -322,8 +325,14 @@ abstract class ChangesListSpecialPage extends SpecialPage { 'description' => 'rcfilters-filter-lastrevision-description', 'default' => false, 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, - &$query_options, &$join_conds ) { - $conds[] = 'rc_this_oldid <> page_latest'; + &$query_options, &$join_conds ) use ( $nonRevisionTypes ) { + $conds[] = $dbr->makeList( + [ + 'rc_this_oldid <> page_latest', + 'rc_type' => $nonRevisionTypes, + ], + LIST_OR + ); }, 'cssClassSuffix' => 'last', 'isRowApplicableCallable' => function ( $ctx, $rc ) { @@ -336,8 +345,14 @@ abstract class ChangesListSpecialPage extends SpecialPage { 'description' => 'rcfilters-filter-previousrevision-description', 'default' => false, 'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds, - &$query_options, &$join_conds ) { - $conds[] = 'rc_this_oldid = page_latest'; + &$query_options, &$join_conds ) use ( $nonRevisionTypes ) { + $conds[] = $dbr->makeList( + [ + 'rc_this_oldid = page_latest', + 'rc_type' => $nonRevisionTypes, + ], + LIST_OR + ); }, 'cssClassSuffix' => 'previous', 'isRowApplicableCallable' => function ( $ctx, $rc ) { @@ -525,6 +540,8 @@ abstract class ChangesListSpecialPage extends SpecialPage { public function execute( $subpage ) { $this->rcSubpage = $subpage; + $this->considerActionsForDefaultSavedQuery(); + $rows = $this->getRows(); $opts = $this->getOptions(); if ( $rows === false ) { @@ -576,6 +593,77 @@ abstract class ChangesListSpecialPage extends SpecialPage { $this->includeRcFiltersApp(); } + /** + * Check whether or not the page should load defaults, and if so, whether + * a default saved query is relevant to be redirected to. If it is relevant, + * redirect properly with all necessary query parameters. + */ + protected function considerActionsForDefaultSavedQuery() { + if ( !$this->isStructuredFilterUiEnabled() ) { + return; + } + + $knownParams = call_user_func_array( + [ $this->getRequest(), 'getValues' ], + array_keys( $this->getOptions()->getAllValues() ) + ); + + // HACK: Temporarily until we can properly define "sticky" filters and parameters, + // we need to exclude several parameters we know should not be counted towards preventing + // the loading of defaults. + $excludedParams = [ 'limit' => '', 'days' => '', 'enhanced' => '', 'from' => '' ]; + $knownParams = array_diff_key( $knownParams, $excludedParams ); + + if ( + // If there are NO known parameters in the URL request + // (that are not excluded) then we need to check into loading + // the default saved query + count( $knownParams ) === 0 + ) { + // Get the saved queries data and parse it + $savedQueries = FormatJson::decode( + $this->getUser()->getOption( static::$savedQueriesPreferenceName ), + true + ); + + if ( $savedQueries && isset( $savedQueries[ 'default' ] ) ) { + // Only load queries that are 'version' 2, since those + // have parameter representation + if ( isset( $savedQueries[ 'version' ] ) && $savedQueries[ 'version' ] === '2' ) { + $savedQueryDefaultID = $savedQueries[ 'default' ]; + $defaultQuery = $savedQueries[ 'queries' ][ $savedQueryDefaultID ][ 'data' ]; + + // Build the entire parameter list + $query = array_merge( + $defaultQuery[ 'params' ], + $defaultQuery[ 'highlights' ], + [ + 'urlversion' => '2', + ] + ); + // Add to the query any parameters that we may have ignored before + // but are still valid and requested in the URL + $query = array_merge( $this->getRequest()->getValues(), $query ); + unset( $query[ 'title' ] ); + $this->getOutput()->redirect( $this->getPageTitle()->getCanonicalURL( $query ) ); + } else { + // There's a default, but the version is not 2, and the server can't + // actually recognize the query itself. This happens if it is before + // the conversion, so we need to tell the UI to reload saved query as + // it does the conversion to version 2 + $this->getOutput()->addJsConfigVars( + 'wgStructuredChangeFiltersDefaultSavedQueryExists', + true + ); + + // Add the class that tells the frontend it is still loading + // another query + $this->getOutput()->addBodyClasses( 'mw-rcfilters-ui-loading' ); + } + } + } + } + /** * Include the modules and configuration for the RCFilters app. * Conditional on the user having the feature enabled. @@ -600,18 +688,11 @@ abstract class ChangesListSpecialPage extends SpecialPage { ) ); - $experimentalStructuredChangeFilters = - $this->getConfig()->get( 'StructuredChangeFiltersEnableExperimentalViews' ); - $out->addJsConfigVars( 'wgStructuredChangeFilters', $jsData['groups'] ); - $out->addJsConfigVars( - 'wgStructuredChangeFiltersEnableExperimentalViews', - $experimentalStructuredChangeFilters - ); $out->addJsConfigVars( 'wgRCFiltersChangeTags', - $this->buildChangeTagList() + $this->getChangeTagList() ); $out->addJsConfigVars( 'StructuredChangeFiltersDisplayConfig', @@ -624,20 +705,15 @@ abstract class ChangesListSpecialPage extends SpecialPage { ] ); - if ( static::$savedQueriesPreferenceName ) { - $savedQueries = FormatJson::decode( - $this->getUser()->getOption( static::$savedQueriesPreferenceName ) - ); - if ( $savedQueries && isset( $savedQueries->default ) ) { - // If there is a default saved query, show a loading spinner, - // since the frontend is going to reload the results - $out->addBodyClasses( 'mw-rcfilters-ui-loading' ); - } - $out->addJsConfigVars( - 'wgStructuredChangeFiltersSavedQueriesPreferenceName', - static::$savedQueriesPreferenceName - ); - } + $out->addJsConfigVars( + 'wgStructuredChangeFiltersSavedQueriesPreferenceName', + static::$savedQueriesPreferenceName + ); + + $out->addJsConfigVars( + 'StructuredChangeFiltersLiveUpdatePollingRate', + $this->getConfig()->get( 'StructuredChangeFiltersLiveUpdatePollingRate' ) + ); } else { $out->addBodyClasses( 'mw-rcfilters-disabled' ); } @@ -648,49 +724,60 @@ abstract class ChangesListSpecialPage extends SpecialPage { * * @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, - ]; - } - } + protected function getChangeTagList() { + $cache = ObjectCache::getMainWANInstance(); + $context = $this->getContext(); + return $cache->getWithSetCallback( + $cache->makeKey( 'changeslistspecialpage-changetags', $context->getLanguage()->getCode() ), + $cache::TTL_MINUTE * 10, + function () use ( $context ) { + $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, $context ); + + $result[] = [ + 'name' => $tagName, + 'label' => Sanitizer::stripAllTags( + ChangeTags::tagDescription( $tagName, $context ) + ), + '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'] ); - } ); + // 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; + return $result; + }, + [ + 'lockTSE' => 30 + ] + ); } /** @@ -944,6 +1031,11 @@ abstract class ChangesListSpecialPage extends SpecialPage { $opts->add( 'urlversion', 1 ); $opts->add( 'tagfilter', '' ); + $opts->add( 'days', $this->getDefaultDays(), FormOptions::FLOAT ); + $opts->add( 'limit', $this->getDefaultLimit(), FormOptions::INT ); + + $opts->add( 'from', '' ); + return $opts; } @@ -1097,6 +1189,9 @@ abstract class ChangesListSpecialPage extends SpecialPage { $query = wfArrayToCgi( $this->convertParamsForLink( $opts->getChangedValues() ) ); $this->getOutput()->redirect( $this->getPageTitle()->getCanonicalURL( $query ) ); } + + $opts->validateIntBounds( 'limit', 0, 5000 ); + $opts->validateBounds( 'days', 0, $this->getConfig()->get( 'RCMaxAge' ) / ( 3600 * 24 ) ); } /** @@ -1200,8 +1295,10 @@ abstract class ChangesListSpecialPage extends SpecialPage { // URL parameters can be per-group, like 'userExpLevel', // or per-filter, like 'hideminor'. if ( $filterGroup->isPerGroupRequestParameter() ) { - $filterGroup->modifyQuery( $dbr, $this, $tables, $fields, $conds, - $query_options, $join_conds, $opts[$filterGroup->getName()] ); + if ( $filterGroup->isActive( $isStructuredUI ) ) { + $filterGroup->modifyQuery( $dbr, $this, $tables, $fields, $conds, + $query_options, $join_conds, $opts[$filterGroup->getName()] ); + } } else { foreach ( $filterGroup->getFilters() as $filter ) { if ( $filter->isActive( $opts, $isStructuredUI ) ) { @@ -1236,6 +1333,19 @@ abstract class ChangesListSpecialPage extends SpecialPage { } $conds[] = "rc_namespace $operator $value"; } + + // Calculate cutoff + $cutoff_unixtime = time() - $opts['days'] * 3600 * 24; + $cutoff = $dbr->timestamp( $cutoff_unixtime ); + + $fromValid = preg_match( '/^[0-9]{14}$/', $opts['from'] ); + if ( $fromValid && $opts['from'] > wfTimestamp( TS_MW, $cutoff ) ) { + $cutoff = $dbr->timestamp( $opts['from'] ); + } else { + $opts->reset( 'from' ); + } + + $conds[] = 'rc_timestamp >= ' . $dbr->addQuotes( $cutoff ); } /** @@ -1422,8 +1532,10 @@ abstract class ChangesListSpecialPage extends SpecialPage { $context->msg( 'recentchanges-legend-heading' )->parse(); # Collapsible + $collapsedState = $this->getRequest()->getCookie( 'changeslist-state' ); + $collapsedClass = $collapsedState === 'collapsed' ? ' mw-collapsed' : ''; $legend = - '
' . + '
' . $legendHeading . '
' . $legend . '
' . '
';