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