Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
31.82% covered (danger)
31.82%
14 / 44
30.00% covered (danger)
30.00%
3 / 10
CRAP
0.00% covered (danger)
0.00%
0 / 1
ChangesListBooleanFilter
32.56% covered (danger)
32.56%
14 / 43
30.00% covered (danger)
30.00%
3 / 10
142.70
0.00% covered (danger)
0.00%
0 / 1
 __construct
80.00% covered (warning)
80.00%
8 / 10
0.00% covered (danger)
0.00%
0 / 1
4.13
 getDefault
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 setDefault
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getShowHide
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 displaysOnUnstructuredUi
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isFeatureAvailableOnStructuredUi
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 modifyQuery
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
6
 getJsData
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 isSelected
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 isActive
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
1<?php
2/**
3 * @license GPL-2.0-or-later
4 * @file
5 */
6
7namespace MediaWiki\RecentChanges;
8
9use InvalidArgumentException;
10use MediaWiki\Html\FormOptions;
11use MediaWiki\SpecialPage\ChangesListSpecialPage;
12use Wikimedia\Rdbms\IReadableDatabase;
13
14/**
15 * Represents a hide-based boolean filter (used on ChangesListSpecialPage and descendants)
16 *
17 * @since 1.29
18 * @ingroup RecentChanges
19 * @author Matthew Flaschen
20 */
21class ChangesListBooleanFilter extends ChangesListFilter {
22    /**
23     * Main unstructured UI i18n key
24     *
25     * @var string
26     */
27    protected $showHide;
28
29    /**
30     * Whether there is a feature designed to replace this filter available on the
31     * structured UI
32     *
33     * @var bool
34     */
35    protected $isReplacedInStructuredUi;
36
37    /**
38     * Default
39     *
40     * @var bool
41     */
42    protected $defaultValue;
43
44    /**
45     * Callable used to do the actual query modification; see constructor
46     *
47     * @var callable
48     */
49    protected $queryCallable;
50
51    /**
52     * Value that defined when this filter is considered active
53     *
54     * @var bool
55     */
56    protected $activeValue;
57
58    /**
59     * Create a new filter with the specified configuration.
60     *
61     * It infers which UI (it can be either or both) to display the filter on based on
62     * which messages are provided.
63     *
64     * If 'label' is provided, it will be displayed on the structured UI.  If
65     * 'showHide' is provided, it will be displayed on the unstructured UI.  Thus,
66     * 'label', 'description', and 'showHide' are optional depending on which UI
67     * it's for.
68     *
69     * @param array $filterDefinition ChangesListFilter definition
70     * * $filterDefinition['name'] string Name.  Used as URL parameter.
71     * * $filterDefinition['group'] ChangesListFilterGroup Group.  Filter group this
72     *     belongs to.
73     * * $filterDefinition['label'] string i18n key of label for structured UI.
74     * * $filterDefinition['description'] string i18n key of description for structured
75     *     UI.
76     * * $filterDefinition['showHide'] string Main i18n key used for unstructured UI.
77     * * $filterDefinition['isReplacedInStructuredUi'] bool Whether there is an
78     *     equivalent feature available in the structured UI; this is optional, defaulting
79     *     to true.  It does not need to be set if the exact same filter is simply visible
80     *     on both.
81     * * $filterDefinition['default'] bool Default
82     * * $filterDefinition['activeValue'] bool This filter is considered active when
83     *     its value is equal to its activeValue. Default is true.
84     * * $filterDefinition['priority'] int Priority integer.  Higher value means higher
85     *     up in the group's filter list.
86     * * $filterDefinition['queryCallable'] callable Callable accepting parameters, used
87     *     to implement filter's DB query modification.  Required, except for legacy
88     *     filters that still use the query hooks directly.  Callback parameters:
89     *     * string $specialPageClassName Class name of current special page
90     *     * IContextSource $context Context, for e.g. user
91     *     * IDatabase $dbr Database, for addQuotes, makeList, and similar
92     *     * array &$tables Array of tables; see IDatabase::select $table
93     *     * array &$fields Array of fields; see IDatabase::select $vars
94     *     * array &$conds Array of conditions; see IDatabase::select $conds
95     *     * array &$query_options Array of query options; see IDatabase::select $options
96     *     * array &$join_conds Array of join conditions; see IDatabase::select $join_conds
97     */
98    public function __construct( $filterDefinition ) {
99        parent::__construct( $filterDefinition );
100
101        if ( isset( $filterDefinition['showHide'] ) ) {
102            $this->showHide = $filterDefinition['showHide'];
103        }
104
105        $this->isReplacedInStructuredUi = $filterDefinition['isReplacedInStructuredUi'] ?? false;
106
107        if ( isset( $filterDefinition['default'] ) ) {
108            $this->setDefault( $filterDefinition['default'] );
109        } else {
110            throw new InvalidArgumentException( 'You must set a default' );
111        }
112
113        if ( isset( $filterDefinition['queryCallable'] ) ) {
114            $this->queryCallable = $filterDefinition['queryCallable'];
115        }
116
117        $this->activeValue = $filterDefinition['activeValue'] ?? true;
118    }
119
120    /**
121     * Get the default value
122     *
123     * @param bool $structuredUI Are we currently showing the structured UI
124     * @return bool|null Default value
125     */
126    public function getDefault( $structuredUI = false ) {
127        return $this->isReplacedInStructuredUi && $structuredUI ?
128            !$this->activeValue :
129            $this->defaultValue;
130    }
131
132    /**
133     * Sets default.  It must be a boolean.
134     *
135     * It will be coerced to boolean.
136     *
137     * @param bool $defaultValue
138     */
139    public function setDefault( $defaultValue ) {
140        $this->defaultValue = (bool)$defaultValue;
141    }
142
143    /**
144     * @return string Main i18n key for unstructured UI
145     */
146    public function getShowHide() {
147        return $this->showHide;
148    }
149
150    /**
151     * @inheritDoc
152     */
153    public function displaysOnUnstructuredUi() {
154        return (bool)$this->showHide;
155    }
156
157    /**
158     * @inheritDoc
159     */
160    public function isFeatureAvailableOnStructuredUi() {
161        return $this->isReplacedInStructuredUi ||
162            parent::isFeatureAvailableOnStructuredUi();
163    }
164
165    /**
166     * Modifies the query to include the filter.  This is only called if the filter is
167     * in effect (taking into account the default).
168     *
169     * @param IReadableDatabase $dbr Database, for addQuotes, makeList, and similar
170     * @param ChangesListSpecialPage $specialPage Current special page
171     * @param array &$tables Array of tables; see IDatabase::select $table
172     * @param array &$fields Array of fields; see IDatabase::select $vars
173     * @param array &$conds Array of conditions; see IDatabase::select $conds
174     * @param array &$query_options Array of query options; see IDatabase::select $options
175     * @param array &$join_conds Array of join conditions; see IDatabase::select $join_conds
176     */
177    public function modifyQuery( IReadableDatabase $dbr, ChangesListSpecialPage $specialPage,
178        &$tables, &$fields, &$conds, &$query_options, &$join_conds
179    ) {
180        if ( $this->queryCallable === null ) {
181            return;
182        }
183
184        ( $this->queryCallable )(
185            get_class( $specialPage ),
186            $specialPage->getContext(),
187            $dbr,
188            $tables,
189            $fields,
190            $conds,
191            $query_options,
192            $join_conds
193        );
194    }
195
196    /**
197     * @inheritDoc
198     */
199    public function getJsData() {
200        $output = parent::getJsData();
201
202        $output['default'] = $this->defaultValue;
203
204        return $output;
205    }
206
207    /**
208     * @inheritDoc
209     */
210    public function isSelected( FormOptions $opts ) {
211        return !$opts[ $this->getName() ] &&
212            array_filter(
213                $this->getSiblings(),
214                static function ( ChangesListBooleanFilter $sibling ) use ( $opts ) {
215                    return $opts[ $sibling->getName() ];
216                }
217            );
218    }
219
220    /**
221     * @inheritDoc
222     */
223    public function isActive( FormOptions $opts, $isStructuredUI ) {
224        if ( $this->isReplacedInStructuredUi && $isStructuredUI ) {
225            return false;
226        }
227
228        return $opts[ $this->getName() ] === $this->activeValue;
229    }
230}
231
232/** @deprecated class alias since 1.44 */
233class_alias( ChangesListBooleanFilter::class, 'ChangesListBooleanFilter' );