Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
86.12% covered (warning)
86.12%
912 / 1059
62.26% covered (warning)
62.26%
33 / 53
CRAP
0.00% covered (danger)
0.00%
0 / 1
ChangesListSpecialPage
86.20% covered (warning)
86.20%
912 / 1058
62.26% covered (warning)
62.26%
33 / 53
269.05
0.00% covered (danger)
0.00%
0 / 1
 __construct
96.40% covered (success)
96.40%
428 / 444
0.00% covered (danger)
0.00%
0 / 1
7
 removeRegistrationFilterDefinitions
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
4
 areFiltersInConflict
62.50% covered (warning)
62.50%
10 / 16
0.00% covered (danger)
0.00%
0 / 1
13.27
 execute
52.94% covered (warning)
52.94%
27 / 51
0.00% covered (danger)
0.00%
0 / 1
42.68
 setTempUserConfig
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 considerActionsForDefaultSavedQuery
39.39% covered (danger)
39.39%
13 / 33
0.00% covered (danger)
0.00%
0 / 1
27.03
 getLinkDays
45.45% covered (danger)
45.45%
5 / 11
0.00% covered (danger)
0.00%
0 / 1
6.60
 includeRcFiltersApp
95.24% covered (success)
95.24%
40 / 42
0.00% covered (danger)
0.00%
0 / 1
5
 getRcFiltersConfigSummary
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 getRcFiltersConfigVars
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 outputNoResults
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
1
 outputTimeout
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getRows
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 getOptions
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 registerFilters
100.00% covered (success)
100.00%
57 / 57
100.00% covered (success)
100.00%
1 / 1
7
 transformFilterDefinition
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 registerFiltersFromDefinitions
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
4
 getLegacyShowHideFilters
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 setup
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
2
 getDefaultOptions
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
2
 registerFilterGroup
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getFilterGroups
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getFilterGroup
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getStructuredFilterJsData
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
3
 fetchOptionsFromRequest
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 parseParameters
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
10
 validateOptions
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 fixContradictoryOptions
94.44% covered (success)
94.44%
17 / 18
0.00% covered (danger)
0.00%
0 / 1
7.01
 fixBackwardsCompatibilityOptions
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
4
 replaceOldOptions
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
7
 convertParamsForLink
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 buildQuery
96.43% covered (success)
96.43%
27 / 28
0.00% covered (danger)
0.00%
0 / 1
8
 doMainQuery
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
6
 runMainQueryHook
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDB
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 webOutputHeader
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 webOutput
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 outputFeedLinks
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 outputChangesList
n/a
0 / 0
n/a
0 / 0
0
 doHeader
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 setTopText
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 setBottomText
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getExtraOptions
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 makeLegend
67.80% covered (warning)
67.80%
40 / 59
0.00% covered (danger)
0.00%
0 / 1
10.14
 addModules
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
3
 getGroupName
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRegisteredExpr
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 getExperienceExpr
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
3
 filterOnUserExperienceLevel
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
11
 isStructuredFilterUiEnabled
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 checkStructuredFilterUiEnabled
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultLimit
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultDays
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 getLimitPreferenceName
n/a
0 / 0
n/a
0 / 0
0
 getSavedQueriesPreferenceName
n/a
0 / 0
n/a
0 / 0
0
 getDefaultDaysPreferenceName
n/a
0 / 0
n/a
0 / 0
0
 getCollapsedPreferenceName
