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 
110 
111  const RESERVED_NAME_CHAR = '_';
112 
145  public function __construct( array $filterDefinition ) {
146  if ( isset( $filterDefinition['group'] ) ) {
147  $this->group = $filterDefinition['group'];
148  } else {
149  throw new MWException( 'You must use \'group\' to specify the ' .
150  'ChangesListFilterGroup this filter belongs to' );
151  }
152 
153  if ( strpos( $filterDefinition['name'], self::RESERVED_NAME_CHAR ) !== false ) {
154  throw new MWException( 'Filter names may not contain \'' .
155  self::RESERVED_NAME_CHAR .
156  '\'. Use the naming convention: \'lowercase\''
157  );
158  }
159 
160  if ( $this->group->getFilter( $filterDefinition['name'] ) ) {
161  throw new MWException( 'Two filters in a group cannot have the ' .
162  "same name: '{$filterDefinition['name']}'" );
163  }
164 
165  $this->name = $filterDefinition['name'];
166 
167  if ( isset( $filterDefinition['cssClassSuffix'] ) ) {
168  $this->cssClassSuffix = $filterDefinition['cssClassSuffix'];
169  $this->isRowApplicableCallable = $filterDefinition['isRowApplicableCallable'];
170  }
171 
172  if ( isset( $filterDefinition['label'] ) ) {
173  $this->label = $filterDefinition['label'];
174  $this->description = $filterDefinition['description'];
175  }
176 
177  $this->priority = $filterDefinition['priority'];
178 
179  $this->group->registerFilter( $this );
180  }
181 
196  public function conflictsWith( $other, $globalKey, $forwardKey, $backwardKey ) {
197  if ( $globalKey === null || $forwardKey === null || $backwardKey === null ) {
198  throw new MWException( 'All messages must be specified' );
199  }
200 
202  $other,
203  $globalKey,
204  $forwardKey
205  );
206 
207  $other->setUnidirectionalConflict(
208  $this,
209  $globalKey,
210  $backwardKey
211  );
212  }
213 
225  public function setUnidirectionalConflict( $other, $globalDescription, $contextDescription ) {
226  if ( $other instanceof ChangesListFilterGroup ) {
227  $this->conflictingGroups[] = [
228  'group' => $other->getName(),
229  'groupObject' => $other,
230  'globalDescription' => $globalDescription,
231  'contextDescription' => $contextDescription,
232  ];
233  } elseif ( $other instanceof ChangesListFilter ) {
234  $this->conflictingFilters[] = [
235  'group' => $other->getGroup()->getName(),
236  'filter' => $other->getName(),
237  'filterObject' => $other,
238  'globalDescription' => $globalDescription,
239  'contextDescription' => $contextDescription,
240  ];
241  } else {
242  throw new MWException( 'You can only pass in a ChangesListFilterGroup or a ChangesListFilter' );
243  }
244  }
245 
255  public function setAsSupersetOf( ChangesListFilter $other ) {
256  if ( $other->getGroup() !== $this->getGroup() ) {
257  throw new MWException( 'Supersets can only be defined for filters in the same group' );
258  }
259 
260  $this->subsetFilters[] = [
261  // It's always the same group, but this makes the representation
262  // more consistent with conflicts.
263  'group' => $other->getGroup()->getName(),
264  'filter' => $other->getName(),
265  ];
266  }
267 
271  public function getName() {
272  return $this->name;
273  }
274 
278  public function getGroup() {
279  return $this->group;
280  }
281 
285  public function getLabel() {
286  return $this->label;
287  }
288 
292  public function getDescription() {
293  return $this->description;
294  }
295 
301  abstract public function displaysOnUnstructuredUi();
302 
309  public function displaysOnStructuredUi() {
310  return $this->label !== null;
311  }
312 
321  return $this->displaysOnStructuredUi();
322  }
323 
327  public function getPriority() {
328  return $this->priority;
329  }
330 
336  protected function getCssClass() {
337  if ( $this->cssClassSuffix !== null ) {
339  } else {
340  return null;
341  }
342  }
343 
351  public function applyCssClassIfNeeded( IContextSource $ctx, RecentChange $rc, array &$classes ) {
352  if ( $this->isRowApplicableCallable === null ) {
353  return;
354  }
355 
356  if ( call_user_func( $this->isRowApplicableCallable, $ctx, $rc ) ) {
357  $classes[] = $this->getCssClass();
358  }
359  }
360 
368  public function getJsData() {
369  $output = [
370  'name' => $this->getName(),
371  'label' => $this->getLabel(),
372  'description' => $this->getDescription(),
373  'cssClass' => $this->getCssClass(),
374  'priority' => $this->priority,
375  'subset' => $this->subsetFilters,
376  'conflicts' => [],
377  'defaultHighlightColor' => $this->defaultHighlightColor
378  ];
379 
380  $output['messageKeys'] = [
381  $this->getLabel(),
382  $this->getDescription(),
383  ];
384 
385  $conflicts = array_merge(
386  $this->conflictingGroups,
387  $this->conflictingFilters
388  );
389 
390  foreach ( $conflicts as $conflictInfo ) {
391  unset( $conflictInfo['filterObject'] );
392  unset( $conflictInfo['groupObject'] );
393  $output['conflicts'][] = $conflictInfo;
394  array_push(
395  $output['messageKeys'],
396  $conflictInfo['globalDescription'],
397  $conflictInfo['contextDescription']
398  );
399  }
400 
401  return $output;
402  }
403 
410  abstract public function isSelected( FormOptions $opts );
411 
417  public function getConflictingGroups() {
418  return array_map(
419  function ( $conflictDesc ) {
420  return $conflictDesc[ 'groupObject' ];
421  },
423  );
424  }
425 
431  public function getConflictingFilters() {
432  return array_map(
433  function ( $conflictDesc ) {
434  return $conflictDesc[ 'filterObject' ];
435  },
437  );
438  }
439 
448  if ( $group->anySelected( $opts ) && $this->isSelected( $opts ) ) {
450  foreach ( $this->getSiblings() as $siblingFilter ) {
451  if ( $siblingFilter->isSelected( $opts ) && !$siblingFilter->hasConflictWithGroup( $group ) ) {
452  return false;
453  }
454  }
455  return true;
456  }
457  return false;
458  }
459 
461  return in_array( $group, $this->getConflictingGroups() );
462  }
463 
471  public function activelyInConflictWithFilter( ChangesListFilter $filter, FormOptions $opts ) {
472  if ( $this->isSelected( $opts ) && $filter->isSelected( $opts ) ) {
474  foreach ( $this->getSiblings() as $siblingFilter ) {
475  if (
476  $siblingFilter->isSelected( $opts ) &&
477  !$siblingFilter->hasConflictWithFilter( $filter )
478  ) {
479  return false;
480  }
481  }
482  return true;
483  }
484  return false;
485  }
486 
487  private function hasConflictWithFilter( ChangesListFilter $filter ) {
488  return in_array( $filter, $this->getConflictingFilters() );
489  }
490 
496  protected function getSiblings() {
497  return array_filter(
498  $this->getGroup()->getFilters(),
499  function ( $filter ) {
500  return $filter !== $this;
501  }
502  );
503  }
504 
509  $this->defaultHighlightColor = $defaultHighlightColor;
510  }
511 }
ChangesListFilter\activelyInConflictWithGroup
activelyInConflictWithGroup(ChangesListFilterGroup $group, FormOptions $opts)
Check if the conflict with a group is currently "active".
Definition: ChangesListFilter.php:447
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:71
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:255
ChangesListFilter\displaysOnStructuredUi
displaysOnStructuredUi()
Checks whether the filter should display on the structured UI This refers to the exact filter.
Definition: ChangesListFilter.php:309
ChangesListFilter\getName
getName()
Definition: ChangesListFilter.php:271
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:196
ChangesListFilter\hasConflictWithFilter
hasConflictWithFilter(ChangesListFilter $filter)
Definition: ChangesListFilter.php:487
ChangesListFilter\getGroup
getGroup()
Definition: ChangesListFilter.php:278
MWException
MediaWiki exception.
Definition: MWException.php:26
ChangesListFilter\hasConflictWithGroup
hasConflictWithGroup(ChangesListFilterGroup $group)
Definition: ChangesListFilter.php:460
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:496
ChangesListFilter\getConflictingGroups
getConflictingGroups()
Get groups conflicting with this filter.
Definition: ChangesListFilter.php:417
ChangesListFilter\getJsData
getJsData()
Gets the JS data required by the front-end of the structured UI.
Definition: ChangesListFilter.php:368
ChangesListFilter\applyCssClassIfNeeded
applyCssClassIfNeeded(IContextSource $ctx, RecentChange $rc, array &$classes)
Add CSS class if needed.
Definition: ChangesListFilter.php:351
ChangesListFilter\getDescription
getDescription()
Definition: ChangesListFilter.php:292
ChangesListFilter\$priority
int $priority
Priority integer.
Definition: ChangesListFilter.php:103
ChangesList\CSS_CLASS_PREFIX
const CSS_CLASS_PREFIX
Definition: ChangesList.php:30
ChangesListFilter\isFeatureAvailableOnStructuredUi
isFeatureAvailableOnStructuredUi()
Checks whether an equivalent feature for this filter is available on the structured UI.
Definition: ChangesListFilter.php:320
ChangesListFilter\getCssClass
getCssClass()
Gets the CSS class.
Definition: ChangesListFilter.php:336
ChangesListFilter\$defaultHighlightColor
string $defaultHighlightColor
Definition: ChangesListFilter.php:109
ChangesListFilter\RESERVED_NAME_CHAR
const RESERVED_NAME_CHAR
Definition: ChangesListFilter.php:111
ChangesListFilter\setDefaultHighlightColor
setDefaultHighlightColor( $defaultHighlightColor)
Definition: ChangesListFilter.php:508
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:53
ChangesListFilterGroup\anySelected
anySelected(FormOptions $opts)
Check if any filter in this group is selected.
Definition: ChangesListFilterGroup.php:432
ChangesListFilter\getLabel
getLabel()
Definition: ChangesListFilter.php:285
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:471
ChangesListFilter\setUnidirectionalConflict
setUnidirectionalConflict( $other, $globalDescription, $contextDescription)
Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
Definition: ChangesListFilter.php:225
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:327
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:145
ChangesListFilter\$name
string $name
Filter name.
Definition: ChangesListFilter.php:35
ChangesListFilter\getConflictingFilters
getConflictingFilters()
Get filters conflicting with this filter.
Definition: ChangesListFilter.php:431