MediaWiki  master
ChangesListFilter.php
Go to the documentation of this file.
1 <?php
29 abstract 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 ) {
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 
470  public function activelyInConflictWithFilter( ChangesListFilter $filter, FormOptions $opts ) {
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 }
ChangesListFilter\activelyInConflictWithGroup
activelyInConflictWithGroup(ChangesListFilterGroup $group, FormOptions $opts)
Check if the conflict with a group is currently "active".
Definition: ChangesListFilter.php:446
ChangesListFilter\isSelected
isSelected(FormOptions $opts)
Checks whether this filter is selected in the provided options.
ChangesListFilter\$subsetFilters
array $subsetFilters
Array of associative arrays with subset information.
Definition: ChangesListFilter.php:96
RecentChange
Utility class for creating new RC entries.
Definition: RecentChange.php:73
ChangesListFilter\displaysOnUnstructuredUi
displaysOnUnstructuredUi()
Checks whether the filter should display on the unstructured UI.
ChangesListFilter\setAsSupersetOf
setAsSupersetOf(ChangesListFilter $other)
Marks that the current instance is (also) a superset of the filter passed in.
Definition: ChangesListFilter.php:254
ChangesListFilter\displaysOnStructuredUi
displaysOnStructuredUi()
Checks whether the filter should display on the structured UI This refers to the exact filter.
Definition: ChangesListFilter.php:308
ChangesListFilter\getName
getName()
Definition: ChangesListFilter.php:270
ChangesListFilter\$isRowApplicableCallable
callable $isRowApplicableCallable
Callable that returns true if and only if a row is attributed to this filter.
Definition: ChangesListFilter.php:52
ChangesListFilterGroup
Represents a filter group (used on ChangesListSpecialPage and descendants)
Definition: ChangesListFilterGroup.php:37
ChangesListFilter\$cssClassSuffix
string null $cssClassSuffix
CSS class suffix used for attribution, e.g.
Definition: ChangesListFilter.php:45
ChangesListFilter\conflictsWith
conflictsWith( $other, $globalKey, $forwardKey, $backwardKey)
Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
Definition: ChangesListFilter.php:195
ChangesListFilter\hasConflictWithFilter
hasConflictWithFilter(ChangesListFilter $filter)
Definition: ChangesListFilter.php:486
ChangesListFilter\getGroup
getGroup()
Definition: ChangesListFilter.php:277
MWException
MediaWiki exception.
Definition: MWException.php:29
ChangesListFilter\hasConflictWithGroup
hasConflictWithGroup(ChangesListFilterGroup $group)
Definition: ChangesListFilter.php:459
ChangesListFilter\$label
string $label
i18n key of label for structured UI
Definition: ChangesListFilter.php:66
ChangesListFilter\$group
ChangesListFilterGroup $group
Group.
Definition: ChangesListFilter.php:59
ChangesListFilter\getSiblings
getSiblings()
Get filters in the same group.
Definition: ChangesListFilter.php:495
ChangesListFilter\getConflictingGroups
getConflictingGroups()
Get groups conflicting with this filter.
Definition: ChangesListFilter.php:416
ChangesListFilter\getJsData
getJsData()
Gets the JS data required by the front-end of the structured UI.
Definition: ChangesListFilter.php:367
ChangesListFilter\applyCssClassIfNeeded
applyCssClassIfNeeded(IContextSource $ctx, RecentChange $rc, array &$classes)
Add CSS class if needed.
Definition: ChangesListFilter.php:350
ChangesListFilter\getDescription
getDescription()
Definition: ChangesListFilter.php:291
ChangesListFilter\$priority
int $priority
Priority integer.
Definition: ChangesListFilter.php:103
ChangesList\CSS_CLASS_PREFIX
const CSS_CLASS_PREFIX
Definition: ChangesList.php:37
ChangesListFilter\isFeatureAvailableOnStructuredUi
isFeatureAvailableOnStructuredUi()
Checks whether an equivalent feature for this filter is available on the structured UI.
Definition: ChangesListFilter.php:319
ChangesListFilter\getCssClass
getCssClass()
Gets the CSS class.
Definition: ChangesListFilter.php:335
ChangesListFilter\$defaultHighlightColor
string $defaultHighlightColor
Definition: ChangesListFilter.php:108
ChangesListFilter\RESERVED_NAME_CHAR
const RESERVED_NAME_CHAR
Definition: ChangesListFilter.php:110
ChangesListFilter\setDefaultHighlightColor
setDefaultHighlightColor( $defaultHighlightColor)
Definition: ChangesListFilter.php:507
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:55
ChangesListFilterGroup\anySelected
anySelected(FormOptions $opts)
Check if any filter in this group is selected.
Definition: ChangesListFilterGroup.php:432
ChangesListFilter\getLabel
getLabel()
Definition: ChangesListFilter.php:284
ChangesListFilter\$conflictingGroups
array $conflictingGroups
Array of associative arrays with conflict information.
Definition: ChangesListFilter.php:81
ChangesListFilter\activelyInConflictWithFilter
activelyInConflictWithFilter(ChangesListFilter $filter, FormOptions $opts)
Check if the conflict with a filter is currently "active".
Definition: ChangesListFilter.php:470
ChangesListFilter\setUnidirectionalConflict
setUnidirectionalConflict( $other, $globalDescription, $contextDescription)
Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
Definition: ChangesListFilter.php:224
ChangesListFilter
Represents a filter (used on ChangesListSpecialPage and descendants)
Definition: ChangesListFilter.php:29
ChangesListFilter\$description
string $description
i18n key of description for structured UI
Definition: ChangesListFilter.php:73
ChangesListFilter\$conflictingFilters
array $conflictingFilters
Array of associative arrays with conflict information.
Definition: ChangesListFilter.php:89
ChangesListFilter\getPriority
getPriority()
Definition: ChangesListFilter.php:326
FormOptions
Helper class to keep track of options when mixing links and form elements.
Definition: FormOptions.php:35
ChangesListFilter\__construct
__construct(array $filterDefinition)
Creates a new filter with the specified configuration, and registers it to the specified group.
Definition: ChangesListFilter.php:144
ChangesListFilter\$name
string $name
Filter name.
Definition: ChangesListFilter.php:35
ChangesListFilter\getConflictingFilters
getConflictingFilters()
Get filters conflicting with this filter.
Definition: ChangesListFilter.php:430