n/a
0 / 0
n/a
0 / 0
0
 expandSymbolicNamespaceFilters
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License along
14 * with this program; if not, write to the Free Software Foundation, Inc.,
15 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 * http://www.gnu.org/copyleft/gpl.html
17 *
18 * @file
19 */
20
21namespace MediaWiki\SpecialPage;
22
23use ChangesListBooleanFilter;
24use ChangesListBooleanFilterGroup;
25use ChangesListFilterGroup;
26use ChangesListStringOptionsFilterGroup;
27use ChangeTags;
28use MediaWiki\Context\IContextSource;
29use MediaWiki\Html\FormOptions;
30use MediaWiki\Html\Html;
31use MediaWiki\Json\FormatJson;
32use MediaWiki\MainConfigNames;
33use MediaWiki\MediaWikiServices;
34use MediaWiki\Parser\Sanitizer;
35use MediaWiki\ResourceLoader as RL;
36use MediaWiki\User\TempUser\TempUserConfig;
37use MediaWiki\User\UserArray;
38use MediaWiki\User\UserIdentity;
39use MediaWiki\User\UserIdentityUtils;
40use MWExceptionHandler;
41use OOUI\IconWidget;
42use RecentChange;
43use Wikimedia\Rdbms\DBQueryTimeoutError;
44use Wikimedia\Rdbms\FakeResultWrapper;
45use Wikimedia\Rdbms\IExpression;
46use Wikimedia\Rdbms\IReadableDatabase;
47use Wikimedia\Rdbms\IResultWrapper;
48use Wikimedia\Rdbms\RawSQLValue;
49use Wikimedia\Timestamp\ConvertibleTimestamp;
50
51/**
52 * Special page which uses a ChangesList to show query results.
53 *
54 * @todo Most of the functions here should be protected instead of public.
55 *
56 * @ingroup RecentChanges
57 * @ingroup SpecialPage
58 */
59abstract class ChangesListSpecialPage extends SpecialPage {
60
61    /** @var string */
62    protected $rcSubpage;
63
64    /** @var FormOptions */
65    protected $rcOptions;
66
67    protected UserIdentityUtils $userIdentityUtils;
68    protected TempUserConfig $tempUserConfig;
69
70    // Order of both groups and filters is significant; first is top-most priority,
71    // descending from there.
72    // 'showHideSuffix' is a shortcut to and avoid spelling out
73    // details specific to subclasses here.
74    /**
75     * Definition information for the filters and their groups
76     *
77     * The value is $groupDefinition, a parameter to the ChangesListFilterGroup constructor.
78     * However, priority is dynamically added for the core groups, to ease maintenance.
79     *
80     * Groups are displayed to the user in the structured UI.  However, if necessary,
81     * all of the filters in a group can be configured to only display on the
82     * unstuctured UI, in which case you don't need a group title.
83     *
84     * @var array
85     */
86    private $filterGroupDefinitions;
87
88    /**
89     * @var array Same format as filterGroupDefinitions, but for a single group (reviewStatus)
90     * that is registered conditionally.
91     */
92    private $legacyReviewStatusFilterGroupDefinition;
93
94    /** @var array Single filter group registered conditionally */
95    private $reviewStatusFilterGroupDefinition;
96
97    /** @var array Single filter group registered conditionally */
98    private $hideCategorizationFilterDefinition;
99
100    /**
101     * Filter groups, and their contained filters
102     * This is an associative array (with group name as key) of ChangesListFilterGroup objects.
103     *
104     * @var ChangesListFilterGroup[]
105     */
106    protected $filterGroups = [];
107
108    /**
109     * @param string $name
110     * @param string $restriction
111     * @param UserIdentityUtils $userIdentityUtils
112     * @param TempUserConfig $tempUserConfig
113     */
114    public function __construct(
115        $name,
116        $restriction,
117        UserIdentityUtils $userIdentityUtils,
118        TempUserConfig $tempUserConfig
119    ) {
120        parent::__construct( $name, $restriction );
121
122        $this->userIdentityUtils = $userIdentityUtils;
123        $this->tempUserConfig = $tempUserConfig;
124
125        $nonRevisionTypes = [ RC_LOG ];
126        $this->getHookRunner()->onSpecialWatchlistGetNonRevisionTypes( $nonRevisionTypes );
127
128        $this->filterGroupDefinitions = [
129            [
130                'name' => 'registration',
131                'title' => 'rcfilters-filtergroup-registration',
132                'class' => ChangesListBooleanFilterGroup::class,
133                'filters' => [
134                    [
135                        'name' => 'hideliu',
136                        // rcshowhideliu-show, rcshowhideliu-hide,
137                        // wlshowhideliu
138                        'showHideSuffix' => 'showhideliu',
139                        'default' => false,
140                        'queryCallable' => function ( string $specialClassName, IContextSource $ctx,
141                            IReadableDatabase $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds
142                        ) {
143                            $conds[] = $this->getRegisteredExpr( false, $dbr );
144                            $join_conds['recentchanges_actor'] = [ 'JOIN', 'actor_id=rc_actor' ];
145                        },
146                        'isReplacedInStructuredUi' => true,
147
148                    ],
149                    [
150                        'name' => 'hideanons',
151                        // rcshowhideanons-show, rcshowhideanons-hide,
152                        // wlshowhideanons
153                        'showHideSuffix' => 'showhideanons',
154                        'default' => false,
155                        'queryCallable' => function ( string $specialClassName, IContextSource $ctx,
156                            IReadableDatabase $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds
157                        ) {
158                            $conds[] = $this->getRegisteredExpr( true, $dbr );
159                            $join_conds['recentchanges_actor'] = [ 'JOIN', 'actor_id=rc_actor' ];
160                        },
161                        'isReplacedInStructuredUi' => true,
162                    ]
163                ],
164            ],
165
166            [
167                'name' => 'userExpLevel',
168                'title' => 'rcfilters-filtergroup-user-experience-level',
169                'class' => ChangesListStringOptionsFilterGroup::class,
170                'isFullCoverage' => true,
171                'filters' => [
172                    [
173                        'name' => 'unregistered',
174                        'label' => 'rcfilters-filter-user-experience-level-unregistered-label',
175                        'description' => $this->tempUserConfig->isKnown() ?
176                            'rcfilters-filter-user-experience-level-unregistered-description-temp' :
177                            'rcfilters-filter-user-experience-level-unregistered-description',
178                        'cssClassSuffix' => 'user-unregistered',
179                        'isRowApplicableCallable' => function ( IContextSource $ctx, RecentChange $rc ) {
180                            return !$this->userIdentityUtils->isNamed( $rc->getPerformerIdentity() );
181                        }
182                    ],
183                    [
184                        'name' => 'registered',
185                        'label' => 'rcfilters-filter-user-experience-level-registered-label',
186                        'description' => 'rcfilters-filter-user-experience-level-registered-description',
187                        'cssClassSuffix' => 'user-registered',
188                        'isRowApplicableCallable' => function ( IContextSource $ctx, RecentChange $rc ) {
189                            return $this->userIdentityUtils->isNamed( $rc->getPerformerIdentity() );
190                        }
191                    ],
192                    [
193                        'name' => 'newcomer',
194                        'label' => 'rcfilters-filter-user-experience-level-newcomer-label',
195                        'description' => 'rcfilters-filter-user-experience-level-newcomer-description',