3 use MediaWiki\MediaWikiServices
;
4 use Wikimedia\TestingAccessWrapper
;
7 * Test class for ChangesListSpecialPage class
9 * Copyright © 2011-, Antoine Musso, Stephane Bisson, Matthew Flaschen
11 * @author Antoine Musso
12 * @author Stephane Bisson
13 * @author Matthew Flaschen
16 * @covers ChangesListSpecialPage
18 class ChangesListSpecialPageTest
extends AbstractChangesListSpecialPageTestCase
{
19 protected function getPage() {
20 $mock = $this->getMockBuilder( ChangesListSpecialPage
::class )
23 'ChangesListSpecialPage',
27 ->setMethods( [ 'getPageTitle' ] )
28 ->getMockForAbstractClass();
30 $mock->method( 'getPageTitle' )->willReturn(
31 Title
::makeTitle( NS_SPECIAL
, 'ChangesListSpecialPage' )
34 $mock = TestingAccessWrapper
::newFromObject(
41 private function buildQuery(
42 $requestOptions = null,
45 $context = new RequestContext
;
46 $context->setRequest( new FauxRequest( $requestOptions ) );
48 $context->setUser( $user );
51 $this->changesListSpecialPage
->setContext( $context );
52 $this->changesListSpecialPage
->filterGroups
= [];
53 $formOptions = $this->changesListSpecialPage
->setup( null );
55 # Filter out rc_timestamp conditions which depends on the test runtime
56 # This condition is not needed as of march 2, 2011 -- hashar
57 # @todo FIXME: Find a way to generate the correct rc_timestamp
61 $queryConditions = [];
66 [ $this->changesListSpecialPage
, 'buildQuery' ],
77 $queryConditions = array_filter(
79 'ChangesListSpecialPageTest::filterOutRcTimestampCondition'
82 return $queryConditions;
85 /** helper to test SpecialRecentchanges::buildQuery() */
86 private function assertConditions(
88 $requestOptions = null,
92 $queryConditions = $this->buildQuery( $requestOptions, $user );
95 self
::normalizeCondition( $expected ),
96 self
::normalizeCondition( $queryConditions ),
101 private static function normalizeCondition( $conds ) {
102 $dbr = wfGetDB( DB_REPLICA
);
103 $normalized = array_map(
104 function ( $k, $v ) use ( $dbr ) {
105 if ( is_array( $v ) ) {
108 // (Ab)use makeList() to format only this entry
109 return $dbr->makeList( [ $k => $v ], Database
::LIST_AND
);
111 array_keys( $conds ),
118 /** return false if condition begins with 'rc_timestamp ' */
119 private static function filterOutRcTimestampCondition( $var ) {
120 return ( is_array( $var ) ||
strpos( $var, 'rc_timestamp ' ) === false );
123 public function testRcNsFilter() {
124 $this->assertConditions(
126 "rc_namespace = '0'",
129 'namespace' => NS_MAIN
,
131 "rc conditions with one namespace"
135 public function testRcNsFilterInversion() {
136 $this->assertConditions(
138 "rc_namespace != '0'",
141 'namespace' => NS_MAIN
,
144 "rc conditions with namespace inverted"
148 public function testRcNsFilterMultiple() {
149 $this->assertConditions(
151 "rc_namespace IN ('1','2','3')",
154 'namespace' => '1;2;3',
156 "rc conditions with multiple namespaces"
160 public function testRcNsFilterMultipleAssociated() {
161 $this->assertConditions(
163 "rc_namespace IN ('0','1','4','5','6','7')",
166 'namespace' => '1;4;7',
169 "rc conditions with multiple namespaces and associated"
173 public function testRcNsFilterAssociatedSpecial() {
174 $this->assertConditions(
176 "rc_namespace IN ('-1','0','1')",
179 'namespace' => '1;-1',
182 "rc conditions with associated and special namespace"
186 public function testRcNsFilterMultipleAssociatedInvert() {
187 $this->assertConditions(
189 "rc_namespace NOT IN ('2','3','8','9')",
192 'namespace' => '2;3;9',
196 "rc conditions with multiple namespaces, associated and inverted"
200 public function testRcNsFilterMultipleInvert() {
201 $this->assertConditions(
203 "rc_namespace NOT IN ('1','2','3')",
206 'namespace' => '1;2;3',
209 "rc conditions with multiple namespaces inverted"
213 public function testRcNsFilterAllContents() {
214 $namespaces = MediaWikiServices
::getInstance()->getNamespaceInfo()->getSubjectNamespaces();
215 $this->assertConditions(
217 'rc_namespace IN (' . $this->db
->makeList( $namespaces ) . ')',
220 'namespace' => 'all-contents',
222 "rc conditions with all-contents"
226 public function testRcHidemyselfFilter() {
227 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW
);
229 $user = $this->getTestUser()->getUser();
230 $user->getActorId( wfGetDB( DB_MASTER
) );
231 $this->assertConditions(
233 "NOT((rc_actor = '{$user->getActorId()}'))",
238 "rc conditions: hidemyself=1 (logged in)",
242 $user = User
::newFromName( '10.11.12.13', false );
243 $id = $user->getActorId( wfGetDB( DB_MASTER
) );
244 $this->assertConditions(
246 "NOT((rc_actor = '{$user->getActorId()}'))",
251 "rc conditions: hidemyself=1 (anon)",
256 public function testRcHidemyselfFilter_old() {
258 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
261 $user = $this->getTestUser()->getUser();
262 $user->getActorId( wfGetDB( DB_MASTER
) );
263 $this->assertConditions(
265 "NOT((rc_user = '{$user->getId()}'))",
270 "rc conditions: hidemyself=1 (logged in)",
274 $user = User
::newFromName( '10.11.12.13', false );
275 $id = $user->getActorId( wfGetDB( DB_MASTER
) );
276 $this->assertConditions(
278 "NOT((rc_user_text = '10.11.12.13'))",
283 "rc conditions: hidemyself=1 (anon)",
288 public function testRcHidebyothersFilter() {
289 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW
);
291 $user = $this->getTestUser()->getUser();
292 $user->getActorId( wfGetDB( DB_MASTER
) );
293 $this->assertConditions(
295 "(rc_actor = '{$user->getActorId()}')",
300 "rc conditions: hidebyothers=1 (logged in)",
304 $user = User
::newFromName( '10.11.12.13', false );
305 $id = $user->getActorId( wfGetDB( DB_MASTER
) );
306 $this->assertConditions(
308 "(rc_actor = '{$user->getActorId()}')",
313 "rc conditions: hidebyothers=1 (anon)",
318 public function testRcHidebyothersFilter_old() {
320 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
323 $user = $this->getTestUser()->getUser();
324 $user->getActorId( wfGetDB( DB_MASTER
) );
325 $this->assertConditions(
327 "(rc_user_text = '{$user->getName()}')",
332 "rc conditions: hidebyothers=1 (logged in)",
336 $user = User
::newFromName( '10.11.12.13', false );
337 $id = $user->getActorId( wfGetDB( DB_MASTER
) );
338 $this->assertConditions(
340 "(rc_user_text = '10.11.12.13')",
345 "rc conditions: hidebyothers=1 (anon)",
350 public function testRcHidepageedits() {
351 $this->assertConditions(
356 'hidepageedits' => 1,
358 "rc conditions: hidepageedits=1"
362 public function testRcHidenewpages() {
363 $this->assertConditions(
370 "rc conditions: hidenewpages=1"
374 public function testRcHidelog() {
375 $this->assertConditions(
382 "rc conditions: hidelog=1"
386 public function testRcHidehumans() {
387 $this->assertConditions(
395 "rc conditions: hidebots=0 hidehumans=1"
399 public function testRcHidepatrolledDisabledFilter() {
400 $this->setMwGlobals( 'wgUseRCPatrol', false );
401 $user = $this->getTestUser()->getUser();
402 $this->assertConditions(
406 'hidepatrolled' => 1,
408 "rc conditions: hidepatrolled=1 (user not allowed)",
413 public function testRcHideunpatrolledDisabledFilter() {
414 $this->setMwGlobals( 'wgUseRCPatrol', false );
415 $user = $this->getTestUser()->getUser();
416 $this->assertConditions(
420 'hideunpatrolled' => 1,
422 "rc conditions: hideunpatrolled=1 (user not allowed)",
427 public function testRcHidepatrolledFilter() {
428 $user = $this->getTestSysop()->getUser();
429 $this->assertConditions(
434 'hidepatrolled' => 1,
436 "rc conditions: hidepatrolled=1",
441 public function testRcHideunpatrolledFilter() {
442 $user = $this->getTestSysop()->getUser();
443 $this->assertConditions(
445 'rc_patrolled' => [ 1, 2 ],
448 'hideunpatrolled' => 1,
450 "rc conditions: hideunpatrolled=1",
455 public function testRcReviewStatusFilter() {
456 $user = $this->getTestSysop()->getUser();
457 $this->assertConditions(
462 'reviewStatus' => 'manual'
464 "rc conditions: reviewStatus=manual",
467 $this->assertConditions(
469 'rc_patrolled' => [ 0, 2 ],
472 'reviewStatus' => 'unpatrolled;auto'
474 "rc conditions: reviewStatus=unpatrolled;auto",
479 public function testRcHideminorFilter() {
480 $this->assertConditions(
487 "rc conditions: hideminor=1"
491 public function testRcHidemajorFilter() {
492 $this->assertConditions(
499 "rc conditions: hidemajor=1"
503 public function testHideCategorization() {
504 $this->assertConditions(
510 'hidecategorization' => 1
512 "rc conditions: hidecategorization=1"
516 public function testFilterUserExpLevelAll() {
517 $this->assertConditions(
522 'userExpLevel' => 'registered;unregistered;newcomer;learner;experienced',
524 "rc conditions: userExpLevel=registered;unregistered;newcomer;learner;experienced"
528 public function testFilterUserExpLevelRegisteredUnregistered() {
529 $this->assertConditions(
534 'userExpLevel' => 'registered;unregistered',
536 "rc conditions: userExpLevel=registered;unregistered"
540 public function testFilterUserExpLevelRegisteredUnregisteredLearner() {
541 $this->assertConditions(
546 'userExpLevel' => 'registered;unregistered;learner',
548 "rc conditions: userExpLevel=registered;unregistered;learner"
552 public function testFilterUserExpLevelAllExperienceLevels() {
553 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW
);
555 $this->assertConditions(
558 'actor_rc_user.actor_user IS NOT NULL',
561 'userExpLevel' => 'newcomer;learner;experienced',
563 "rc conditions: userExpLevel=newcomer;learner;experienced"
567 public function testFilterUserExpLevelAllExperienceLevels_old() {
569 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
572 $this->assertConditions(
578 'userExpLevel' => 'newcomer;learner;experienced',
580 "rc conditions: userExpLevel=newcomer;learner;experienced"
584 public function testFilterUserExpLevelRegistrered() {
585 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW
);
587 $this->assertConditions(
590 'actor_rc_user.actor_user IS NOT NULL',
593 'userExpLevel' => 'registered',
595 "rc conditions: userExpLevel=registered"
599 public function testFilterUserExpLevelRegistrered_old() {
601 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
604 $this->assertConditions(
610 'userExpLevel' => 'registered',
612 "rc conditions: userExpLevel=registered"
616 public function testFilterUserExpLevelUnregistrered() {
617 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW
);
619 $this->assertConditions(
622 'actor_rc_user.actor_user IS NULL',
625 'userExpLevel' => 'unregistered',
627 "rc conditions: userExpLevel=unregistered"
631 public function testFilterUserExpLevelUnregistrered_old() {
633 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
636 $this->assertConditions(
642 'userExpLevel' => 'unregistered',
644 "rc conditions: userExpLevel=unregistered"
648 public function testFilterUserExpLevelRegistreredOrLearner() {
649 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW
);
651 $this->assertConditions(
654 'actor_rc_user.actor_user IS NOT NULL',
657 'userExpLevel' => 'registered;learner',
659 "rc conditions: userExpLevel=registered;learner"
663 public function testFilterUserExpLevelRegistreredOrLearner_old() {
665 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
668 $this->assertConditions(
674 'userExpLevel' => 'registered;learner',
676 "rc conditions: userExpLevel=registered;learner"
680 public function testFilterUserExpLevelUnregistreredOrExperienced() {
681 $this->setMwGlobals( 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_NEW
);
683 $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
686 '/\(actor_rc_user\.actor_user IS NULL\) OR '
687 . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
689 "rc conditions: userExpLevel=unregistered;experienced"
693 public function testFilterUserExpLevelUnregistreredOrExperienced_old() {
695 'wgActorTableSchemaMigrationStage', SCHEMA_COMPAT_WRITE_BOTH | SCHEMA_COMPAT_READ_OLD
698 $conds = $this->buildQuery( [ 'userExpLevel' => 'unregistered;experienced' ] );
701 '/\(rc_user = 0\) OR '
702 . '\(\(user_editcount >= 500\) AND \(user_registration <= \'[^\']+\'\)\)/',
704 "rc conditions: userExpLevel=unregistered;experienced"
708 public function testFilterUserExpLevel() {
710 $this->setMwGlobals( [
711 'wgLearnerEdits' => 10,
712 'wgLearnerMemberSince' => 4,
713 'wgExperiencedUserEdits' => 500,
714 'wgExperiencedUserMemberSince' => 30,
717 $this->createUsers( [
718 'Newcomer1' => [ 'edits' => 2, 'days' => 2 ],
719 'Newcomer2' => [ 'edits' => 12, 'days' => 3 ],
720 'Newcomer3' => [ 'edits' => 8, 'days' => 5 ],
721 'Learner1' => [ 'edits' => 15, 'days' => 10 ],
722 'Learner2' => [ 'edits' => 450, 'days' => 20 ],
723 'Learner3' => [ 'edits' => 460, 'days' => 33 ],
724 'Learner4' => [ 'edits' => 525, 'days' => 28 ],
725 'Experienced1' => [ 'edits' => 538, 'days' => 33 ],
729 $this->assertArrayEquals(
730 [ 'Newcomer1', 'Newcomer2', 'Newcomer3' ],
731 $this->fetchUsers( [ 'newcomer' ], $now )
734 // newcomers and learner
735 $this->assertArrayEquals(
737 'Newcomer1', 'Newcomer2', 'Newcomer3',
738 'Learner1', 'Learner2', 'Learner3', 'Learner4',
740 $this->fetchUsers( [ 'newcomer', 'learner' ], $now )
743 // newcomers and more learner
744 $this->assertArrayEquals(
746 'Newcomer1', 'Newcomer2', 'Newcomer3',
749 $this->fetchUsers( [ 'newcomer', 'experienced' ], $now )
753 $this->assertArrayEquals(
754 [ 'Learner1', 'Learner2', 'Learner3', 'Learner4' ],
755 $this->fetchUsers( [ 'learner' ], $now )
758 // more experienced only
759 $this->assertArrayEquals(
761 $this->fetchUsers( [ 'experienced' ], $now )
764 // learner and more experienced
765 $this->assertArrayEquals(
767 'Learner1', 'Learner2', 'Learner3', 'Learner4',
770 $this->fetchUsers( [ 'learner', 'experienced' ], $now )
774 private function createUsers( $specs, $now ) {
775 $dbw = wfGetDB( DB_MASTER
);
776 foreach ( $specs as $name => $spec ) {
780 'editcount' => $spec['edits'],
781 'registration' => $dbw->timestamp( $this->daysAgo( $spec['days'], $now ) ),
788 private function fetchUsers( $filters, $now ) {
797 call_user_func_array(
798 [ $this->changesListSpecialPage
, 'filterOnUserExperienceLevel' ],
800 get_class( $this->changesListSpecialPage
),
801 $this->changesListSpecialPage
->getContext(),
802 $this->changesListSpecialPage
->getDB(),
813 // @todo: This is not at all safe or sane. It just blindly assumes
814 // nothing in $conds depends on any other tables.
815 $result = wfGetDB( DB_MASTER
)->select(
818 array_filter( $conds ) +
[ 'user_email' => 'ut' ]
822 foreach ( $result as $row ) {
823 $usernames[] = $row->user_name
;
829 private function daysAgo( $days, $now ) {
830 $secondsPerDay = 86400;
831 return $now - $days * $secondsPerDay;
834 public function testGetStructuredFilterJsData() {
835 $this->changesListSpecialPage
->filterGroups
= [];
839 'name' => 'gub-group',
840 'title' => 'gub-group-title',
841 'class' => ChangesListBooleanFilterGroup
::class,
845 'label' => 'foo-label',
846 'description' => 'foo-description',
848 'showHide' => 'showhidefoo',
853 'label' => 'bar-label',
854 'description' => 'bar-description',
862 'name' => 'des-group',
863 'title' => 'des-group-title',
864 'class' => ChangesListStringOptionsFilterGroup
::class,
865 'isFullCoverage' => true,
869 'label' => 'grault-label',
870 'description' => 'grault-description',
874 'label' => 'garply-label',
875 'description' => 'garply-description',
878 'queryCallable' => function () {
880 'default' => ChangesListStringOptionsFilterGroup
::NONE
,
884 'name' => 'unstructured',
885 'class' => ChangesListBooleanFilterGroup
::class,
888 'name' => 'hidethud',
889 'showHide' => 'showhidethud',
895 'showHide' => 'showhidemos',
903 $this->changesListSpecialPage
->registerFiltersFromDefinitions( $definition );
905 $this->assertArrayEquals(
907 // Filters that only display in the unstructured UI are
908 // are not included, and neither are groups that would
909 // be empty due to the above.
912 'name' => 'gub-group',
913 'title' => 'gub-group-title',
914 'type' => ChangesListBooleanFilterGroup
::TYPE
,
919 'label' => 'bar-label',
920 'description' => 'bar-description',
926 'defaultHighlightColor' => null
930 'label' => 'foo-label',
931 'description' => 'foo-description',
937 'defaultHighlightColor' => null
940 'fullCoverage' => true,
945 'name' => 'des-group',
946 'title' => 'des-group-title',
947 'type' => ChangesListStringOptionsFilterGroup
::TYPE
,
949 'fullCoverage' => true,
953 'label' => 'grault-label',
954 'description' => 'grault-description',
959 'defaultHighlightColor' => null
963 'label' => 'garply-label',
964 'description' => 'garply-description',
969 'defaultHighlightColor' => null
974 'default' => ChangesListStringOptionsFilterGroup
::NONE
,
985 'grault-description',
987 'garply-description',
990 $this->changesListSpecialPage
->getStructuredFilterJsData(),
991 /** ordered= */ false,
996 public function provideParseParameters() {
998 [ 'hidebots', [ 'hidebots' => true ] ],
1000 [ 'bots', [ 'hidebots' => false ] ],
1002 [ 'hideminor', [ 'hideminor' => true ] ],
1004 [ 'minor', [ 'hideminor' => false ] ],
1006 [ 'hidemajor', [ 'hidemajor' => true ] ],
1008 [ 'hideliu', [ 'hideliu' => true ] ],
1010 [ 'hidepatrolled', [ 'hidepatrolled' => true ] ],
1012 [ 'hideunpatrolled', [ 'hideunpatrolled' => true ] ],
1014 [ 'hideanons', [ 'hideanons' => true ] ],
1016 [ 'hidemyself', [ 'hidemyself' => true ] ],
1018 [ 'hidebyothers', [ 'hidebyothers' => true ] ],
1020 [ 'hidehumans', [ 'hidehumans' => true ] ],
1022 [ 'hidepageedits', [ 'hidepageedits' => true ] ],
1024 [ 'pagedits', [ 'hidepageedits' => false ] ],
1026 [ 'hidenewpages', [ 'hidenewpages' => true ] ],
1028 [ 'hidecategorization', [ 'hidecategorization' => true ] ],
1030 [ 'hidelog', [ 'hidelog' => true ] ],
1033 'userExpLevel=learner;experienced',
1035 'userExpLevel' => 'learner;experienced'
1039 // A few random combos
1041 'bots,hideliu,hidemyself',
1043 'hidebots' => false,
1045 'hidemyself' => true,
1050 'minor,hideanons,categorization',
1052 'hideminor' => false,
1053 'hideanons' => true,
1054 'hidecategorization' => false,
1059 'hidehumans,bots,hidecategorization',
1061 'hidehumans' => true,
1062 'hidebots' => false,
1063 'hidecategorization' => true,
1068 'hidemyself,userExpLevel=newcomer;learner,hideminor',
1070 'hidemyself' => true,
1071 'hideminor' => true,
1072 'userExpLevel' => 'newcomer;learner',
1078 public function provideGetFilterConflicts() {
1082 "expectedConflicts" => false,
1087 "userExpLevel" => "newcomer",
1089 "expectedConflicts" => false,
1093 "hideanons" => true,
1094 "userExpLevel" => "learner",
1096 "expectedConflicts" => false,
1100 "hidemajor" => true,
1101 "hidenewpages" => true,
1102 "hidepageedits" => true,
1103 "hidecategorization" => false,
1105 "hideWikidata" => true,
1107 "expectedConflicts" => true,
1111 "hidemajor" => true,
1112 "hidenewpages" => false,
1113 "hidepageedits" => true,
1114 "hidecategorization" => false,
1116 "hideWikidata" => true,
1118 "expectedConflicts" => true,
1122 "hidemajor" => true,
1123 "hidenewpages" => false,
1124 "hidepageedits" => false,
1125 "hidecategorization" => true,
1127 "hideWikidata" => true,
1129 "expectedConflicts" => false,
1133 "hideminor" => true,
1134 "hidenewpages" => true,
1135 "hidepageedits" => true,
1136 "hidecategorization" => false,
1138 "hideWikidata" => true,
1140 "expectedConflicts" => false,
1146 * @dataProvider provideGetFilterConflicts
1148 public function testGetFilterConflicts( $parameters, $expectedConflicts ) {
1149 $context = new RequestContext
;
1150 $context->setRequest( new FauxRequest( $parameters ) );
1151 $this->changesListSpecialPage
->setContext( $context );
1153 $this->assertEquals(
1155 $this->changesListSpecialPage
->areFiltersInConflict()
1159 public function validateOptionsProvider() {
1162 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 1 ],
1164 [ 'userExpLevel' => 'unregistered', 'hidebots' => 1, ],
1168 [ 'hideanons' => 1, 'hideliu' => 1, 'hidebots' => 0 ],
1170 [ 'hidebots' => 0, 'hidehumans' => 1 ],
1174 [ 'hideanons' => 1 ],
1176 [ 'userExpLevel' => 'registered' ],
1182 [ 'userExpLevel' => 'unregistered' ],
1186 [ 'hideanons' => 1, 'hidebots' => 1 ],
1188 [ 'userExpLevel' => 'registered', 'hidebots' => 1 ],
1192 [ 'hideliu' => 1, 'hidebots' => 0 ],
1194 [ 'userExpLevel' => 'unregistered', 'hidebots' => 0 ],
1198 [ 'hidemyself' => 1, 'hidebyothers' => 1 ],
1204 [ 'hidebots' => 1, 'hidehumans' => 1 ],
1210 [ 'hidepatrolled' => 1, 'hideunpatrolled' => 1 ],
1216 [ 'hideminor' => 1, 'hidemajor' => 1 ],
1223 [ 'hidepageedits' => 1, 'hidenewpages' => 1, 'hidecategorization' => 1, 'hidelog' => 1, ],