MediaWiki REL1_34
ChangesListFilter.php
Go to the documentation of this file.
1<?php
29abstract class ChangesListFilter {
35 protected $name;
36
45 protected $cssClassSuffix;
46
53
59 protected $group;
60
66 protected $label;
67
73 protected $description;
74
81 protected $conflictingGroups = [];
82
89 protected $conflictingFilters = [];
90
96 protected $subsetFilters = [];
97
103 protected $priority;
104
110
112
142 public function __construct( array $filterDefinition ) {
143 if ( isset( $filterDefinition['group'] ) ) {
144 $this->group = $filterDefinition['group'];
145 } else {
146 throw new MWException( 'You must use \'group\' to specify the ' .
147 'ChangesListFilterGroup this filter belongs to' );
148 }
149
150 if ( strpos( $filterDefinition['name'], self::RESERVED_NAME_CHAR ) !== false ) {
151 throw new MWException( 'Filter names may not contain \'' .
152 self::RESERVED_NAME_CHAR .
153 '\'. Use the naming convention: \'lowercase\''
154 );
155 }
156
157 if ( $this->group->getFilter( $filterDefinition['name'] ) ) {
158 throw new MWException( 'Two filters in a group cannot have the ' .
159 "same name: '{$filterDefinition['name']}'" );
160 }
161
162 $this->name = $filterDefinition['name'];
163
164 if ( isset( $filterDefinition['cssClassSuffix'] ) ) {
165 $this->cssClassSuffix = $filterDefinition['cssClassSuffix'];
166 $this->isRowApplicableCallable = $filterDefinition['isRowApplicableCallable'];
167 }
168
169 if ( isset( $filterDefinition['label'] ) ) {
170 $this->label = $filterDefinition['label'];
171 $this->description = $filterDefinition['description'];
172 }
173
174 $this->priority = $filterDefinition['priority'];
175
176 $this->group->registerFilter( $this );
177 }
178
193 public function conflictsWith( $other, $globalKey, $forwardKey, $backwardKey ) {
194 if ( $globalKey === null || $forwardKey === null || $backwardKey === null ) {
195 throw new MWException( 'All messages must be specified' );
196 }
197
199 $other,
200 $globalKey,
201 $forwardKey
202 );
203
204 $other->setUnidirectionalConflict(
205 $this,
206 $globalKey,
207 $backwardKey
208 );
209 }
210
222 public function setUnidirectionalConflict( $other, $globalDescription, $contextDescription ) {
223 if ( $other instanceof ChangesListFilterGroup ) {
224 $this->conflictingGroups[] = [
225 'group' => $other->getName(),
226 'groupObject' => $other,
227 'globalDescription' => $globalDescription,
228 'contextDescription' => $contextDescription,
229 ];
230 } elseif ( $other instanceof ChangesListFilter ) {
231 $this->conflictingFilters[] = [
232 'group' => $other->getGroup()->getName(),
233 'filter' => $other->getName(),
234 'filterObject' => $other,
235 'globalDescription' => $globalDescription,
236 'contextDescription' => $contextDescription,
237 ];
238 } else {
239 throw new MWException( 'You can only pass in a ChangesListFilterGroup or a ChangesListFilter' );
240 }
241 }
242
252 public function setAsSupersetOf( ChangesListFilter $other ) {
253 if ( $other->getGroup() !== $this->getGroup() ) {
254 throw new MWException( 'Supersets can only be defined for filters in the same group' );
255 }
256
257 $this->subsetFilters[] = [
258 // It's always the same group, but this makes the representation
259 // more consistent with conflicts.
260 'group' => $other->getGroup()->getName(),
261 'filter' => $other->getName(),
262 ];
263 }
264
268 public function getName() {
269 return $this->name;
270 }
271
275 public function getGroup() {
276 return $this->group;
277 }
278
282 public function getLabel() {
283 return $this->label;
284 }
285
289 public function getDescription() {
290 return $this->description;
291 }
292
298 abstract public function displaysOnUnstructuredUi();
299
306 public function displaysOnStructuredUi() {
307 return $this->label !== null;
308 }
309
318 return $this->displaysOnStructuredUi();
319 }
320
324 public function getPriority() {
325 return $this->priority;
326 }
327
333 protected function getCssClass() {
334 if ( $this->cssClassSuffix !== null ) {
336 } else {
337 return null;
338 }
339 }
340
348 public function applyCssClassIfNeeded( IContextSource $ctx, RecentChange $rc, array &$classes ) {
349 if ( $this->isRowApplicableCallable === null ) {
350 return;
351 }
352
353 if ( call_user_func( $this->isRowApplicableCallable, $ctx, $rc ) ) {
354 $classes[] = $this->getCssClass();
355 }
356 }
357
365 public function getJsData() {
366 $output = [
367 'name' => $this->getName(),
368 'label' => $this->getLabel(),
369 'description' => $this->getDescription(),
370 'cssClass' => $this->getCssClass(),
371 'priority' => $this->priority,
372 'subset' => $this->subsetFilters,
373 'conflicts' => [],
374 'defaultHighlightColor' => $this->defaultHighlightColor
375 ];
376
377 $output['messageKeys'] = [
378 $this->getLabel(),
379 $this->getDescription(),
380 ];
381
382 $conflicts = array_merge(
383 $this->conflictingGroups,
384 $this->conflictingFilters
385 );
386
387 foreach ( $conflicts as $conflictInfo ) {
388 unset( $conflictInfo['filterObject'] );
389 unset( $conflictInfo['groupObject'] );
390 $output['conflicts'][] = $conflictInfo;
391 array_push(
392 $output['messageKeys'],
393 $conflictInfo['globalDescription'],
394 $conflictInfo['contextDescription']
395 );
396 }
397
398 return $output;
399 }
400
407 abstract public function isSelected( FormOptions $opts );
408
414 public function getConflictingGroups() {
415 return array_map(
416 function ( $conflictDesc ) {
417 return $conflictDesc[ 'groupObject' ];
418 },
420 );
421 }
422
428 public function getConflictingFilters() {
429 return array_map(
430 function ( $conflictDesc ) {
431 return $conflictDesc[ 'filterObject' ];
432 },
434 );
435 }
436
445 if ( $group->anySelected( $opts ) && $this->isSelected( $opts ) ) {
447 foreach ( $this->getSiblings() as $siblingFilter ) {
448 if ( $siblingFilter->isSelected( $opts ) && !$siblingFilter->hasConflictWithGroup( $group ) ) {
449 return false;
450 }
451 }
452 return true;
453 }
454 return false;
455 }
456
458 return in_array( $group, $this->getConflictingGroups() );
459 }
460
469 if ( $this->isSelected( $opts ) && $filter->isSelected( $opts ) ) {
471 foreach ( $this->getSiblings() as $siblingFilter ) {
472 if (
473 $siblingFilter->isSelected( $opts ) &&
474 !$siblingFilter->hasConflictWithFilter( $filter )
475 ) {
476 return false;
477 }
478 }
479 return true;
480 }
481 return false;
482 }
483
485 return in_array( $filter, $this->getConflictingFilters() );
486 }
487
493 protected function getSiblings() {
494 return array_filter(
495 $this->getGroup()->getFilters(),
496 function ( $filter ) {
497 return $filter !== $this;
498 }
499 );
500 }
501
506 $this->defaultHighlightColor = $defaultHighlightColor;
507 }
508}
Represents a filter group (used on ChangesListSpecialPage and descendants)
Represents a filter (used on ChangesListSpecialPage and descendants)
displaysOnUnstructuredUi()
Checks whether the filter should display on the unstructured UI.
$cssClassSuffix
CSS class suffix used for attribution, e.g.
getConflictingFilters()
Get filters conflicting with this filter.
getJsData()
Gets the JS data required by the front-end of the structured UI.
activelyInConflictWithGroup(ChangesListFilterGroup $group, FormOptions $opts)
Check if the conflict with a group is currently "active".
$conflictingGroups
Array of associative arrays with conflict information.
$conflictingFilters
Array of associative arrays with conflict information.
displaysOnStructuredUi()
Checks whether the filter should display on the structured UI This refers to the exact filter.
$label
i18n key of label for structured UI
setUnidirectionalConflict( $other, $globalDescription, $contextDescription)
Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
isFeatureAvailableOnStructuredUi()
Checks whether an equivalent feature for this filter is available on the structured UI.
applyCssClassIfNeeded(IContextSource $ctx, RecentChange $rc, array &$classes)
Add CSS class if needed.
setAsSupersetOf(ChangesListFilter $other)
Marks that the current instance is (also) a superset of the filter passed in.
$priority
Priority integer.
__construct(array $filterDefinition)
Creates a new filter with the specified configuration, and registers it to the specified group.
hasConflictWithGroup(ChangesListFilterGroup $group)
$subsetFilters
Array of associative arrays with subset information.
isSelected(FormOptions $opts)
Checks whether this filter is selected in the provided options.
setDefaultHighlightColor( $defaultHighlightColor)
$description
i18n key of description for structured UI
getConflictingGroups()
Get groups conflicting with this filter.
hasConflictWithFilter(ChangesListFilter $filter)
getCssClass()
Gets the CSS class.
getSiblings()
Get filters in the same group.
activelyInConflictWithFilter(ChangesListFilter $filter, FormOptions $opts)
Check if the conflict with a filter is currently "active".
$isRowApplicableCallable
Callable that returns true if and only if a row is attributed to this filter.
conflictsWith( $other, $globalKey, $forwardKey, $backwardKey)
Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
const CSS_CLASS_PREFIX
Helper class to keep track of options when mixing links and form elements.
MediaWiki exception.
Utility class for creating new RC entries.
Interface for objects which can provide a MediaWiki context on request.
$filter