Merge "Warn if stateful ParserOutput transforms are used"
[lhc/web/wiklou.git] / includes / changes / ChangesListBooleanFilter.php
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
24 use Wikimedia\Rdbms\IDatabase;
25
26 /**
27 * Represents a hide-based boolean filter (used on ChangesListSpecialPage and descendants)
28 *
29 * @since 1.29
30 */
31 class ChangesListBooleanFilter extends ChangesListFilter {
32 // This can sometimes be different on Special:RecentChanges
33 // and Special:Watchlist, due to the double-legacy hooks
34 // (SpecialRecentChangesFilters and SpecialWatchlistFilters)
35
36 // but there will be separate sets of ChangesListFilterGroup and ChangesListFilter instances
37 // for those pages (it should work even if they're both loaded
38 // at once, but that can't happen).
39 /**
40 * Main unstructured UI i18n key
41 *
42 * @var string $showHide
43 */
44 protected $showHide;
45
46 /**
47 * Whether there is a feature designed to replace this filter available on the
48 * structured UI
49 *
50 * @var bool $isReplacedInStructuredUi
51 */
52 protected $isReplacedInStructuredUi;
53
54 /**
55 * Default
56 *
57 * @var bool $defaultValue
58 */
59 protected $defaultValue;
60
61 /**
62 * Callable used to do the actual query modification; see constructor
63 *
64 * @var callable $queryCallable
65 */
66 protected $queryCallable;
67
68 /**
69 * Value that defined when this filter is considered active
70 *
71 * @var bool $activeValue
72 */
73 protected $activeValue;
74
75 /**
76 * Create a new filter with the specified configuration.
77 *
78 * It infers which UI (it can be either or both) to display the filter on based on
79 * which messages are provided.
80 *
81 * If 'label' is provided, it will be displayed on the structured UI. If
82 * 'showHide' is provided, it will be displayed on the unstructured UI. Thus,
83 * 'label', 'description', and 'showHide' are optional depending on which UI
84 * it's for.
85 *
86 * @param array $filterDefinition ChangesListFilter definition
87 * * $filterDefinition['name'] string Name. Used as URL parameter.
88 * * $filterDefinition['group'] ChangesListFilterGroup Group. Filter group this
89 * belongs to.
90 * * $filterDefinition['label'] string i18n key of label for structured UI.
91 * * $filterDefinition['description'] string i18n key of description for structured
92 * UI.
93 * * $filterDefinition['showHide'] string Main i18n key used for unstructured UI.
94 * * $filterDefinition['isReplacedInStructuredUi'] bool Whether there is an
95 * equivalent feature available in the structured UI; this is optional, defaulting
96 * to true. It does not need to be set if the exact same filter is simply visible
97 * on both.
98 * * $filterDefinition['default'] bool Default
99 * * $filterDefinition['activeValue'] bool This filter is considered active when
100 * its value is equal to its activeValue. Default is true.
101 * * $filterDefinition['priority'] int Priority integer. Higher value means higher
102 * up in the group's filter list.
103 * * $filterDefinition['queryCallable'] callable Callable accepting parameters, used
104 * to implement filter's DB query modification. Required, except for legacy
105 * filters that still use the query hooks directly. Callback parameters:
106 * * string $specialPageClassName Class name of current special page
107 * * IContextSource $context Context, for e.g. user
108 * * IDatabase $dbr Database, for addQuotes, makeList, and similar
109 * * array &$tables Array of tables; see IDatabase::select $table
110 * * array &$fields Array of fields; see IDatabase::select $vars
111 * * array &$conds Array of conditions; see IDatabase::select $conds
112 * * array &$query_options Array of query options; see IDatabase::select $options
113 * * array &$join_conds Array of join conditions; see IDatabase::select $join_conds
114 */
115 public function __construct( $filterDefinition ) {
116 parent::__construct( $filterDefinition );
117
118 if ( isset( $filterDefinition['showHide'] ) ) {
119 $this->showHide = $filterDefinition['showHide'];
120 }
121
122 if ( isset( $filterDefinition['isReplacedInStructuredUi'] ) ) {
123 $this->isReplacedInStructuredUi = $filterDefinition['isReplacedInStructuredUi'];
124 } else {
125 $this->isReplacedInStructuredUi = false;
126 }
127
128 if ( isset( $filterDefinition['default'] ) ) {
129 $this->setDefault( $filterDefinition['default'] );
130 } else {
131 throw new MWException( 'You must set a default' );
132 }
133
134 if ( isset( $filterDefinition['queryCallable'] ) ) {
135 $this->queryCallable = $filterDefinition['queryCallable'];
136 }
137
138 if ( isset( $filterDefinition['activeValue'] ) ) {
139 $this->activeValue = $filterDefinition['activeValue'];
140 } else {
141 $this->activeValue = true;
142 }
143 }
144
145 /**
146 * Get the default value
147 *
148 * @param bool $structuredUI Are we currently showing the structured UI
149 * @return bool|null Default value
150 */
151 public function getDefault( $structuredUI = false ) {
152 return $this->isReplacedInStructuredUi && $structuredUI ?
153 !$this->activeValue :
154 $this->defaultValue;
155 }
156
157 /**
158 * Sets default. It must be a boolean.
159 *
160 * It will be coerced to boolean.
161 *
162 * @param bool $defaultValue
163 */
164 public function setDefault( $defaultValue ) {
165 $this->defaultValue = (bool)$defaultValue;
166 }
167
168 /**
169 * @return string Main i18n key for unstructured UI
170 */
171 public function getShowHide() {
172 return $this->showHide;
173 }
174
175 /**
176 * @inheritDoc
177 */
178 public function displaysOnUnstructuredUi() {
179 return !!$this->showHide;
180 }
181
182 /**
183 * @inheritDoc
184 */
185 public function isFeatureAvailableOnStructuredUi() {
186 return $this->isReplacedInStructuredUi ||
187 parent::isFeatureAvailableOnStructuredUi();
188 }
189
190 /**
191 * Modifies the query to include the filter. This is only called if the filter is
192 * in effect (taking into account the default).
193 *
194 * @param IDatabase $dbr Database, for addQuotes, makeList, and similar
195 * @param ChangesListSpecialPage $specialPage Current special page
196 * @param array &$tables Array of tables; see IDatabase::select $table
197 * @param array &$fields Array of fields; see IDatabase::select $vars
198 * @param array &$conds Array of conditions; see IDatabase::select $conds
199 * @param array &$query_options Array of query options; see IDatabase::select $options
200 * @param array &$join_conds Array of join conditions; see IDatabase::select $join_conds
201 */
202 public function modifyQuery( IDatabase $dbr, ChangesListSpecialPage $specialPage,
203 &$tables, &$fields, &$conds, &$query_options, &$join_conds
204 ) {
205 if ( $this->queryCallable === null ) {
206 return;
207 }
208
209 call_user_func_array(
210 $this->queryCallable,
211 [
212 get_class( $specialPage ),
213 $specialPage->getContext(),
214 $dbr,
215 &$tables,
216 &$fields,
217 &$conds,
218 &$query_options,
219 &$join_conds
220 ]
221 );
222 }
223
224 /**
225 * @inheritDoc
226 */
227 public function getJsData() {
228 $output = parent::getJsData();
229
230 $output['default'] = $this->defaultValue;
231
232 return $output;
233 }
234
235 /**
236 * @inheritDoc
237 */
238 public function isSelected( FormOptions $opts ) {
239 return !$opts[ $this->getName() ] &&
240 array_filter(
241 $this->getSiblings(),
242 function ( ChangesListBooleanFilter $sibling ) use ( $opts ) {
243 return $opts[ $sibling->getName() ];
244 }
245 );
246 }
247
248 /**
249 * @param FormOptions $opts Query parameters merged with defaults
250 * @param bool $isStructuredUI Whether the structured UI is currently enabled
251 * @return bool Whether this filter should be considered active
252 */
253 public function isActive( FormOptions $opts, $isStructuredUI ) {
254 if ( $this->isReplacedInStructuredUi && $isStructuredUI ) {
255 return false;
256 }
257
258 return $opts[ $this->getName() ] === $this->activeValue;
259 }
260 }