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 
143  public function __construct( array $filterDefinition ) {
144  if ( isset( $filterDefinition['group'] ) ) {
145  $this->group = $filterDefinition['group'];
146  } else {
147  throw new MWException( 'You must use \'group\' to specify the ' .
148  'ChangesListFilterGroup this filter belongs to' );
149  }
150 
151  if ( strpos( $filterDefinition['name'], self::RESERVED_NAME_CHAR ) !== false ) {
152  throw new MWException( 'Filter names may not contain \'' .
153  self::RESERVED_NAME_CHAR .
154  '\'. Use the naming convention: \'lowercase\''
155  );
156  }
157 
158  if ( $this->group->getFilter( $filterDefinition['name'] ) ) {
159  throw new MWException( 'Two filters in a group cannot have the ' .
160  "same name: '{$filterDefinition['name']}'" );
161  }
162 
163  $this->name = $filterDefinition['name'];
164 
165  if ( isset( $filterDefinition['cssClassSuffix'] ) ) {
166  $this->cssClassSuffix = $filterDefinition['cssClassSuffix'];
167  $this->isRowApplicableCallable = $filterDefinition['isRowApplicableCallable'];
168  }
169 
170  if ( isset( $filterDefinition['label'] ) ) {
171  $this->label = $filterDefinition['label'];
172  $this->description = $filterDefinition['description'];
173  }
174 
175  $this->priority = $filterDefinition['priority'];
176 
177  $this->group->registerFilter( $this );
178  }
179 
194  public function conflictsWith( $other, $globalKey, $forwardKey, $backwardKey ) {
195  if ( $globalKey === null || $forwardKey === null || $backwardKey === null ) {
196  throw new MWException( 'All messages must be specified' );
197  }
198 
200  $other,
201  $globalKey,
202  $forwardKey
203  );
204 
205  $other->setUnidirectionalConflict(
206  $this,
207  $globalKey,
208  $backwardKey
209  );
210  }
211 
223  public function setUnidirectionalConflict( $other, $globalDescription, $contextDescription ) {
224  if ( $other instanceof ChangesListFilterGroup ) {
225  $this->conflictingGroups[] = [
226  'group' => $other->getName(),
227  'groupObject' => $other,
228  'globalDescription' => $globalDescription,
229  'contextDescription' => $contextDescription,
230  ];
231  } elseif ( $other instanceof ChangesListFilter ) {
232  $this->conflictingFilters[] = [
233  'group' => $other->getGroup()->getName(),
234  'filter' => $other->getName(),
235  'filterObject' => $other,
236  'globalDescription' => $globalDescription,
237  'contextDescription' => $contextDescription,
238  ];
239  } else {
240  throw new MWException( 'You can only pass in a ChangesListFilterGroup or a ChangesListFilter' );
241  }
242  }
243 
253  public function setAsSupersetOf( ChangesListFilter $other ) {
254  if ( $other->getGroup() !== $this->getGroup() ) {
255  throw new MWException( 'Supersets can only be defined for filters in the same group' );
256  }
257 
258  $this->subsetFilters[] = [
259  // It's always the same group, but this makes the representation
260  // more consistent with conflicts.
261  'group' => $other->getGroup()->getName(),
262  'filter' => $other->getName(),
263  ];
264  }
265 
269  public function getName() {
270  return $this->name;
271  }
272 
276  public function getGroup() {
277  return $this->group;
278  }
279 
283  public function getLabel() {
284  return $this->label;
285  }
286 
290  public function getDescription() {
291  return $this->description;
292  }
293 
299  abstract public function displaysOnUnstructuredUi();
300 
307  public function displaysOnStructuredUi() {
308  return $this->label !== null;
309  }
310 
319  return $this->displaysOnStructuredUi();
320  }
321 
325  public function getPriority() {
326  return $this->priority;
327  }
328 
334  protected function getCssClass() {
335  if ( $this->cssClassSuffix !== null ) {
337  } else {
338  return null;
339  }
340  }
341 
349  public function applyCssClassIfNeeded( IContextSource $ctx, RecentChange $rc, array &$classes ) {
350  if ( $this->isRowApplicableCallable === null ) {
351  return;
352  }
353 
354  if ( call_user_func( $this->isRowApplicableCallable, $ctx, $rc ) ) {
355  $classes[] = $this->getCssClass();
356  }
357  }
358 
366  public function getJsData() {
367  $output = [
368  'name' => $this->getName(),
369  'label' => $this->getLabel(),
370  'description' => $this->getDescription(),
371  'cssClass' => $this->getCssClass(),
372  'priority' => $this->priority,
373  'subset' => $this->subsetFilters,
374  'conflicts' => [],
375  'defaultHighlightColor' => $this->defaultHighlightColor
376  ];
377 
378  $output['messageKeys'] = [
379  $this->getLabel(),
380  $this->getDescription(),
381  ];
382 
383  $conflicts = array_merge(
384  $this->conflictingGroups,
385  $this->conflictingFilters
386  );
387 
388  foreach ( $conflicts as $conflictInfo ) {
389  unset( $conflictInfo['filterObject'] );
390  unset( $conflictInfo['groupObject'] );
391  $output['conflicts'][] = $conflictInfo;
392  array_push(
393  $output['messageKeys'],
394  $conflictInfo['globalDescription'],
395  $conflictInfo['contextDescription']
396  );
397  }
398 
399  return $output;
400  }
401 
408  abstract public function isSelected( FormOptions $opts );
409 
415  public function getConflictingGroups() {
416  return array_map(
417  static function ( $conflictDesc ) {
418  return $conflictDesc[ 'groupObject' ];
419  },
421  );
422  }
423 
429  public function getConflictingFilters() {
430  return array_map(
431  static function ( $conflictDesc ) {
432  return $conflictDesc[ 'filterObject' ];
433  },
435  );
436  }
437 
446  if ( $group->anySelected( $opts ) && $this->isSelected( $opts ) ) {
448  foreach ( $this->getSiblings() as $siblingFilter ) {
449  if ( $siblingFilter->isSelected( $opts ) && !$siblingFilter->hasConflictWithGroup( $group ) ) {
450  return false;
451  }
452  }
453  return true;
454  }
455  return false;
456  }
457 
459  return in_array( $group, $this->getConflictingGroups() );
460  }
461 
469  public function activelyInConflictWithFilter( ChangesListFilter $filter, FormOptions $opts ) {
470  if ( $this->isSelected( $opts ) && $filter->isSelected( $opts ) ) {
472  foreach ( $this->getSiblings() as $siblingFilter ) {
473  if (
474  $siblingFilter->isSelected( $opts ) &&
475  !$siblingFilter->hasConflictWithFilter( $filter )
476  ) {
477  return false;
478  }
479  }
480  return true;
481  }
482  return false;
483  }
484 
485  private function hasConflictWithFilter( ChangesListFilter $filter ) {
486  return in_array( $filter, $this->getConflictingFilters() );
487  }
488 
494  protected function getSiblings() {
495  return array_filter(
496  $this->getGroup()->getFilters(),
497  function ( $filter ) {
498  return $filter !== $this;
499  }
500  );
501  }
502 
507  $this->defaultHighlightColor = $defaultHighlightColor;
508  }
509 }
ChangesListFilter\activelyInConflictWithGroup
activelyInConflictWithGroup(ChangesListFilterGroup $group, FormOptions $opts)
Check if the conflict with a group is currently "active".
Definition: ChangesListFilter.php:445
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:80
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:253
ChangesListFilter\displaysOnStructuredUi
displaysOnStructuredUi()
Checks whether the filter should display on the structured UI This refers to the exact filter.
Definition: ChangesListFilter.php:307
ChangesListFilter\getName
getName()
Definition: ChangesListFilter.php:269
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:194
ChangesListFilter\hasConflictWithFilter
hasConflictWithFilter(ChangesListFilter $filter)
Definition: ChangesListFilter.php:485
ChangesListFilter\getGroup
getGroup()
Definition: ChangesListFilter.php:276
MWException
MediaWiki exception.
Definition: MWException.php:29
ChangesListFilter\hasConflictWithGroup
hasConflictWithGroup(ChangesListFilterGroup $group)
Definition: ChangesListFilter.php:458
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:494
ChangesListFilter\getConflictingGroups
getConflictingGroups()
Get groups conflicting with this filter.
Definition: ChangesListFilter.php:415
ChangesListFilter\getJsData
getJsData()
Gets the JS data required by the front-end of the structured UI.
Definition: ChangesListFilter.php:366
ChangesListFilter\applyCssClassIfNeeded
applyCssClassIfNeeded(IContextSource $ctx, RecentChange $rc, array &$classes)
Add CSS class if needed.
Definition: ChangesListFilter.php:349
ChangesListFilter\getDescription
getDescription()
Definition: ChangesListFilter.php:290
ChangesListFilter\$priority
int $priority
Priority integer.
Definition: ChangesListFilter.php:103
ChangesList\CSS_CLASS_PREFIX
const CSS_CLASS_PREFIX
Definition: ChangesList.php:39
ChangesListFilter\isFeatureAvailableOnStructuredUi
isFeatureAvailableOnStructuredUi()
Checks whether an equivalent feature for this filter is available on the structured UI.
Definition: ChangesListFilter.php:318
ChangesListFilter\getCssClass
getCssClass()
Gets the CSS class.
Definition: ChangesListFilter.php:334
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:506
IContextSource
Interface for objects which can provide a MediaWiki context on request.
Definition: IContextSource.php:58
ChangesListFilterGroup\anySelected
anySelected(FormOptions $opts)
Check if any filter in this group is selected.
Definition: ChangesListFilterGroup.php:432
ChangesListFilter\getLabel
getLabel()
Definition: ChangesListFilter.php:283
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:469
ChangesListFilter\setUnidirectionalConflict
setUnidirectionalConflict( $other, $globalDescription, $contextDescription)
Marks that the given ChangesListFilterGroup or ChangesListFilter conflicts with this object.
Definition: ChangesListFilter.php:223
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:325
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:143
ChangesListFilter\$name
string $name
Filter name.
Definition: ChangesListFilter.php:35
ChangesListFilter\getConflictingFilters
getConflictingFilters()
Get filters conflicting with this filter.
Definition: ChangesListFilter.php:429