MediaWiki REL1_35
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
109
110 private const RESERVED_NAME_CHAR = '_';
111
144 public function __construct( array $filterDefinition ) {
145 if ( isset( $filterDefinition['group'] ) ) {
146 $this->group = $filterDefinition['group'];
147 } else {
148 throw new MWException( 'You must use \'group\' to specify the ' .
149 'ChangesListFilterGroup this filter belongs to' );
150 }
151
152 if ( strpos( $filterDefinition['name'], self::RESERVED_NAME_CHAR ) !== false ) {
153 throw new MWException( 'Filter names may not contain \'' .
154 self::RESERVED_NAME_CHAR .
155 '\'. Use the naming convention: \'lowercase\''
156 );
157 }
158
159 if ( $this->group->getFilter( $filterDefinition['name'] ) ) {
160 throw new MWException( 'Two filters in a group cannot have the ' .
161 "same name: '{$filterDefinition['name']}'" );
162 }
163
164 $this->name = $filterDefinition['name'];
165
166 if ( isset( $filterDefinition['cssClassSuffix'] ) ) {
167 $this->cssClassSuffix = $filterDefinition['cssClassSuffix'];
168 $this->isRowApplicableCallable = $filterDefinition['isRowApplicableCallable'];
169 }
170
171 if ( isset( $filterDefinition['label'] ) ) {
172 $this->label = $filterDefinition['label'];
173 $this->description = $filterDefinition['description'];
174 }
175
176 $this->priority = $filterDefinition['priority'];
177
178 $this->group->registerFilter( $this );
179 }
180
195 public function conflictsWith( $other, $globalKey, $forwardKey, $backwardKey ) {
196 if ( $globalKey === null || $forwardKey === null || $backwardKey === null ) {
197 throw new MWException( 'All messages must be specified' );
198 }
199
201 $other,
202 $globalKey,
203 $forwardKey
204 );
205
206 $other->setUnidirectionalConflict(
207 $this,
208 $globalKey,
209 $backwardKey
210 );
211 }
212
224 public function setUnidirectionalConflict( $other, $globalDescription, $contextDescription ) {
225 if ( $other instanceof ChangesListFilterGroup ) {
226 $this->conflictingGroups[] = [
227 'group' => $other->getName(),
228 'groupObject' => $other,
229 'globalDescription' => $globalDescription,
230 'contextDescription' => $contextDescription,
231 ];
232 } elseif ( $other instanceof ChangesListFilter ) {
233 $this->conflictingFilters[] = [
234 'group' => $other->getGroup()->getName(),
235 'filter' => $other->getName(),
236 'filterObject' => $other,
237 'globalDescription' => $globalDescription,
238 'contextDescription' => $contextDescription,
239 ];
240 } else {
241 throw new MWException( 'You can only pass in a ChangesListFilterGroup or a ChangesListFilter' );
242 }
243 }
244
254 public function setAsSupersetOf( ChangesListFilter $other ) {
255 if ( $other->getGroup() !== $this->getGroup() ) {
256 throw new MWException( 'Supersets can only be defined for filters in the same group' );
257 }
258
259 $this->subsetFilters[] = [
260 // It's always the same group, but this makes the representation
261 // more consistent with conflicts.
262 'group' => $other->getGroup()->getName(),
263 'filter' => $other->getName(),
264 ];
265 }
266
270 public function getName() {
271 return $this->name;
272 }
273
277 public function getGroup() {
278 return $this->group;
279 }
280
284 public function getLabel() {
285 return $this->label;
286 }
287
291 public function getDescription() {
292 return $this->description;
293 }
294
300 abstract public function displaysOnUnstructuredUi();
301
308 public function displaysOnStructuredUi() {
309 return $this->label !== null;
310 }
311
320 return $this->displaysOnStructuredUi();
321 }
322
326 public function getPriority() {
327 return $this->priority;
328 }
329
335 protected function getCssClass() {
336 if ( $this->cssClassSuffix !== null ) {
337 return ChangesList::CSS_CLASS_PREFIX . $this->cssClassSuffix;
338 } else {
339 return null;
340 }
341 }
342
350 public function applyCssClassIfNeeded( IContextSource $ctx, RecentChange $rc, array &$classes ) {
351 if ( $this->isRowApplicableCallable === null ) {
352 return;
353 }
354
355 if ( call_user_func( $this->isRowApplicableCallable, $ctx, $rc ) ) {
356 $classes[] = $this->getCssClass();
357 }
358 }
359
367 public function getJsData() {
368 $output = [
369 'name' => $this->getName(),
370 'label' => $this->getLabel(),
371 'description' => $this->getDescription(),
372 'cssClass' => $this->getCssClass(),
373 'priority' => $this->priority,
374 'subset' => $this->subsetFilters,
375 'conflicts' => [],
376 'defaultHighlightColor' => $this->defaultHighlightColor
377 ];
378
379 $output['messageKeys'] = [
380 $this->getLabel(),
381 $this->getDescription(),
382 ];
383
384 $conflicts = array_merge(
385 $this->conflictingGroups,
386 $this->conflictingFilters
387 );
388
389 foreach ( $conflicts as $conflictInfo ) {
390 unset( $conflictInfo['filterObject'] );
391 unset( $conflictInfo['groupObject'] );
392 $output['conflicts'][] = $conflictInfo;
393 array_push(
394 $output['messageKeys'],
395 $conflictInfo['globalDescription'],
396 $conflictInfo['contextDescription']
397 );
398 }
399
400 return $output;
401 }
402
409 abstract public function isSelected( FormOptions $opts );
410
416 public function getConflictingGroups() {
417 return array_map(
418 function ( $conflictDesc ) {
419 return $conflictDesc[ 'groupObject' ];
420 },
422 );
423 }
424
430 public function getConflictingFilters() {
431 return array_map(
432 function ( $conflictDesc ) {
433 return $conflictDesc[ 'filterObject' ];
434 },
436 );
437 }
438
447 if ( $group->anySelected( $opts ) && $this->isSelected( $opts ) ) {
449 foreach ( $this->getSiblings() as $siblingFilter ) {
450 if ( $siblingFilter->isSelected( $opts ) && !$siblingFilter->hasConflictWithGroup( $group ) ) {
451 return false;
452 }
453 }
454 return true;
455 }
456 return false;
457 }
458
460 return in_array( $group, $this->getConflictingGroups() );
461 }
462
471 if ( $this->isSelected( $opts ) && $filter->isSelected( $opts ) ) {
473 foreach ( $this->getSiblings() as $siblingFilter ) {
474 if (
475 $siblingFilter->isSelected( $opts ) &&
476 !$siblingFilter->hasConflictWithFilter( $filter )
477 ) {
478 return false;
479 }
480 }
481 return true;
482 }
483 return false;
484 }
485
486 private function hasConflictWithFilter( ChangesListFilter $filter ) {
487 return in_array( $filter, $this->getConflictingFilters() );
488 }
489
495 protected function getSiblings() {
496 return array_filter(
497 $this->getGroup()->getFilters(),
498 function ( $filter ) {
499 return $filter !== $this;
500 }
501 );
502 }
503
508 $this->defaultHighlightColor = $defaultHighlightColor;
509 }
510}
Represents a filter group (used on ChangesListSpecialPage and descendants)
anySelected(FormOptions $opts)
Check if any filter in this group is selected.
Represents a filter (used on ChangesListSpecialPage and descendants)
displaysOnUnstructuredUi()
Checks whether the filter should display on the unstructured UI.
string null $cssClassSuffix
CSS class suffix used for attribution, e.g.
getConflictingFilters()
Get filters conflicting with this filter.
ChangesListFilterGroup $group
Group.
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".
array $conflictingGroups
Array of associative arrays with conflict information.
int $priority
Priority integer.
array $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.
string $label
i18n key of label for structured UI
setUnidirectionalConflict( $other, $globalDescription, $contextDescription)
Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
string $name
Filter name.
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.
__construct(array $filterDefinition)
Creates a new filter with the specified configuration, and registers it to the specified group.
hasConflictWithGroup(ChangesListFilterGroup $group)
array $subsetFilters
Array of associative arrays with subset information.
isSelected(FormOptions $opts)
Checks whether this filter is selected in the provided options.
setDefaultHighlightColor( $defaultHighlightColor)
string $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".
callable $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.
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.