Skip to content

Commit c06e525

Browse files
author
Stephane Bisson
committed
Enable RCFilters app on Watchlist
* Add classes prefixed with cl (for ChangesList) to both RC and WL so that the JS app can locate similar elements using the same selectors * Make saved queries preference name configurable so that RC and WL use different preferences * Move some code from SpecialRecentchanges.php to its base class so it's accessible to SpecialWatchlist.php To use the RCFilters app on WL, append ?rcfilters=1 to the URL Bug: T171132 Bug: T171218 Change-Id: If7c63284224df24fa02022b4ea74bef116f28ca6
1 parent a5d7d02 commit c06e525

12 files changed

+310
-183
lines changed

includes/DefaultSettings.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6846,6 +6846,11 @@
68466846
*/
68476847
$wgStructuredChangeFiltersEnableLiveUpdate = false;
68486848

6849+
/**
6850+
* Whether to enable RCFilters app on Special:Watchlist
6851+
*/
6852+
$wgStructuredChangeFiltersOnWatchlist = false;
6853+
68496854
/**
68506855
* Use new page patrolling to check new pages on Special:Newpages
68516856
*/

includes/Preferences.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,9 @@ static function rcPreferences( $user, IContextSource $context, &$defaultPreferen
920920
$defaultPreferences['rcfilters-saved-queries'] = [
921921
'type' => 'api',
922922
];
923+
$defaultPreferences['rcfilters-wl-saved-queries'] = [
924+
'type' => 'api',
925+
];
923926
$defaultPreferences['rcfilters-rclimit'] = [
924927
'type' => 'api',
925928
];

includes/specialpage/ChangesListSpecialPage.php

Lines changed: 119 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,105 @@ public function execute( $subpage ) {
553553
LoggerFactory::getInstance( 'objectcache' )
554554
) );
555555
}
556+
557+
$this->includeRcFiltersApp();
558+
}
559+
560+
/**
561+
* Include the modules and configuration for the RCFilters app.
562+
* Conditional on the user having the feature enabled.
563+
*/
564+
protected function includeRcFiltersApp() {
565+
if ( $this->isStructuredFilterUiEnabled() ) {
566+
$out = $this->getOutput();
567+
$jsData = $this->getStructuredFilterJsData();
568+
569+
$messages = [];
570+
foreach ( $jsData['messageKeys'] as $key ) {
571+
$messages[$key] = $this->msg( $key )->plain();
572+
}
573+
574+
$out->addHTML(
575+
ResourceLoader::makeInlineScript(
576+
ResourceLoader::makeMessageSetScript( $messages )
577+
)
578+
);
579+
580+
$experimentalStructuredChangeFilters =
581+
$this->getConfig()->get( 'StructuredChangeFiltersEnableExperimentalViews' );
582+
583+
$out->addJsConfigVars( 'wgStructuredChangeFilters', $jsData['groups'] );
584+
$out->addJsConfigVars(
585+
'wgStructuredChangeFiltersEnableExperimentalViews',
586+
$experimentalStructuredChangeFilters
587+
);
588+
$out->addJsConfigVars(
589+
'wgStructuredChangeFiltersEnableLiveUpdate',
590+
$this->getConfig()->get( 'StructuredChangeFiltersEnableLiveUpdate' )
591+
);
592+
$out->addJsConfigVars(
593+
'wgRCFiltersChangeTags',
594+
$this->buildChangeTagList()
595+
);
596+
$out->addJsConfigVars(
597+
'StructuredChangeFiltersDisplayConfig',
598+
[
599+
'maxDays' => (int)$this->getConfig()->get( 'RCMaxAge' ) / ( 24 * 3600 ), // Translate to days
600+
'limitArray' => $this->getConfig()->get( 'RCLinkLimits' ),
601+
'daysArray' => $this->getConfig()->get( 'RCLinkDays' ),
602+
]
603+
);
604+
}
605+
}
606+
607+
/**
608+
* Fetch the change tags list for the front end
609+
*
610+
* @return Array Tag data
611+
*/
612+
protected function buildChangeTagList() {
613+
$explicitlyDefinedTags = array_fill_keys( ChangeTags::listExplicitlyDefinedTags(), 0 );
614+
$softwareActivatedTags = array_fill_keys( ChangeTags::listSoftwareActivatedTags(), 0 );
615+
616+
// Hit counts disabled for perf reasons, see T169997
617+
/*
618+
$tagStats = ChangeTags::tagUsageStatistics();
619+
$tagHitCounts = array_merge( $explicitlyDefinedTags, $softwareActivatedTags, $tagStats );
620+
621+
// Sort by hits
622+
arsort( $tagHitCounts );
623+
*/
624+
$tagHitCounts = array_merge( $explicitlyDefinedTags, $softwareActivatedTags );
625+
626+
// Build the list and data
627+
$result = [];
628+
foreach ( $tagHitCounts as $tagName => $hits ) {
629+
if (
630+
// Only get active tags
631+
isset( $explicitlyDefinedTags[ $tagName ] ) ||
632+
isset( $softwareActivatedTags[ $tagName ] )
633+
) {
634+
// Parse description
635+
$desc = ChangeTags::tagLongDescriptionMessage( $tagName, $this->getContext() );
636+
637+
$result[] = [
638+
'name' => $tagName,
639+
'label' => Sanitizer::stripAllTags(
640+
ChangeTags::tagDescription( $tagName, $this->getContext() )
641+
),
642+
'description' => $desc ? Sanitizer::stripAllTags( $desc->parse() ) : '',
643+
'cssClass' => Sanitizer::escapeClass( 'mw-tag-' . $tagName ),
644+
'hits' => $hits,
645+
];
646+
}
647+
}
648+
649+
// Instead of sorting by hit count (disabled, see above), sort by display name
650+
usort( $result, function ( $a, $b ) {
651+
return strcasecmp( $a['label'], $b['label'] );
652+
} );
653+
654+
return $result;
556655
}
557656

