Localisation updates from https://translatewiki.net.
[lhc/web/wiklou.git] / includes / changes / ChangesListStringOptionsFilterGroup.php
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 * @license GPL 2+
22 * @author Matthew Flaschen
23 */
24
25 use Wikimedia\Rdbms\IDatabase;
26
27 /**
28 * Represents a filter group with multiple string options. They are passed to the server as
29 * a single form parameter separated by a delimiter. The parameter name is the
30 * group name. E.g. groupname=opt1;opt2 .
31 *
32 * If all options are selected they are replaced by the term "all".
33 *
34 * There is also a single DB query modification for the whole group.
35 *
36 * @since 1.29
37 */
38
39 class ChangesListStringOptionsFilterGroup extends ChangesListFilterGroup {
40 /**
41 * Type marker, used by JavaScript
42 */
43 const TYPE = 'string_options';
44
45 /**
46 * Delimiter
47 */
48 const SEPARATOR = ';';
49
50 /**
51 * Signifies that all options in the group are selected.
52 */
53 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 const NONE = '';
62
63 /**
64 * Defaul parameter value
65 *
66 * @var string $defaultValue
67 */
68 protected $defaultValue;
69
70 /**
71 * Callable used to do the actual query modification; see constructor
72 *
73 * @var callable $queryCallable
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 MWException( '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 MWException( 'You must specify a default' );
128 }
129 }
130
131 /**
132 * @inheritDoc
133 */
134 public function isPerGroupRequestParameter() {
135 return true;
136 }
137
138 /**
139 * Sets default of filter group.
140 *
141 * @param string $defaultValue
142 */
143 public function setDefault( $defaultValue ) {
144 $this->defaultValue = $defaultValue;
145 }
146
147 /**
148 * Gets default of filter group
149 *
150 * @return string $defaultValue
151 */
152 public function getDefault() {
153 return $this->defaultValue;
154 }
155
156 /**
157 * @inheritDoc
158 */
159 protected function createFilter( array $filterDefinition ) {
160 return new ChangesListStringOptionsFilter( $filterDefinition );
161 }
162
163 /**
164 * Registers a filter in this group
165 *
166 * @param ChangesListStringOptionsFilter $filter ChangesListStringOptionsFilter
167 */
168 public function registerFilter( ChangesListStringOptionsFilter $filter ) {
169 $this->filters[$filter->getName()] = $filter;
170 }
171
172 /**
173 * Modifies the query to include the filter group.
174 *
175 * The modification is only done if the filter group is in effect. This means that
176 * one or more valid and allowed filters were selected.
177 *
178 * @param IDatabase $dbr Database, for addQuotes, makeList, and similar
179 * @param ChangesListSpecialPage $specialPage Current special page
180 * @param array &$tables Array of tables; see IDatabase::select $table
181 * @param array &$fields Array of fields; see IDatabase::select $vars
182 * @param array &$conds Array of conditions; see IDatabase::select $conds
183 * @param array &$query_options Array of query options; see IDatabase::select $options
184 * @param array &$join_conds Array of join conditions; see IDatabase::select $join_conds
185 * @param string $value URL parameter value
186 */
187 public function modifyQuery( IDatabase $dbr, ChangesListSpecialPage $specialPage,
188 &$tables, &$fields, &$conds, &$query_options, &$join_conds, $value
189 ) {
190 $allowedFilterNames = [];
191 foreach ( $this->filters as $filter ) {
192 $allowedFilterNames[] = $filter->getName();
193 }
194
195 if ( $value === self::ALL ) {
196 $selectedValues = $allowedFilterNames;
197 } else {
198 $selectedValues = explode( self::SEPARATOR, strtolower( $value ) );
199
200 // remove values that are not recognized or not currently allowed
201 $selectedValues = array_intersect(
202 $selectedValues,
203 $allowedFilterNames
204 );
205 }
206
207 // If there are now no values, because all are disallowed or invalid (also,
208 // the user may not have selected any), this is a no-op.
209
210 // If everything is unchecked, the group always has no effect, regardless
211 // of full-coverage.
212 if ( count( $selectedValues ) === 0 ) {
213 return;
214 }
215
216 sort( $selectedValues );
217
218 call_user_func_array(
219 $this->queryCallable,
220 [
221 get_class( $specialPage ),
222 $specialPage->getContext(),
223 $dbr,
224 &$tables,
225 &$fields,
226 &$conds,
227 &$query_options,
228 &$join_conds,
229 $selectedValues
230 ]
231 );
232 }
233
234 /**
235 * @inheritDoc
236 */
237 public function getJsData() {
238 $output = parent::getJsData();
239
240 $output['separator'] = self::SEPARATOR;
241 $output['default'] = $this->getDefault();
242
243 return $output;
244 }
245
246 /**
247 * Check if this filter group is currently active
248 *
249 * @param {boolean} $isStructuredUI Is structured filters UI current enabled
250 */
251 public function isActive( $isStructuredUI ) {
252 // STRING_OPTIONS filter groups are exclusively active on Structured UI
253 return $isStructuredUI;
254 }
255 }