Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
93.18% |
41 / 44 |
|
62.50% |
5 / 8 |
CRAP | |
0.00% |
0 / 1 |
ChangesListStringOptionsFilterGroup | |
93.18% |
41 / 44 |
|
62.50% |
5 / 8 |
15.07 | |
0.00% |
0 / 1 |
__construct | |
87.50% |
7 / 8 |
|
0.00% |
0 / 1 |
3.02 | |||
setDefault | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getDefault | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
createFilter | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
registerFilter | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
modifyQuery | |
96.30% |
26 / 27 |
|
0.00% |
0 / 1 |
5 | |||
getJsData | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
1 | |||
addOptions | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | /** |
3 | * Represents a filter group (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 | |
24 | use MediaWiki\Html\FormOptions; |
25 | use MediaWiki\SpecialPage\ChangesListSpecialPage; |
26 | use Wikimedia\Rdbms\IReadableDatabase; |
27 | |
28 | /** |
29 | * Represents a filter group with multiple string options. They are passed to the server as |
30 | * a single form parameter separated by a delimiter. The parameter name is the |
31 | * group name. E.g. groupname=opt1;opt2 . |
32 | * |
33 | * If all options are selected they are replaced by the term "all". |
34 | * |
35 | * There is also a single DB query modification for the whole group. |
36 | * |
37 | * @since 1.29 |
38 | */ |
39 | class ChangesListStringOptionsFilterGroup extends ChangesListFilterGroup { |
40 | /** |
41 | * Type marker, used by JavaScript |
42 | */ |
43 | public const TYPE = 'string_options'; |
44 | |
45 | /** |
46 | * Delimiter |
47 | */ |
48 | public const SEPARATOR = ';'; |
49 | |
50 | /** |
51 | * Signifies that all options in the group are selected. |
52 | */ |
53 | public const ALL = 'all'; |
54 | |
55 | /** |
56 | * Signifies that no options in the group are selected, meaning the group has no effect. |
57 | * |
58 | * For full-coverage groups, this is the same as ALL if all filters are allowed. |
59 | * For others, it is not. |
60 | */ |
61 | public const NONE = ''; |
62 | |
63 | /** |
64 | * Default parameter value |
65 | * |
66 | * @var string |
67 | */ |
68 | protected $defaultValue; |
69 | |
70 | /** |
71 | * Callable used to do the actual query modification; see constructor |
72 | * |
73 | * @var callable |
74 | */ |
75 | protected $queryCallable; |
76 | |
77 | /** |
78 | * Create a new filter group with the specified configuration |
79 | * |
80 | * @param array $groupDefinition Configuration of group |
81 | * * $groupDefinition['name'] string Group name |
82 | * * $groupDefinition['title'] string i18n key for title (optional, can be omitted |
83 | * only if none of the filters in the group display in the structured UI) |
84 | * * $groupDefinition['priority'] int Priority integer. Higher means higher in the |
85 | * group list. |
86 | * * $groupDefinition['filters'] array Numeric array of filter definitions, each of which |
87 | * is an associative array to be passed to the filter constructor. However, |
88 | * 'priority' is optional for the filters. Any filter that has priority unset |
89 | * will be put to the bottom, in the order given. |
90 | * * $groupDefinition['default'] string Default for group. |
91 | * * $groupDefinition['isFullCoverage'] bool Whether the group is full coverage; |
92 | * if true, this means that checking every item in the group means no |
93 | * changes list entries are filtered out. |
94 | * * $groupDefinition['queryCallable'] callable Callable accepting parameters: |
95 | * * string $specialPageClassName Class name of current special page |
96 | * * IContextSource $context Context, for e.g. user |
97 | * * IDatabase $dbr Database, for addQuotes, makeList, and similar |
98 | * * array &$tables Array of tables; see IDatabase::select $table |
99 | * * array &$fields Array of fields; see IDatabase::select $vars |
100 | * * array &$conds Array of conditions; see IDatabase::select $conds |
101 | * * array &$query_options Array of query options; see IDatabase::select $options |
102 | * * array &$join_conds Array of join conditions; see IDatabase::select $join_conds |
103 | * * array $selectedValues The allowed and requested values, lower-cased and sorted |
104 | * * $groupDefinition['whatsThisHeader'] string i18n key for header of "What's |
105 | * This" popup (optional). |
106 | * * $groupDefinition['whatsThisBody'] string i18n key for body of "What's This" |
107 | * popup (optional). |
108 | * * $groupDefinition['whatsThisUrl'] string URL for main link of "What's This" |
109 | * popup (optional). |
110 | * * $groupDefinition['whatsThisLinkText'] string i18n key of text for main link of |
111 | * "What's This" popup (optional). |
112 | */ |
113 | public function __construct( array $groupDefinition ) { |
114 | if ( !isset( $groupDefinition['isFullCoverage'] ) ) { |
115 | throw new InvalidArgumentException( 'You must specify isFullCoverage' ); |
116 | } |
117 | |
118 | $groupDefinition['type'] = self::TYPE; |
119 | |
120 | parent::__construct( $groupDefinition ); |
121 | |
122 | $this->queryCallable = $groupDefinition['queryCallable']; |
123 | |
124 | if ( isset( $groupDefinition['default'] ) ) { |
125 | $this->setDefault( $groupDefinition['default'] ); |
126 | } else { |
127 | throw new InvalidArgumentException( 'You must specify a default' ); |
128 | } |
129 | } |
130 | |
131 | /** |
132 | * Sets default of filter group. |
133 | * |
134 | * @param string $defaultValue |
135 | */ |
136 | public function setDefault( $defaultValue ) { |
137 | $this->defaultValue = $defaultValue; |
138 | } |
139 | |
140 | /** |
141 | * Gets default of filter group |
142 | * |
143 | * @return string |
144 | */ |
145 | public function getDefault() { |
146 | return $this->defaultValue; |
147 | } |
148 | |
149 | /** |
150 | * @inheritDoc |
151 | */ |
152 | protected function createFilter( array $filterDefinition ) { |
153 | return new ChangesListStringOptionsFilter( $filterDefinition ); |
154 | } |
155 | |
156 | /** |
157 | * Registers a filter in this group |
158 | * |
159 | * @param ChangesListStringOptionsFilter $filter |
160 | * @suppress PhanParamSignaturePHPDocMismatchHasParamType,PhanParamSignatureMismatch |
161 | */ |
162 | public function registerFilter( ChangesListStringOptionsFilter $filter ) { |
163 | $this->filters[$filter->getName()] = $filter; |
164 | } |
165 | |
166 | /** |
167 | * @inheritDoc |
168 | */ |
169 | public function modifyQuery( IReadableDatabase $dbr, ChangesListSpecialPage $specialPage, |
170 | &$tables, &$fields, &$conds, &$query_options, &$join_conds, |
171 | FormOptions $opts, $isStructuredFiltersEnabled |
172 | ) { |
173 | // STRING_OPTIONS filter groups are exclusively active on Structured UI |
174 | if ( !$isStructuredFiltersEnabled ) { |
175 | return; |
176 | } |
177 | |
178 | $value = $opts[ $this->getName() ]; |
179 | $allowedFilterNames = []; |
180 | foreach ( $this->filters as $filter ) { |
181 | $allowedFilterNames[] = $filter->getName(); |
182 | } |
183 | |
184 | if ( $value === self::ALL ) { |
185 | $selectedValues = $allowedFilterNames; |
186 | } else { |
187 | $selectedValues = explode( self::SEPARATOR, strtolower( $value ) ); |
188 | |
189 | // remove values that are not recognized or not currently allowed |
190 | $selectedValues = array_intersect( |
191 | $selectedValues, |
192 | $allowedFilterNames |
193 | ); |
194 | } |
195 | |
196 | // If there are now no values, because all are disallowed or invalid (also, |
197 | // the user may not have selected any), this is a no-op. |
198 | |
199 | // If everything is unchecked, the group always has no effect, regardless |
200 | // of full-coverage. |
201 | if ( count( $selectedValues ) === 0 ) { |
202 | return; |
203 | } |
204 | |
205 | sort( $selectedValues ); |
206 | |
207 | ( $this->queryCallable )( |
208 | get_class( $specialPage ), |
209 | $specialPage->getContext(), |
210 | $dbr, |
211 | $tables, |
212 | $fields, |
213 | $conds, |
214 | $query_options, |
215 | $join_conds, |
216 | $selectedValues |
217 | ); |
218 | } |
219 | |
220 | /** |
221 | * @inheritDoc |
222 | */ |
223 | public function getJsData() { |
224 | $output = parent::getJsData(); |
225 | |
226 | $output['separator'] = self::SEPARATOR; |
227 | $output['default'] = $this->getDefault(); |
228 | |
229 | return $output; |
230 | } |
231 | |
232 | /** |
233 | * @inheritDoc |
234 | */ |
235 | public function addOptions( FormOptions $opts, $allowDefaults, $isStructuredFiltersEnabled ) { |
236 | $opts->add( $this->getName(), $allowDefaults ? $this->getDefault() : '' ); |
237 | } |
238 | } |