MediaWiki  1.29.1
ChangesListSpecialPage.php
Go to the documentation of this file.
1 <?php
26 
33 abstract class ChangesListSpecialPage extends SpecialPage {
35  protected $rcSubpage;
36 
38  protected $rcOptions;
39 
41  protected $customFilters;
42 
43  // Order of both groups and filters is significant; first is top-most priority,
44  // descending from there.
45  // 'showHideSuffix' is a shortcut to and avoid spelling out
46  // details specific to subclasses here.
61 
62  // Same format as filterGroupDefinitions, but for a single group (reviewStatus)
63  // that is registered conditionally.
65 
66  // Single filter registered conditionally
68 
75  protected $filterGroups = [];
76 
77  public function __construct( $name, $restriction ) {
78  parent::__construct( $name, $restriction );
79 
80  $this->filterGroupDefinitions = [
81  [
82  'name' => 'registration',
83  'title' => 'rcfilters-filtergroup-registration',
85  'filters' => [
86  [
87  'name' => 'hideliu',
88  'label' => 'rcfilters-filter-registered-label',
89  'description' => 'rcfilters-filter-registered-description',
90  // rcshowhideliu-show, rcshowhideliu-hide,
91  // wlshowhideliu
92  'showHideSuffix' => 'showhideliu',
93  'default' => false,
94  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
95  &$query_options, &$join_conds ) {
96 
97  $conds[] = 'rc_user = 0';
98  },
99  'cssClassSuffix' => 'liu',
100  'isRowApplicableCallable' => function ( $ctx, $rc ) {
101  return $rc->getAttribute( 'rc_user' );
102  },
103 
104  ],
105  [
106  'name' => 'hideanons',
107  'label' => 'rcfilters-filter-unregistered-label',
108  'description' => 'rcfilters-filter-unregistered-description',
109  // rcshowhideanons-show, rcshowhideanons-hide,
110  // wlshowhideanons
111  'showHideSuffix' => 'showhideanons',
112  'default' => false,
113  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
114  &$query_options, &$join_conds ) {
115 
116  $conds[] = 'rc_user != 0';
117  },
118  'cssClassSuffix' => 'anon',
119  'isRowApplicableCallable' => function ( $ctx, $rc ) {
120  return !$rc->getAttribute( 'rc_user' );
121  },
122  ]
123  ],
124  ],
125 
126  [
127  'name' => 'userExpLevel',
128  'title' => 'rcfilters-filtergroup-userExpLevel',
130  // Excludes unregistered users
131  'isFullCoverage' => false,
132  'filters' => [
133  [
134  'name' => 'newcomer',
135  'label' => 'rcfilters-filter-user-experience-level-newcomer-label',
136  'description' => 'rcfilters-filter-user-experience-level-newcomer-description',
137  'cssClassSuffix' => 'user-newcomer',
138  'isRowApplicableCallable' => function ( $ctx, $rc ) {
139  $performer = $rc->getPerformer();
140  return $performer && $performer->isLoggedIn() &&
141  $performer->getExperienceLevel() === 'newcomer';
142  }
143  ],
144  [
145  'name' => 'learner',
146  'label' => 'rcfilters-filter-user-experience-level-learner-label',
147  'description' => 'rcfilters-filter-user-experience-level-learner-description',
148  'cssClassSuffix' => 'user-learner',
149  'isRowApplicableCallable' => function ( $ctx, $rc ) {
150  $performer = $rc->getPerformer();
151  return $performer && $performer->isLoggedIn() &&
152  $performer->getExperienceLevel() === 'learner';
153  },
154  ],
155  [
156  'name' => 'experienced',
157  'label' => 'rcfilters-filter-user-experience-level-experienced-label',
158  'description' => 'rcfilters-filter-user-experience-level-experienced-description',
159  'cssClassSuffix' => 'user-experienced',
160  'isRowApplicableCallable' => function ( $ctx, $rc ) {
161  $performer = $rc->getPerformer();
162  return $performer && $performer->isLoggedIn() &&
163  $performer->getExperienceLevel() === 'experienced';
164  },
165  ]
166  ],
168  'queryCallable' => [ $this, 'filterOnUserExperienceLevel' ],
169  ],
170 
171  [
172  'name' => 'authorship',
173  'title' => 'rcfilters-filtergroup-authorship',
175  'filters' => [
176  [
177  'name' => 'hidemyself',
178  'label' => 'rcfilters-filter-editsbyself-label',
179  'description' => 'rcfilters-filter-editsbyself-description',
180  // rcshowhidemine-show, rcshowhidemine-hide,
181  // wlshowhidemine
182  'showHideSuffix' => 'showhidemine',
183  'default' => false,
184  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
185  &$query_options, &$join_conds ) {
186 
187  $user = $ctx->getUser();
188  $conds[] = 'rc_user_text != ' . $dbr->addQuotes( $user->getName() );
189  },
190  'cssClassSuffix' => 'self',
191  'isRowApplicableCallable' => function ( $ctx, $rc ) {
192  return $ctx->getUser()->equals( $rc->getPerformer() );
193  },
194  ],
195  [
196  'name' => 'hidebyothers',
197  'label' => 'rcfilters-filter-editsbyother-label',
198  'description' => 'rcfilters-filter-editsbyother-description',
199  'default' => false,
200  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
201  &$query_options, &$join_conds ) {
202 
203  $user = $ctx->getUser();
204  $conds[] = 'rc_user_text = ' . $dbr->addQuotes( $user->getName() );
205  },
206  'cssClassSuffix' => 'others',
207  'isRowApplicableCallable' => function ( $ctx, $rc ) {
208  return !$ctx->getUser()->equals( $rc->getPerformer() );
209  },
210  ]
211  ]
212  ],
213 
214  [
215  'name' => 'automated',
216  'title' => 'rcfilters-filtergroup-automated',
218  'filters' => [
219  [
220  'name' => 'hidebots',
221  'label' => 'rcfilters-filter-bots-label',
222  'description' => 'rcfilters-filter-bots-description',
223  // rcshowhidebots-show, rcshowhidebots-hide,
224  // wlshowhidebots
225  'showHideSuffix' => 'showhidebots',
226  'default' => false,
227  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
228  &$query_options, &$join_conds ) {
229 
230  $conds[] = 'rc_bot = 0';
231  },
232  'cssClassSuffix' => 'bot',
233  'isRowApplicableCallable' => function ( $ctx, $rc ) {
234  return $rc->getAttribute( 'rc_bot' );
235  },
236  ],
237  [
238  'name' => 'hidehumans',
239  'label' => 'rcfilters-filter-humans-label',
240  'description' => 'rcfilters-filter-humans-description',
241  'default' => false,
242  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
243  &$query_options, &$join_conds ) {
244 
245  $conds[] = 'rc_bot = 1';
246  },
247  'cssClassSuffix' => 'human',
248  'isRowApplicableCallable' => function ( $ctx, $rc ) {
249  return !$rc->getAttribute( 'rc_bot' );
250  },
251  ]
252  ]
253  ],
254 
255  // reviewStatus (conditional)
256 
257  [
258  'name' => 'significance',
259  'title' => 'rcfilters-filtergroup-significance',
261  'priority' => -6,
262  'filters' => [
263  [
264  'name' => 'hideminor',
265  'label' => 'rcfilters-filter-minor-label',
266  'description' => 'rcfilters-filter-minor-description',
267  // rcshowhideminor-show, rcshowhideminor-hide,
268  // wlshowhideminor
269  'showHideSuffix' => 'showhideminor',
270  'default' => false,
271  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
272  &$query_options, &$join_conds ) {
273 
274  $conds[] = 'rc_minor = 0';
275  },
276  'cssClassSuffix' => 'minor',
277  'isRowApplicableCallable' => function ( $ctx, $rc ) {
278  return $rc->getAttribute( 'rc_minor' );
279  }
280  ],
281  [
282  'name' => 'hidemajor',
283  'label' => 'rcfilters-filter-major-label',
284  'description' => 'rcfilters-filter-major-description',
285  'default' => false,
286  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
287  &$query_options, &$join_conds ) {
288 
289  $conds[] = 'rc_minor = 1';
290  },
291  'cssClassSuffix' => 'major',
292  'isRowApplicableCallable' => function ( $ctx, $rc ) {
293  return !$rc->getAttribute( 'rc_minor' );
294  }
295  ]
296  ]
297  ],
298 
299  // With extensions, there can be change types that will not be hidden by any of these.
300  [
301  'name' => 'changeType',
302  'title' => 'rcfilters-filtergroup-changetype',
304  'filters' => [
305  [
306  'name' => 'hidepageedits',
307  'label' => 'rcfilters-filter-pageedits-label',
308  'description' => 'rcfilters-filter-pageedits-description',
309  'default' => false,
310  'priority' => -2,
311  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
312  &$query_options, &$join_conds ) {
313 
314  $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_EDIT );
315  },
316  'cssClassSuffix' => 'src-mw-edit',
317  'isRowApplicableCallable' => function ( $ctx, $rc ) {
318  return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_EDIT;
319  },
320  ],
321  [
322  'name' => 'hidenewpages',
323  'label' => 'rcfilters-filter-newpages-label',
324  'description' => 'rcfilters-filter-newpages-description',
325  'default' => false,
326  'priority' => -3,
327  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
328  &$query_options, &$join_conds ) {
329 
330  $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_NEW );
331  },
332  'cssClassSuffix' => 'src-mw-new',
333  'isRowApplicableCallable' => function ( $ctx, $rc ) {
334  return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_NEW;
335  },
336  ],
337 
338  // hidecategorization
339 
340  [
341  'name' => 'hidelog',
342  'label' => 'rcfilters-filter-logactions-label',
343  'description' => 'rcfilters-filter-logactions-description',
344  'default' => false,
345  'priority' => -5,
346  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
347  &$query_options, &$join_conds ) {
348 
349  $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_LOG );
350  },
351  'cssClassSuffix' => 'src-mw-log',
352  'isRowApplicableCallable' => function ( $ctx, $rc ) {
353  return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_LOG;
354  }
355  ],
356  ],
357  ],
358  ];
359 
360  $this->reviewStatusFilterGroupDefinition = [
361  [
362  'name' => 'reviewStatus',
363  'title' => 'rcfilters-filtergroup-reviewstatus',
365  'priority' => -5,
366  'filters' => [
367  [
368  'name' => 'hidepatrolled',
369  'label' => 'rcfilters-filter-patrolled-label',
370  'description' => 'rcfilters-filter-patrolled-description',
371  // rcshowhidepatr-show, rcshowhidepatr-hide
372  // wlshowhidepatr
373  'showHideSuffix' => 'showhidepatr',
374  'default' => false,
375  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
376  &$query_options, &$join_conds ) {
377 
378  $conds[] = 'rc_patrolled = 0';
379  },
380  'cssClassSuffix' => 'patrolled',
381  'isRowApplicableCallable' => function ( $ctx, $rc ) {
382  return $rc->getAttribute( 'rc_patrolled' );
383  },
384  ],
385  [
386  'name' => 'hideunpatrolled',
387  'label' => 'rcfilters-filter-unpatrolled-label',
388  'description' => 'rcfilters-filter-unpatrolled-description',
389  'default' => false,
390  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
391  &$query_options, &$join_conds ) {
392 
393  $conds[] = 'rc_patrolled = 1';
394  },
395  'cssClassSuffix' => 'unpatrolled',
396  'isRowApplicableCallable' => function ( $ctx, $rc ) {
397  return !$rc->getAttribute( 'rc_patrolled' );
398  },
399  ],
400  ],
401  ]
402  ];
403 
404  $this->hideCategorizationFilterDefinition = [
405  'name' => 'hidecategorization',
406  'label' => 'rcfilters-filter-categorization-label',
407  'description' => 'rcfilters-filter-categorization-description',
408  // rcshowhidecategorization-show, rcshowhidecategorization-hide.
409  // wlshowhidecategorization
410  'showHideSuffix' => 'showhidecategorization',
411  'default' => false,
412  'priority' => -4,
413  'queryCallable' => function ( $specialClassName, $ctx, $dbr, &$tables, &$fields, &$conds,
414  &$query_options, &$join_conds ) {
415 
416  $conds[] = 'rc_type != ' . $dbr->addQuotes( RC_CATEGORIZE );
417  },
418  'cssClassSuffix' => 'src-mw-categorize',
419  'isRowApplicableCallable' => function ( $ctx, $rc ) {
420  return $rc->getAttribute( 'rc_source' ) === RecentChange::SRC_CATEGORIZE;
421  },
422  ];
423  }
424 
430  protected function areFiltersInConflict() {
431  $opts = $this->getOptions();
433  foreach ( $this->getFilterGroups() as $group ) {
434 
435  if ( $group->getConflictingGroups() ) {
436  wfLogWarning(
437  $group->getName() .
438  " specifies conflicts with other groups but these are not supported yet."
439  );
440  }
441 
443  foreach ( $group->getConflictingFilters() as $conflictingFilter ) {
444  if ( $conflictingFilter->activelyInConflictWithGroup( $group, $opts ) ) {
445  return true;
446  }
447  }
448 
450  foreach ( $group->getFilters() as $filter ) {
451 
453  foreach ( $filter->getConflictingFilters() as $conflictingFilter ) {
454  if (
455  $conflictingFilter->activelyInConflictWithFilter( $filter, $opts ) &&
456  $filter->activelyInConflictWithFilter( $conflictingFilter, $opts )
457  ) {
458  return true;
459  }
460  }
461 
462  }
463 
464  }
465 
466  return false;
467  }
468 
474  public function execute( $subpage ) {
475  $this->rcSubpage = $subpage;
476 
477  $this->setHeaders();
478  $this->outputHeader();
479  $this->addModules();
480 
481  $rows = $this->getRows();
482  $opts = $this->getOptions();
483  if ( $rows === false ) {
484  if ( !$this->including() ) {
485  $this->doHeader( $opts, 0 );
486  $this->outputNoResults();
487  $this->getOutput()->setStatusCode( 404 );
488  }
489 
490  return;
491  }
492 
493  $batch = new LinkBatch;
494  foreach ( $rows as $row ) {
495  $batch->add( NS_USER, $row->rc_user_text );
496  $batch->add( NS_USER_TALK, $row->rc_user_text );
497  $batch->add( $row->rc_namespace, $row->rc_title );
498  if ( $row->rc_source === RecentChange::SRC_LOG ) {
499  $formatter = LogFormatter::newFromRow( $row );
500  foreach ( $formatter->getPreloadTitles() as $title ) {
501  $batch->addObj( $title );
502  }
503  }
504  }
505  $batch->execute();
506  $this->webOutput( $rows, $opts );
507 
508  $rows->free();
509 
510  if ( $this->getConfig()->get( 'EnableWANCacheReaper' ) ) {
511  // Clean up any bad page entries for titles showing up in RC
513  $this->getDB(),
514  LoggerFactory::getInstance( 'objectcache' )
515  ) );
516  }
517  }
518 
522  protected function outputNoResults() {
523  $this->getOutput()->addHTML(
524  '<div class="mw-changeslist-empty">' .
525  $this->msg( 'recentchanges-noresult' )->parse() .
526  '</div>'
527  );
528  }
529 
535  public function getRows() {
536  $opts = $this->getOptions();
537 
538  $tables = [];
539  $fields = [];
540  $conds = [];
541  $query_options = [];
542  $join_conds = [];
543  $this->buildQuery( $tables, $fields, $conds, $query_options, $join_conds, $opts );
544 
545  return $this->doMainQuery( $tables, $fields, $conds, $query_options, $join_conds, $opts );
546  }
547 
553  public function getOptions() {
554  if ( $this->rcOptions === null ) {
555  $this->rcOptions = $this->setup( $this->rcSubpage );
556  }
557 
558  return $this->rcOptions;
559  }
560 
570  protected function registerFilters() {
571  $this->registerFiltersFromDefinitions( $this->filterGroupDefinitions );
572 
573  // Make sure this is not being transcluded (we don't want to show this
574  // information to all users just because the user that saves the edit can
575  // patrol)
576  if ( !$this->including() && $this->getUser()->useRCPatrol() ) {
577  $this->registerFiltersFromDefinitions( $this->reviewStatusFilterGroupDefinition );
578  }
579 
580  $changeTypeGroup = $this->getFilterGroup( 'changeType' );
581 
582  if ( $this->getConfig()->get( 'RCWatchCategoryMembership' ) ) {
583  $transformedHideCategorizationDef = $this->transformFilterDefinition(
584  $this->hideCategorizationFilterDefinition
585  );
586 
587  $transformedHideCategorizationDef['group'] = $changeTypeGroup;
588 
589  $hideCategorization = new ChangesListBooleanFilter(
590  $transformedHideCategorizationDef
591  );
592  }
593 
594  Hooks::run( 'ChangesListSpecialPageStructuredFilters', [ $this ] );
595 
596  $unstructuredGroupDefinition =
598  $this->getCustomFilters()
599  );
600  $this->registerFiltersFromDefinitions( [ $unstructuredGroupDefinition ] );
601 
602  $userExperienceLevel = $this->getFilterGroup( 'userExpLevel' );
603 
604  $registration = $this->getFilterGroup( 'registration' );
605  $anons = $registration->getFilter( 'hideanons' );
606 
607  // This means there is a conflict between any item in user experience level
608  // being checked and only anons being *shown* (hideliu=1&hideanons=0 in the
609  // URL, or equivalent).
610  $userExperienceLevel->conflictsWith(
611  $anons,
612  'rcfilters-filtergroup-user-experience-level-conflicts-unregistered-global',
613  'rcfilters-filtergroup-user-experience-level-conflicts-unregistered',
614  'rcfilters-filter-unregistered-conflicts-user-experience-level'
615  );
616 
617  $categoryFilter = $changeTypeGroup->getFilter( 'hidecategorization' );
618  $logactionsFilter = $changeTypeGroup->getFilter( 'hidelog' );
619  $pagecreationFilter = $changeTypeGroup->getFilter( 'hidenewpages' );
620 
621  $significanceTypeGroup = $this->getFilterGroup( 'significance' );
622  $hideMinorFilter = $significanceTypeGroup->getFilter( 'hideminor' );
623 
624  // categoryFilter is conditional; see registerFilters
625  if ( $categoryFilter !== null ) {
626  $hideMinorFilter->conflictsWith(
627  $categoryFilter,
628  'rcfilters-hideminor-conflicts-typeofchange-global',
629  'rcfilters-hideminor-conflicts-typeofchange',
630  'rcfilters-typeofchange-conflicts-hideminor'
631  );
632  }
633  $hideMinorFilter->conflictsWith(
634  $logactionsFilter,
635  'rcfilters-hideminor-conflicts-typeofchange-global',
636  'rcfilters-hideminor-conflicts-typeofchange',
637  'rcfilters-typeofchange-conflicts-hideminor'
638  );
639  $hideMinorFilter->conflictsWith(
640  $pagecreationFilter,
641  'rcfilters-hideminor-conflicts-typeofchange-global',
642  'rcfilters-hideminor-conflicts-typeofchange',
643  'rcfilters-typeofchange-conflicts-hideminor'
644  );
645  }
646 
656  protected function transformFilterDefinition( array $filterDefinition ) {
657  return $filterDefinition;
658  }
659 
668  protected function registerFiltersFromDefinitions( array $definition ) {
669  $autoFillPriority = -1;
670  foreach ( $definition as $groupDefinition ) {
671  if ( !isset( $groupDefinition['priority'] ) ) {
672  $groupDefinition['priority'] = $autoFillPriority;
673  } else {
674  // If it's explicitly specified, start over the auto-fill
675  $autoFillPriority = $groupDefinition['priority'];
676  }
677 
678  $autoFillPriority--;
679 
680  $className = $groupDefinition['class'];
681  unset( $groupDefinition['class'] );
682 
683  foreach ( $groupDefinition['filters'] as &$filterDefinition ) {
684  $filterDefinition = $this->transformFilterDefinition( $filterDefinition );
685  }
686 
687  $this->registerFilterGroup( new $className( $groupDefinition ) );
688  }
689  }
690 
698  // Special internal unstructured group
699  $unstructuredGroupDefinition = [
700  'name' => 'unstructured',
702  'priority' => -1, // Won't display in structured
703  'filters' => [],
704  ];
705 
706  foreach ( $customFilters as $name => $params ) {
707  $unstructuredGroupDefinition['filters'][] = [
708  'name' => $name,
709  'showHide' => $params['msg'],
710  'default' => $params['default'],
711  ];
712  }
713 
714  return $unstructuredGroupDefinition;
715  }
716 
725  public function setup( $parameters ) {
726  $this->registerFilters();
727 
728  $opts = $this->getDefaultOptions();
729 
730  $opts = $this->fetchOptionsFromRequest( $opts );
731 
732  // Give precedence to subpage syntax
733  if ( $parameters !== null ) {
734  $this->parseParameters( $parameters, $opts );
735  }
736 
737  $this->validateOptions( $opts );
738 
739  return $opts;
740  }
741 
751  public function getDefaultOptions() {
752  $config = $this->getConfig();
753  $opts = new FormOptions();
754  $structuredUI = $this->getUser()->getOption( 'rcenhancedfilters' );
755 
756  // Add all filters
757  foreach ( $this->filterGroups as $filterGroup ) {
758  // URL parameters can be per-group, like 'userExpLevel',
759  // or per-filter, like 'hideminor'.
760  if ( $filterGroup->isPerGroupRequestParameter() ) {
761  $opts->add( $filterGroup->getName(), $filterGroup->getDefault() );
762  } else {
763  foreach ( $filterGroup->getFilters() as $filter ) {
764  $opts->add( $filter->getName(), $filter->getDefault( $structuredUI ) );
765  }
766  }
767  }
768 
769  $opts->add( 'namespace', '', FormOptions::INTNULL );
770  $opts->add( 'invert', false );
771  $opts->add( 'associated', false );
772 
773  return $opts;
774  }
775 
781  public function registerFilterGroup( ChangesListFilterGroup $group ) {
782  $groupName = $group->getName();
783 
784  $this->filterGroups[$groupName] = $group;
785  }
786 
792  protected function getFilterGroups() {
793  return $this->filterGroups;
794  }
795 
803  public function getFilterGroup( $groupName ) {
804  return isset( $this->filterGroups[$groupName] ) ?
805  $this->filterGroups[$groupName] :
806  null;
807  }
808 
809  // Currently, this intentionally only includes filters that display
810  // in the structured UI. This can be changed easily, though, if we want
811  // to include data on filters that use the unstructured UI. messageKeys is a
812  // special top-level value, with the value being an array of the message keys to
813  // send to the client.
821  public function getStructuredFilterJsData() {
822  $output = [
823  'groups' => [],
824  'messageKeys' => [],
825  ];
826 
827  $context = $this->getContext();
828 
829  usort( $this->filterGroups, function ( $a, $b ) {
830  return $b->getPriority() - $a->getPriority();
831  } );
832 
833  foreach ( $this->filterGroups as $groupName => $group ) {
834  $groupOutput = $group->getJsData( $this );
835  if ( $groupOutput !== null ) {
836  $output['messageKeys'] = array_merge(
837  $output['messageKeys'],
838  $groupOutput['messageKeys']
839  );
840 
841  unset( $groupOutput['messageKeys'] );
842  $output['groups'][] = $groupOutput;
843  }
844  }
845 
846  return $output;
847  }
848 
855  protected function getCustomFilters() {
856  if ( $this->customFilters === null ) {
857  $this->customFilters = [];
858  Hooks::run( 'ChangesListSpecialPageFilters', [ $this, &$this->customFilters ], '1.29' );
859  }
860 
861  return $this->customFilters;
862  }
863 
872  protected function fetchOptionsFromRequest( $opts ) {
873  $opts->fetchValuesFromRequest( $this->getRequest() );
874 
875  return $opts;
876  }
877 
884  public function parseParameters( $par, FormOptions $opts ) {
885  $stringParameterNameSet = [];
886  $hideParameterNameSet = [];
887 
888  // URL parameters can be per-group, like 'userExpLevel',
889  // or per-filter, like 'hideminor'.
890 
891  foreach ( $this->filterGroups as $filterGroup ) {
892  if ( $filterGroup->isPerGroupRequestParameter() ) {
893  $stringParameterNameSet[$filterGroup->getName()] = true;
894  } elseif ( $filterGroup->getType() === ChangesListBooleanFilterGroup::TYPE ) {
895  foreach ( $filterGroup->getFilters() as $filter ) {
896  $hideParameterNameSet[$filter->getName()] = true;
897  }
898  }
899  }
900 
901  $bits = preg_split( '/\s*,\s*/', trim( $par ) );
902  foreach ( $bits as $bit ) {
903  $m = [];
904  if ( isset( $hideParameterNameSet[$bit] ) ) {
905  // hidefoo => hidefoo=true
906  $opts[$bit] = true;
907  } elseif ( isset( $hideParameterNameSet["hide$bit"] ) ) {
908  // foo => hidefoo=false
909  $opts["hide$bit"] = false;
910  } elseif ( preg_match( '/^(.*)=(.*)$/', $bit, $m ) ) {
911  if ( isset( $stringParameterNameSet[$m[1]] ) ) {
912  $opts[$m[1]] = $m[2];
913  }
914  }
915  }
916  }
917 
923  public function validateOptions( FormOptions $opts ) {
924  // nothing by default
925  }
926 
938  protected function buildQuery( &$tables, &$fields, &$conds, &$query_options,
939  &$join_conds, FormOptions $opts ) {
940 
941  $dbr = $this->getDB();
942  $user = $this->getUser();
943 
944  $context = $this->getContext();
945  foreach ( $this->filterGroups as $filterGroup ) {
946  // URL parameters can be per-group, like 'userExpLevel',
947  // or per-filter, like 'hideminor'.
948  if ( $filterGroup->isPerGroupRequestParameter() ) {
949  $filterGroup->modifyQuery( $dbr, $this, $tables, $fields, $conds,
950  $query_options, $join_conds, $opts[$filterGroup->getName()] );
951  } else {
952  foreach ( $filterGroup->getFilters() as $filter ) {
953  if ( $opts[$filter->getName()] ) {
954  $filter->modifyQuery( $dbr, $this, $tables, $fields, $conds,
955  $query_options, $join_conds );
956  }
957  }
958  }
959  }
960 
961  // Namespace filtering
962  if ( $opts['namespace'] !== '' ) {
963  $selectedNS = $dbr->addQuotes( $opts['namespace'] );
964  $operator = $opts['invert'] ? '!=' : '=';
965  $boolean = $opts['invert'] ? 'AND' : 'OR';
966 
967  // Namespace association (T4429)
968  if ( !$opts['associated'] ) {
969  $condition = "rc_namespace $operator $selectedNS";
970  } else {
971  // Also add the associated namespace
972  $associatedNS = $dbr->addQuotes(
973  MWNamespace::getAssociated( $opts['namespace'] )
974  );
975  $condition = "(rc_namespace $operator $selectedNS "
976  . $boolean
977  . " rc_namespace $operator $associatedNS)";
978  }
979 
980  $conds[] = $condition;
981  }
982  }
983 
995  protected function doMainQuery( $tables, $fields, $conds,
996  $query_options, $join_conds, FormOptions $opts ) {
997 
998  $tables[] = 'recentchanges';
999  $fields = array_merge( RecentChange::selectFields(), $fields );
1000 
1002  $tables,
1003  $fields,
1004  $conds,
1005  $join_conds,
1006  $query_options,
1007  ''
1008  );
1009 
1010  // It makes no sense to hide both anons and logged-in users. When this occurs, try a guess on
1011  // what the user meant and either show only bots or force anons to be shown.
1012 
1013  // -------
1014 
1015  // XXX: We're no longer doing this handling. To preserve back-compat, we need to complete
1016  // T151873 (particularly the hideanons/hideliu/hidebots/hidehumans part) in conjunction
1017  // with merging this.
1018 
1019  if ( !$this->runMainQueryHook( $tables, $fields, $conds, $query_options, $join_conds,
1020  $opts )
1021  ) {
1022  return false;
1023  }
1024 
1025  $dbr = $this->getDB();
1026 
1027  return $dbr->select(
1028  $tables,
1029  $fields,
1030  $conds,
1031  __METHOD__,
1032  $query_options,
1033  $join_conds
1034  );
1035  }
1036 
1037  protected function runMainQueryHook( &$tables, &$fields, &$conds,
1038  &$query_options, &$join_conds, $opts
1039  ) {
1040  return Hooks::run(
1041  'ChangesListSpecialPageQuery',
1042  [ $this->getName(), &$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts ]
1043  );
1044  }
1045 
1051  protected function getDB() {
1052  return wfGetDB( DB_REPLICA );
1053  }
1054 
1061  public function webOutput( $rows, $opts ) {
1062  if ( !$this->including() ) {
1063  $this->outputFeedLinks();
1064  $this->doHeader( $opts, $rows->numRows() );
1065  }
1066 
1067  $this->outputChangesList( $rows, $opts );
1068  }
1069 
1073  public function outputFeedLinks() {
1074  // nothing by default
1075  }
1076 
1083  abstract public function outputChangesList( $rows, $opts );
1084 
1091  public function doHeader( $opts, $numRows ) {
1092  $this->setTopText( $opts );
1093 
1094  // @todo Lots of stuff should be done here.
1095 
1096  $this->setBottomText( $opts );
1097  }
1098 
1105  public function setTopText( FormOptions $opts ) {
1106  // nothing by default
1107  }
1108 
1115  public function setBottomText( FormOptions $opts ) {
1116  // nothing by default
1117  }
1118 
1128  public function getExtraOptions( $opts ) {
1129  return [];
1130  }
1131 
1137  public function makeLegend() {
1138  $context = $this->getContext();
1139  $user = $context->getUser();
1140  # The legend showing what the letters and stuff mean
1141  $legend = Html::openElement( 'dl' ) . "\n";
1142  # Iterates through them and gets the messages for both letter and tooltip
1143  $legendItems = $context->getConfig()->get( 'RecentChangesFlags' );
1144  if ( !( $user->useRCPatrol() || $user->useNPPatrol() ) ) {
1145  unset( $legendItems['unpatrolled'] );
1146  }
1147  foreach ( $legendItems as $key => $item ) { # generate items of the legend
1148  $label = isset( $item['legend'] ) ? $item['legend'] : $item['title'];
1149  $letter = $item['letter'];
1150  $cssClass = isset( $item['class'] ) ? $item['class'] : $key;
1151 
1152  $legend .= Html::element( 'dt',
1153  [ 'class' => $cssClass ], $context->msg( $letter )->text()
1154  ) . "\n" .
1155  Html::rawElement( 'dd',
1156  [ 'class' => Sanitizer::escapeClass( 'mw-changeslist-legend-' . $key ) ],
1157  $context->msg( $label )->parse()
1158  ) . "\n";
1159  }
1160  # (+-123)
1161  $legend .= Html::rawElement( 'dt',
1162  [ 'class' => 'mw-plusminus-pos' ],
1163  $context->msg( 'recentchanges-legend-plusminus' )->parse()
1164  ) . "\n";
1165  $legend .= Html::element(
1166  'dd',
1167  [ 'class' => 'mw-changeslist-legend-plusminus' ],
1168  $context->msg( 'recentchanges-label-plusminus' )->text()
1169  ) . "\n";
1170  $legend .= Html::closeElement( 'dl' ) . "\n";
1171 
1172  # Collapsibility
1173  $legend =
1174  '<div class="mw-changeslist-legend">' .
1175  $context->msg( 'recentchanges-legend-heading' )->parse() .
1176  '<div class="mw-collapsible-content">' . $legend . '</div>' .
1177  '</div>';
1178 
1179  return $legend;
1180  }
1181 
1185  protected function addModules() {
1186  $out = $this->getOutput();
1187  // Styles and behavior for the legend box (see makeLegend())
1188  $out->addModuleStyles( [
1189  'mediawiki.special.changeslist.legend',
1190  'mediawiki.special.changeslist',
1191  ] );
1192  $out->addModules( 'mediawiki.special.changeslist.legend.js' );
1193  }
1194 
1195  protected function getGroupName() {
1196  return 'changes';
1197  }
1198 
1213  public function filterOnUserExperienceLevel( $specialPageClassName, $context, $dbr,
1214  &$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedExpLevels ) {
1215 
1216  global $wgLearnerEdits,
1217  $wgExperiencedUserEdits,
1218  $wgLearnerMemberSince,
1219  $wgExperiencedUserMemberSince;
1220 
1221  $LEVEL_COUNT = 3;
1222 
1223  // If all levels are selected, all logged-in users are included (but no
1224  // anons), so we can short-circuit.
1225  if ( count( $selectedExpLevels ) === $LEVEL_COUNT ) {
1226  $conds[] = 'rc_user != 0';
1227  return;
1228  }
1229 
1230  $tables[] = 'user';
1231  $join_conds['user'] = [ 'LEFT JOIN', 'rc_user = user_id' ];
1232 
1233  $now = time();
1234  $secondsPerDay = 86400;
1235  $learnerCutoff = $now - $wgLearnerMemberSince * $secondsPerDay;
1236  $experiencedUserCutoff = $now - $wgExperiencedUserMemberSince * $secondsPerDay;
1237 
1238  $aboveNewcomer = $dbr->makeList(
1239  [
1240  'user_editcount >= ' . intval( $wgLearnerEdits ),
1241  'user_registration <= ' . $dbr->timestamp( $learnerCutoff ),
1242  ],
1244  );
1245 
1246  $aboveLearner = $dbr->makeList(
1247  [
1248  'user_editcount >= ' . intval( $wgExperiencedUserEdits ),
1249  'user_registration <= ' . $dbr->timestamp( $experiencedUserCutoff ),
1250  ],
1252  );
1253 
1254  if ( $selectedExpLevels === [ 'newcomer' ] ) {
1255  $conds[] = "NOT ( $aboveNewcomer )";
1256  } elseif ( $selectedExpLevels === [ 'learner' ] ) {
1257  $conds[] = $dbr->makeList(
1258  [ $aboveNewcomer, "NOT ( $aboveLearner )" ],
1260  );
1261  } elseif ( $selectedExpLevels === [ 'experienced' ] ) {
1262  $conds[] = $aboveLearner;
1263  } elseif ( $selectedExpLevels === [ 'learner', 'newcomer' ] ) {
1264  $conds[] = "NOT ( $aboveLearner )";
1265  } elseif ( $selectedExpLevels === [ 'experienced', 'newcomer' ] ) {
1266  $conds[] = $dbr->makeList(
1267  [ "NOT ( $aboveNewcomer )", $aboveLearner ],
1269  );
1270  } elseif ( $selectedExpLevels === [ 'experienced', 'learner' ] ) {
1271  $conds[] = $aboveNewcomer;
1272  }
1273  }
1274 }
ContextSource\getConfig
getConfig()
Get the Config object.
Definition: ContextSource.php:68
$context
error also a ContextSource you ll probably need to make sure the header is varied on and they can depend only on the ResourceLoaderContext $context
Definition: hooks.txt:2612
ChangesListFilterGroup\getName
getName()
Definition: ChangesListFilterGroup.php:284
ChangesListSpecialPage\getExtraOptions
getExtraOptions( $opts)
Get options to be displayed in a form.
Definition: ChangesListSpecialPage.php:1128
ChangesListSpecialPage\__construct
__construct( $name, $restriction)
Definition: ChangesListSpecialPage.php:77
$tables
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist & $tables
Definition: hooks.txt:990
ContextSource\msg
msg()
Get a Message object with context set Parameters are the same as wfMessage()
Definition: ContextSource.php:187
LinkBatch
Class representing a list of titles The execute() method checks them all for existence and adds them ...
Definition: LinkBatch.php:34
SpecialPage\getOutput
getOutput()
Get the OutputPage being used for this instance.
Definition: SpecialPage.php:675
captcha-old.count
count
Definition: captcha-old.py:225
ChangesListSpecialPage\makeLegend
makeLegend()
Return the legend displayed within the fieldset.
Definition: ChangesListSpecialPage.php:1137
ChangesListSpecialPage\execute
execute( $subpage)
Main execution point.
Definition: ChangesListSpecialPage.php:474
ChangesListSpecialPage\parseParameters
parseParameters( $par, FormOptions $opts)
Process $par and put options found in $opts.
Definition: ChangesListSpecialPage.php:884
ChangesListSpecialPage\doMainQuery
doMainQuery( $tables, $fields, $conds, $query_options, $join_conds, FormOptions $opts)
Process the query.
Definition: ChangesListSpecialPage.php:995
FormOptions\INTNULL
const INTNULL
Integer type or null, maps to WebRequest::getIntOrNull() This is useful for the namespace selector.
Definition: FormOptions.php:54
RC_LOG
const RC_LOG
Definition: Defines.php:142
use
as see the revision history and available at free of to any person obtaining a copy of this software and associated documentation to deal in the Software without including without limitation the rights to use
Definition: MIT-LICENSE.txt:10
ChangesListSpecialPage
Special page which uses a ChangesList to show query results.
Definition: ChangesListSpecialPage.php:33
$user
please add to it if you re going to add events to the MediaWiki code where normally authentication against an external auth plugin would be creating a account $user
Definition: hooks.txt:246
DeferredUpdates\addUpdate
static addUpdate(DeferrableUpdate $update, $stage=self::POSTSEND)
Add an update to the deferred list to be run later by execute()
Definition: DeferredUpdates.php:76
ChangesListBooleanFilter
An individual filter in a boolean group.
Definition: ChangesListBooleanFilter.php:32
$params
$params
Definition: styleTest.css.php:40
ChangesListSpecialPage\filterOnUserExperienceLevel
filterOnUserExperienceLevel( $specialPageClassName, $context, $dbr, &$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedExpLevels)
Filter on users' experience levels; this will not be called if nothing is selected.
Definition: ChangesListSpecialPage.php:1213
RC_EDIT
const RC_EDIT
Definition: Defines.php:140
WANCacheReapUpdate
Class for fixing stale WANObjectCache keys using a purge event source.
Definition: WANCacheReapUpdate.php:24
ChangesListSpecialPage\fetchOptionsFromRequest
fetchOptionsFromRequest( $opts)
Fetch values for a FormOptions object from the WebRequest associated with this instance.
Definition: ChangesListSpecialPage.php:872
wfLogWarning
wfLogWarning( $msg, $callerOffset=1, $level=E_USER_WARNING)
Send a warning as a PHP error and the debug log.
Definition: GlobalFunctions.php:1155
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:304
Wikimedia\Rdbms\ResultWrapper
Result wrapper for grabbing data queried from an IDatabase object.
Definition: ResultWrapper.php:24
RecentChange\SRC_CATEGORIZE
const SRC_CATEGORIZE
Definition: RecentChange.php:70
SpecialPage\getName
getName()
Get the name of this Special Page.
Definition: SpecialPage.php:150
ContextSource\getUser
getUser()
Get the User object.
Definition: ContextSource.php:133
ChangesListFilterGroup
Represents a filter group (used on ChangesListSpecialPage and descendants)
Definition: ChangesListFilterGroup.php:35
RecentChange\SRC_LOG
const SRC_LOG
Definition: RecentChange.php:68
ChangesListSpecialPage\$filterGroups
$filterGroups
Filter groups, and their contained filters This is an associative array (with group name as key) of C...
Definition: ChangesListSpecialPage.php:75
php
injection txt This is an overview of how MediaWiki makes use of dependency injection The design described here grew from the discussion of RFC T384 The term dependency this means that anything an object needs to operate should be injected from the the object itself should only know narrow no concrete implementation of the logic it relies on The requirement to inject everything typically results in an architecture that based on two main types of and essentially stateless service objects that use other service objects to operate on the value objects As of the beginning MediaWiki is only starting to use the DI approach Much of the code still relies on global state or direct resulting in a highly cyclical dependency which acts as the top level factory for services in MediaWiki which can be used to gain access to default instances of various services MediaWikiServices however also allows new services to be defined and default services to be redefined Services are defined or redefined by providing a callback the instantiator that will return a new instance of the service When it will create an instance of MediaWikiServices and populate it with the services defined in the files listed by thereby bootstrapping the DI framework Per $wgServiceWiringFiles lists includes ServiceWiring php
Definition: injection.txt:35
LIST_AND
const LIST_AND
Definition: Defines.php:41
ChangesListSpecialPage\outputNoResults
outputNoResults()
Add the "no results" message to the output.
Definition: ChangesListSpecialPage.php:522
Wikimedia\Rdbms\IDatabase
Basic database interface for live and lazy-loaded relation database handles.
Definition: IDatabase.php:40
ChangesListSpecialPage\doHeader
doHeader( $opts, $numRows)
Set the text to be displayed above the changes.
Definition: ChangesListSpecialPage.php:1091
Html\closeElement
static closeElement( $element)
Returns "</$element>".
Definition: Html.php:309
ChangesListSpecialPage\webOutput
webOutput( $rows, $opts)
Send output to the OutputPage object, only called if not used feeds.
Definition: ChangesListSpecialPage.php:1061
LIST_OR
const LIST_OR
Definition: Defines.php:44
SpecialPage\getConfig
getConfig()
Shortcut to get main config object.
Definition: SpecialPage.php:714
$title
namespace and then decline to actually register it file or subcat img or subcat $title
Definition: hooks.txt:934
ChangesListSpecialPage\$filterGroupDefinitions
$filterGroupDefinitions
Definition information for the filters and their groups.
Definition: ChangesListSpecialPage.php:60
ChangeTags\modifyDisplayQuery
static modifyDisplayQuery(&$tables, &$fields, &$conds, &$join_conds, &$options, $filter_tag=false)
Applies all tags-related changes to a query.
Definition: ChangeTags.php:632
ChangesListSpecialPage\$reviewStatusFilterGroupDefinition
$reviewStatusFilterGroupDefinition
Definition: ChangesListSpecialPage.php:64
LogFormatter\newFromRow
static newFromRow( $row)
Handy shortcut for constructing a formatter directly from database row.
Definition: LogFormatter.php:74
ChangesListSpecialPage\getRows
getRows()
Get the database result for this special page instance.
Definition: ChangesListSpecialPage.php:535
ChangesListSpecialPage\outputFeedLinks
outputFeedLinks()
Output feed links.
Definition: ChangesListSpecialPage.php:1073
wfGetDB
wfGetDB( $db, $groups=[], $wiki=false)
Get a Database object.
Definition: GlobalFunctions.php:3060
ChangesListSpecialPage\runMainQueryHook
runMainQueryHook(&$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts)
Definition: ChangesListSpecialPage.php:1037
ChangesListSpecialPage\$hideCategorizationFilterDefinition
$hideCategorizationFilterDefinition
Definition: ChangesListSpecialPage.php:67
ChangesListSpecialPage\getStructuredFilterJsData
getStructuredFilterJsData()
Gets structured filter information needed by JS.
Definition: ChangesListSpecialPage.php:821
ChangesListSpecialPage\outputChangesList
outputChangesList( $rows, $opts)
Build and output the actual changes list.
ChangesListSpecialPage\getFilterGroups
getFilterGroups()
Gets the currently registered filters groups.
Definition: ChangesListSpecialPage.php:792
$output
this hook is for auditing only RecentChangesLinked and Watchlist RecentChangesLinked and Watchlist Do not use this to implement individual filters if they are compatible with the ChangesListFilter and ChangesListFilterGroup structure use sub classes of those in conjunction with the ChangesListSpecialPageStructuredFilters hook This hook can be used to implement filters that do not implement that or custom behavior that is not an individual filter e g Watchlist and Watchlist you will want to construct new ChangesListBooleanFilter or ChangesListStringOptionsFilter objects When constructing you specify which group they belong to You can reuse existing or create your you must register them with $special registerFilterGroup removed from all revisions and log entries to which it was applied This gives extensions a chance to take it off their books as the deletion has already been partly carried out by this point or something similar the user will be unable to create the tag set and then return false from the hook function Ensure you consume the ChangeTagAfterDelete hook to carry out custom deletion actions as context called by AbstractContent::getParserOutput May be used to override the normal model specific rendering of page content as context as context the output can only depend on parameters provided to this hook not on global state indicating whether full HTML should be generated If generation of HTML may be but other information should still be present in the ParserOutput object & $output
Definition: hooks.txt:1049
SpecialPage\setHeaders
setHeaders()
Sets headers - this should be called from the execute() method of all derived classes!
Definition: SpecialPage.php:484
SpecialPage\getUser
getUser()
Shortcut to get the User executing this instance.
Definition: SpecialPage.php:685
RecentChange\SRC_EDIT
const SRC_EDIT
Definition: RecentChange.php:66
global
when a variable name is used in a it is silently declared as a new masking the global
Definition: design.txt:93
DB_REPLICA
const DB_REPLICA
Definition: defines.php:25
RecentChange\SRC_NEW
const SRC_NEW
Definition: RecentChange.php:67
ChangesListSpecialPage\buildQuery
buildQuery(&$tables, &$fields, &$conds, &$query_options, &$join_conds, FormOptions $opts)
Sets appropriate tables, fields, conditions, etc.
Definition: ChangesListSpecialPage.php:938
SpecialPage\getContext
getContext()
Gets the context this SpecialPage is executed in.
Definition: SpecialPage.php:648
ChangesListSpecialPage\$rcOptions
FormOptions $rcOptions
Definition: ChangesListSpecialPage.php:38
NS_USER_TALK
const NS_USER_TALK
Definition: Defines.php:65
SpecialPage\msg
msg()
Wrapper around wfMessage that sets the current context.
Definition: SpecialPage.php:746
ChangesListSpecialPage\registerFilters
registerFilters()
Register all filters and their groups (including those from hooks), plus handle conflicts and default...
Definition: ChangesListSpecialPage.php:570
SpecialPage
Parent class for all special pages.
Definition: SpecialPage.php:36
ChangesListSpecialPage\setup
setup( $parameters)
Register all the filters, including legacy hook-driven ones.
Definition: ChangesListSpecialPage.php:725
ChangesListSpecialPage\getFilterGroup
getFilterGroup( $groupName)
Gets a specified ChangesListFilterGroup by name.
Definition: ChangesListSpecialPage.php:803
SpecialPage\getRequest
getRequest()
Get the WebRequest being used for this instance.
Definition: SpecialPage.php:665
ChangesListStringOptionsFilterGroup\NONE
const NONE
Signifies that no options in the group are selected, meaning the group has no effect.
Definition: ChangesListStringOptionsFilterGroup.php:61
RC_NEW
const RC_NEW
Definition: Defines.php:141
ChangesListSpecialPage\getCustomFilters
getCustomFilters()
Get custom show/hide filters using deprecated ChangesListSpecialPageFilters hook.
Definition: ChangesListSpecialPage.php:855
ChangesListSpecialPage\addModules
addModules()
Add page-specific modules.
Definition: ChangesListSpecialPage.php:1185
ChangesListSpecialPage\getGroupName
getGroupName()
Under which header this special page is listed in Special:SpecialPages See messages 'specialpages-gro...
Definition: ChangesListSpecialPage.php:1195
ChangesListSpecialPage\areFiltersInConflict
areFiltersInConflict()
Check if filters are in conflict and guaranteed to return no results.
Definition: ChangesListSpecialPage.php:430
ChangesListSpecialPage\getDefaultOptions
getDefaultOptions()
Get a FormOptions object containing the default options.
Definition: ChangesListSpecialPage.php:751
ChangesListSpecialPage\transformFilterDefinition
transformFilterDefinition(array $filterDefinition)
Transforms filter definition to prepare it for constructor.
Definition: ChangesListSpecialPage.php:656
RecentChange\selectFields
static selectFields()
Return the list of recentchanges fields that should be selected to create a new recentchanges object.
Definition: RecentChange.php:204
$dbr
if(! $regexes) $dbr
Definition: cleanup.php:94
ChangesListSpecialPage\getDB
getDB()
Return a IDatabase object for reading.
Definition: ChangesListSpecialPage.php:1051
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
Html\openElement
static openElement( $element, $attribs=[])
Identical to rawElement(), but has no third parameter and omits the end tag (and the self-closing '/'...
Definition: Html.php:251
Html\rawElement
static rawElement( $element, $attribs=[], $contents='')
Returns an HTML element in a string.
Definition: Html.php:209
NS_USER
const NS_USER
Definition: Defines.php:64
LoggerFactory
MediaWiki Logger LoggerFactory implements a PSR[0] compatible message logging system Named Psr Log LoggerInterface instances can be obtained from the MediaWiki Logger LoggerFactory::getInstance() static method. MediaWiki\Logger\LoggerFactory expects a class implementing the MediaWiki\Logger\Spi interface to act as a factory for new Psr\Log\LoggerInterface instances. The "Spi" in MediaWiki\Logger\Spi stands for "service provider interface". An SPI is an API intended to be implemented or extended by a third party. This software design pattern is intended to enable framework extension and replaceable components. It is specifically used in the MediaWiki\Logger\LoggerFactory service to allow alternate PSR-3 logging implementations to be easily integrated with MediaWiki. The service provider interface allows the backend logging library to be implemented in multiple ways. The $wgMWLoggerDefaultSpi global provides the classname of the default MediaWiki\Logger\Spi implementation to be loaded at runtime. This can either be the name of a class implementing the MediaWiki\Logger\Spi with a zero argument const ructor or a callable that will return an MediaWiki\Logger\Spi instance. Alternately the MediaWiki\Logger\LoggerFactory MediaWiki Logger LoggerFactory
Definition: logger.txt:5
ChangesListSpecialPage\$rcSubpage
string $rcSubpage
Definition: ChangesListSpecialPage.php:35
$batch
$batch
Definition: linkcache.txt:23
of
globals txt Globals are evil The original MediaWiki code relied on globals for processing context far too often MediaWiki development since then has been a story of slowly moving context out of global variables and into objects Storing processing context in object member variables allows those objects to be reused in a much more flexible way Consider the elegance of
Definition: globals.txt:10
class
you have access to all of the normal MediaWiki so you can get a DB use the etc For full docs on the Maintenance class
Definition: maintenance.txt:52
RC_CATEGORIZE
const RC_CATEGORIZE
Definition: Defines.php:144
FormOptions
Helper class to keep track of options when mixing links and form elements.
Definition: FormOptions.php:35
ChangesListSpecialPage\registerFiltersFromDefinitions
registerFiltersFromDefinitions(array $definition)
Register filters from a definition object.
Definition: ChangesListSpecialPage.php:668
Html\element
static element( $element, $attribs=[], $contents='')
Identical to rawElement(), but HTML-escapes $contents (like Xml::element()).
Definition: Html.php:231
ChangesListBooleanFilterGroup\TYPE
const TYPE
Type marker, used by JavaScript.
Definition: ChangesListBooleanFilterGroup.php:14
ChangesListSpecialPage\getFilterGroupDefinitionFromLegacyCustomFilters
getFilterGroupDefinitionFromLegacyCustomFilters( $customFilters)
Get filter group definition from legacy custom filters.
Definition: ChangesListSpecialPage.php:697
Hooks\run
static run( $event, array $args=[], $deprecatedVersion=null)
Call hook functions defined in Hooks::register and $wgHooks.
Definition: Hooks.php:131
ChangesListSpecialPage\setBottomText
setBottomText(FormOptions $opts)
Send the text to be displayed after the options.
Definition: ChangesListSpecialPage.php:1115
ChangesListSpecialPage\validateOptions
validateOptions(FormOptions $opts)
Validate a FormOptions object generated by getDefaultOptions() with values already populated.
Definition: ChangesListSpecialPage.php:923
ChangesListSpecialPage\registerFilterGroup
registerFilterGroup(ChangesListFilterGroup $group)
Register a structured changes list filter group.
Definition: ChangesListSpecialPage.php:781
ChangesListSpecialPage\getOptions
getOptions()
Get the current FormOptions for this request.
Definition: ChangesListSpecialPage.php:553
MWNamespace\getAssociated
static getAssociated( $index)
Get the associated namespace.
Definition: MWNamespace.php:140
SpecialPage\outputHeader
outputHeader( $summaryMessageKey='')
Outputs a summary message on top of special pages Per default the message key is the canonical name o...
Definition: SpecialPage.php:583
SpecialPage\including
including( $x=null)
Whether the special page is being evaluated via transclusion.
Definition: SpecialPage.php:226
ChangesListSpecialPage\$customFilters
array $customFilters
Definition: ChangesListSpecialPage.php:41
array
the array() calling protocol came about after MediaWiki 1.4rc1.
ChangesListSpecialPage\setTopText
setTopText(FormOptions $opts)
Send the text to be displayed before the options.
Definition: ChangesListSpecialPage.php:1105
$out
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output $out
Definition: hooks.txt:783