Merge "Add support for PHP7 random_bytes in favor of mcrypt_create_iv"
[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 /**
26 * Represents a filter group with multiple string options. They are passed to the server as
27 * a single form parameter separated by a delimiter. The parameter name is the
28 * group name. E.g. groupname=opt1;opt2 .
29 *
30 * If all options are selected they are replaced by the term "all".
31 *
32 * There is also a single DB query modification for the whole group.
33 *
34 * @since 1.29
35 */
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 * Group name; used as form parameter.
63 *
64 * @var string $name
65 */
66
67 /**
68 * Defaul parameter value
69 *
70 * @var string $defaultValue
71 */
72 protected $defaultValue;
73
74 /**
75 * Callable used to do the actual query modification; see constructor
76 *
77 * @var callable $queryCallable
78 */
79 protected $queryCallable;
80
81 /**
82 * Create a new filter group with the specified configuration
83 *
84 * @param array $groupDefinition Configuration of group
85 * * $groupDefinition['name'] string Group name
86 * * $groupDefinition['title'] string i18n key for title (optional, can be omitted
87 * * only if none of the filters in the group display in the structured UI)
88 * * $groupDefinition['priority'] int Priority integer. Higher means higher in the
89 * * group list.
90 * * $groupDefinition['filters'] array Numeric array of filter definitions, each of which
91 * * is an associative array to be passed to the filter constructor. However,
92 * * 'priority' is optional for the filters. Any filter that has priority unset
93 * * will be put to the bottom, in the order given.
94 * * $groupDefinition['default'] string Default for group.
95 * * $groupDefinition['isFullCoverage'] bool Whether the group is full coverage;
96 * * if true, this means that checking every item in the group means no
97 * * changes list entries are filtered out.
98 * * $groupDefinition['queryCallable'] callable Callable accepting parameters:
99 * * string $specialPageClassName Class name of current special page
100 * * IContextSource $context Context, for e.g. user
101 * * IDatabase $dbr Database, for addQuotes, makeList, and similar
102 * * array &$tables Array of tables; see IDatabase::select $table
103 * * array &$fields Array of fields; see IDatabase::select $vars
104 * * array &$conds Array of conditions; see IDatabase::select $conds
105 * * array &$query_options Array of query options; see IDatabase::select $options
106 * * array &$join_conds Array of join conditions; see IDatabase::select $join_conds
107 * * array $selectedValues The allowed and requested values, lower-cased and sorted
108 */
109 public function __construct( array $groupDefinition ) {
110 if ( !isset( $groupDefinition['isFullCoverage'] ) ) {
111 throw new MWException( 'You must specify isFullCoverage' );
112 }
113
114 $groupDefinition['type'] = self::TYPE;
115
116 parent::__construct( $groupDefinition );
117
118 $this->queryCallable = $groupDefinition['queryCallable'];
119
120 if ( isset( $groupDefinition['default'] ) ) {
121 $this->setDefault( $groupDefinition['default'] );
122 } else {
123 throw new MWException( 'You must specify a default' );
124 }
125 }
126
127 /**
128 * @inheritdoc
129 */
130 public function isPerGroupRequestParameter() {
131 return true;
132 }
133
134 /**
135 * Sets default of filter group.
136 *
137 * @param string $defaultValue
138 */
139 public function setDefault( $defaultValue ) {
140 $this->defaultValue = $defaultValue;
141 }
142
143 /**
144 * Gets default of filter group
145 *
146 * @return string $defaultValue
147 */
148 public function getDefault() {
149 return $this->defaultValue;
150 }
151
152 /**
153 * @inheritdoc
154 */
155 protected function createFilter( array $filterDefinition ) {
156 return new ChangesListStringOptionsFilter( $filterDefinition );
157 }
158
159 /**
160 * Registers a filter in this group
161 *
162 * @param ChangesListStringOptionsFilter $filter ChangesListStringOptionsFilter
163 */
164 public function registerFilter( ChangesListStringOptionsFilter $filter ) {
165 $this->filters[$filter->getName()] = $filter;
166 }
167
168 /**
169 * Modifies the query to include the filter group.
170 *
171 * The modification is only done if the filter group is in effect. This means that
172 * one or more valid and allowed filters were selected.
173 *
174 * @param IDatabase $dbr Database, for addQuotes, makeList, and similar
175 * @param ChangesListSpecialPage $specialPage Current special page
176 * @param array &$tables Array of tables; see IDatabase::select $table
177 * @param array &$fields Array of fields; see IDatabase::select $vars
178 * @param array &$conds Array of conditions; see IDatabase::select $conds
179 * @param array &$query_options Array of query options; see IDatabase::select $options
180 * @param array &$join_conds Array of join conditions; see IDatabase::select $join_conds
181 * @param string $value URL parameter value
182 */
183 public function modifyQuery( IDatabase $dbr, ChangesListSpecialPage $specialPage,
184 &$tables, &$fields, &$conds, &$query_options, &$join_conds, $value ) {
185
186 $allowedFilterNames = [];
187 foreach ( $this->filters as $filter ) {
188 if ( $filter->isAllowed( $specialPage ) ) {
189 $allowedFilterNames[] = $filter->getName();
190 }
191 }
192
193 if ( $value === self::ALL ) {
194 $selectedValues = $allowedFilterNames;
195 } else {
196 $selectedValues = explode( self::SEPARATOR, strtolower( $value ) );
197
198 // remove values that are not recognized or not currently allowed
199 $selectedValues = array_intersect(
200 $selectedValues,
201 $allowedFilterNames
202 );
203 }
204
205 // If there are now no values, because all are disallowed or invalid (also,
206 // the user may not have selected any), this is a no-op.
207
208 // If everything is unchecked, the group always has no effect, regardless
209 // of full-coverage.
210 if ( count( $selectedValues ) === 0 ) {
211 return;
212 }
213
214 sort( $selectedValues );
215
216 call_user_func_array(
217 $this->queryCallable,
218 [
219 get_class( $specialPage ),
220 $specialPage->getContext(),
221 $dbr,
222 &$tables,
223 &$fields,
224 &$conds,
225 &$query_options,
226 &$join_conds,
227 $selectedValues
228 ]
229 );
230 }
231
232 /**
233 * @inheritdoc
234 */
235 public function getJsData( ChangesListSpecialPage $specialPage ) {
236 $output = parent::getJsData( $specialPage );
237
238 $output['separator'] = self::SEPARATOR;
239 $output['default'] = $this->getDefault();
240
241 return $output;
242 }
243 }