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