558657
/**
@@ -779,19 +878,20 @@ public function setup( $parameters ) {
779878
* @return FormOptions
780879
*/
781880
public function getDefaultOptions() {
782-
$config = $this->getConfig();
783881
$opts = new FormOptions();
784-
$structuredUI = $this->getUser()->getOption( 'rcenhancedfilters' );
882+
$structuredUI = $this->isStructuredFilterUiEnabled();
785883
// If urlversion=2 is set, ignore the filter defaults and set them all to false/empty
786884
$useDefaults = $this->getRequest()->getInt( 'urlversion' ) !== 2;
787885

788886
// Add all filters
887+
/** @var ChangesListFilterGroup $filterGroup */
789888
foreach ( $this->filterGroups as $filterGroup ) {
790889
// URL parameters can be per-group, like 'userExpLevel',
791890
// or per-filter, like 'hideminor'.
792891
if ( $filterGroup->isPerGroupRequestParameter() ) {
793892
$opts->add( $filterGroup->getName(), $useDefaults ? $filterGroup->getDefault() : '' );
794893
} else {
894+
/** @var ChangesListBooleanFilter $filter */
795895
foreach ( $filterGroup->getFilters() as $filter ) {
796896
$opts->add( $filter->getName(), $useDefaults ? $filter->getDefault( $structuredUI ) : false );
797897
}
@@ -857,8 +957,6 @@ public function getStructuredFilterJsData() {
857957
'messageKeys' => [],
858958
];
859959

860-
$context = $this->getContext();
861-
862960
usort( $this->filterGroups, function ( $a, $b ) {
863961
return $b->getPriority() - $a->getPriority();
864962
} );
@@ -1055,9 +1153,7 @@ protected function buildQuery( &$tables, &$fields, &$conds, &$query_options,
10551153
&$join_conds, FormOptions $opts
10561154
) {
10571155
$dbr = $this->getDB();
1058-
$user = $this->getUser();
10591156

1060-
$context = $this->getContext();
10611157
foreach ( $this->filterGroups as $filterGroup ) {
10621158
// URL parameters can be per-group, like 'userExpLevel',
10631159
// or per-filter, like 'hideminor'.
@@ -1279,12 +1375,11 @@ public function makeLegend() {
12791375
) . "\n";
12801376
$legend .= Html::closeElement( 'dl' ) . "\n";
12811377

1282-
# Collapsibility
1283-
$legendHeading = $this->getUser()->getOption(
1284-
'rcenhancedfilters'
1285-
) ?
1378+
$legendHeading = $this->isStructuredFilterUiEnabled() ?
12861379
$context->msg( 'rcfilters-legend-heading' )->parse() :
12871380
$context->msg( 'recentchanges-legend-heading' )->parse();
1381+
1382+
# Collapsible
12881383
$legend =
12891384
'<div class="mw-changeslist-legend">' .
12901385
$legendHeading .
@@ -1305,6 +1400,11 @@ protected function addModules() {
13051400
'mediawiki.special.changeslist',
13061401
] );
13071402
$out->addModules( 'mediawiki.special.changeslist.legend.js' );
1403+
1404+
if ( $this->isStructuredFilterUiEnabled() ) {
1405+
$out->addModules( 'mediawiki.rcfilters.filters.ui' );
1406+
$out->addModuleStyles( 'mediawiki.rcfilters.filters.base.styles' );
1407+
}
13081408
}
13091409

13101410
protected function getGroupName() {
@@ -1426,4 +1526,13 @@ public function filterOnUserExperienceLevel( $specialPageClassName, $context, $d
14261526
$conds[] = reset( $conditions );
14271527
}
14281528
}
1529+
1530+
/**
1531+
* Check whether the structured filter UI is enabled
1532+
*
1533+
* @return bool
1534+
*/
1535+
protected function isStructuredFilterUiEnabled() {
1536+
return $this->getUser()->getOption( 'rcenhancedfilters' );
1537+
}
14291538
}

includes/specials/SpecialRecentchanges.php

Lines changed: 8 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -164,94 +164,12 @@ public function execute( $subpage ) {
164164
parent::execute( $subpage );
165165

166166
if ( $this->isStructuredFilterUiEnabled() ) {
167-
$jsData = $this->getStructuredFilterJsData();
168-
169-
$messages = [];
170-
foreach ( $jsData['messageKeys'] as $key ) {
171-
$messages[$key] = $this->msg( $key )->plain();
172-
}
173-
174-
$out->addHTML(
175-
ResourceLoader::makeInlineScript(
176-
ResourceLoader::makeMessageSetScript( $messages )
177-
)
178-
);
179-
180-
$experimentalStructuredChangeFilters =
181-
$this->getConfig()->get( 'StructuredChangeFiltersEnableExperimentalViews' );
182-
183-
$out->addJsConfigVars( 'wgStructuredChangeFilters', $jsData['groups'] );
167+
$out->addJsConfigVars( 'wgStructuredChangeFiltersLiveUpdateSupported', true );
184168
$out->addJsConfigVars(
185-
'wgStructuredChangeFiltersEnableExperimentalViews',
186-
$experimentalStructuredChangeFilters
169+
'wgStructuredChangeFiltersSavedQueriesPreferenceName',
170+
'rcfilters-saved-queries'
187171
);
188-
$out->addJsConfigVars(
189-
'wgStructuredChangeFiltersEnableLiveUpdate',
190-
$this->getConfig()->get( 'StructuredChangeFiltersEnableLiveUpdate' )
191-
);
192-
$out->addJsConfigVars(
193-
'wgRCFiltersChangeTags',
194-
$this->buildChangeTagList()
195-
);
196-
$out->addJsConfigVars(
197-
'StructuredChangeFiltersDisplayConfig',
198-
[
199-
'maxDays' => (int)$this->getConfig()->get( 'RCMaxAge' ) / ( 24 * 3600 ), // Translate to days
200-
'limitArray' => $this->getConfig()->get( 'RCLinkLimits' ),
201-
'daysArray' => $this->getConfig()->get( 'RCLinkDays' ),
202-
]
203-
);
204-
}
205-
}
206-
207-
/**
208-
* Fetch the change tags list for the front end
209-
*
210-
* @return Array Tag data
211-
*/
212-
protected function buildChangeTagList() {
213-
$explicitlyDefinedTags = array_fill_keys( ChangeTags::listExplicitlyDefinedTags(), 0 );
214-
$softwareActivatedTags = array_fill_keys( ChangeTags::listSoftwareActivatedTags(), 0 );
215-
216-
// Hit counts disabled for perf reasons, see T169997
217-
/*
218-
$tagStats = ChangeTags::tagUsageStatistics();
219-
$tagHitCounts = array_merge( $explicitlyDefinedTags, $softwareActivatedTags, $tagStats );
220-
221-
// Sort by hits
222-
arsort( $tagHitCounts );
223-
*/
224-
$tagHitCounts = array_merge( $explicitlyDefinedTags, $softwareActivatedTags );
225-
226-
// Build the list and data
227-
$result = [];
228-
foreach ( $tagHitCounts as $tagName => $hits ) {
229-
if (
230-
// Only get active tags
231-
isset( $explicitlyDefinedTags[ $tagName ] ) ||
232-
isset( $softwareActivatedTags[ $tagName ] )
233-
) {
234-
// Parse description
235-
$desc = ChangeTags::tagLongDescriptionMessage( $tagName, $this->getContext() );
236-
237-
$result[] = [
238-
'name' => $tagName,
239-
'label' => Sanitizer::stripAllTags(
240-
ChangeTags::tagDescription( $tagName, $this->getContext() )
241-
),
242-
'description' => $desc ? Sanitizer::stripAllTags( $desc->parse() ) : '',
243-
'cssClass' => Sanitizer::escapeClass( 'mw-tag-' . $tagName ),
244-
'hits' => $hits,
245-
];
246-
}
247172
}
248-
249-
// Instead of sorting by hit count (disabled, see above), sort by display name
250-
usort( $result, function ( $a, $b ) {
251-
return strcasecmp( $a['label'], $b['label'] );
252-
} );
253-
254-
return $result;
255173
}
256174

257175
/**
@@ -540,8 +458,6 @@ public function outputChangesList( $rows, $opts ) {
540458
&& $this->getUser()->getOption( 'shownumberswatching' );
541459
$watcherCache = [];
542460

543-
$dbr = $this->getDB();
544-
545461
$counter = 1;
546462
$list = ChangesList::newFromContext( $this->getContext(), $this->filterGroups );
547463
$list->initChangesListRows( $rows );
@@ -636,7 +552,7 @@ public function doHeader( $opts, $numRows ) {
636552
++$count;
637553
$addSubmit = ( $count === $extraOptsCount ) ? $submit : '';
638554

639-
$out .= Xml::openElement( 'tr' );
555+
$out .= Xml::openElement( 'tr', [ 'class' => $name . 'Form' ] );
640556
if ( is_array( $optionRow ) ) {
641557
$out .= Xml::tags(
642558
'td',
@@ -673,11 +589,11 @@ public function doHeader( $opts, $numRows ) {
673589
$rcoptions = Xml::fieldset(
674590
$this->msg( 'recentchanges-legend' )->text(),
675591
$panelString,
676-
[ 'class' => 'rcoptions' ]
592+
[ 'class' => 'rcoptions cloptions' ]
677593
);
678594

679595
// Insert a placeholder for RCFilters
680-
if ( $this->getUser()->getOption( 'rcenhancedfilters' ) ) {
596+
if ( $this->isStructuredFilterUiEnabled() ) {
681597
$rcfilterContainer = Html::element(
682598
'div',
683599
[ 'class' => 'rcfilters-container' ]
@@ -729,7 +645,7 @@ function setTopText( FormOptions $opts ) {
729645

730646
$topLinksAttributes = [ 'class' => 'mw-recentchanges-toplinks' ];
731647

732-
if ( $this->getUser()->getOption( 'rcenhancedfilters' ) ) {
648+
if ( $this->isStructuredFilterUiEnabled() ) {
733649
$contentTitle = Html::rawElement( 'div',
734650
[ 'class' => 'mw-recentchanges-toplinks-title' ],
735651
$this->msg( 'rcfilters-other-review-tools' )->parse()
@@ -785,28 +701,13 @@ function getExtraOptions( $opts ) {
785701
return $extraOpts;
786702
}
787703

788-
/**
789-
* Check whether the structured filter UI is enabled
790-
*
791-
* @return bool
792-
*/
793-
protected function isStructuredFilterUiEnabled() {
794-
return $this->getUser()->getOption(
795-
'rcenhancedfilters'
796-
);
797-
}
798-
799704
/**
800705
* Add page-specific modules.
801706
*/
802707
protected function addModules() {
803708
parent::addModules();
804709
$out = $this->getOutput();
805710
$out->addModules( 'mediawiki.special.recentchanges' );
806-
if ( $this->isStructuredFilterUiEnabled() ) {
807-
$out->addModules( 'mediawiki.rcfilters.filters.ui' );
808-
$out->addModuleStyles( 'mediawiki.rcfilters.filters.base.styles' );
809-
}
810711
}
811712

812713
/**
@@ -1030,7 +931,6 @@ function optionsPanel( $defaults, $nondefaults, $numRows ) {
1030931

1031932
$filterGroups = $this->getFilterGroups();
1032933

1033-
$context = $this->getContext();
1034934
foreach ( $filterGroups as $groupName => $group ) {
1035935
if ( !$group->isPerGroupRequestParameter() ) {
1036936
foreach ( $group->getFilters() as $key => $filter ) {
@@ -1047,7 +947,7 @@ function optionsPanel( $defaults, $nondefaults, $numRows ) {
1047947
[ $key => 1 - $options[$key] ], $nondefaults );
1048948

1049949
$attribs = [
1050-
'class' => "$msg rcshowhideoption",
950+
'class' => "$msg rcshowhideoption clshowhideoption",
1051951
'data-filter-name' => $filter->getName(),
1052952
];
1053953

0 commit comments

Comments
 (0)