85 parent::__construct(
$name, $restriction );
87 $this->filterGroupDefinitions = [
89 'name' =>
'registration',
90 'title' =>
'rcfilters-filtergroup-registration',
97 'showHideSuffix' =>
'showhideliu',
99 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
100 &$query_options, &$join_conds
102 $conds[] =
'rc_user = 0';
104 'isReplacedInStructuredUi' =>
true,
108 'name' =>
'hideanons',
111 'showHideSuffix' =>
'showhideanons',
113 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
114 &$query_options, &$join_conds
116 $conds[] =
'rc_user != 0';
118 'isReplacedInStructuredUi' =>
true,
124 'name' =>
'userExpLevel',
125 'title' =>
'rcfilters-filtergroup-userExpLevel',
127 'isFullCoverage' =>
true,
130 'name' =>
'unregistered',
131 'label' =>
'rcfilters-filter-user-experience-level-unregistered-label',
132 'description' =>
'rcfilters-filter-user-experience-level-unregistered-description',
133 'cssClassSuffix' =>
'user-unregistered',
134 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
135 return !$rc->getAttribute(
'rc_user' );
139 'name' =>
'registered',
140 'label' =>
'rcfilters-filter-user-experience-level-registered-label',
141 'description' =>
'rcfilters-filter-user-experience-level-registered-description',
142 'cssClassSuffix' =>
'user-registered',
143 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
144 return $rc->getAttribute(
'rc_user' );
148 'name' =>
'newcomer',
149 'label' =>
'rcfilters-filter-user-experience-level-newcomer-label',
150 'description' =>
'rcfilters-filter-user-experience-level-newcomer-description',
151 'cssClassSuffix' =>
'user-newcomer',
152 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
153 $performer = $rc->getPerformer();
154 return $performer && $performer->isLoggedIn() &&
155 $performer->getExperienceLevel() ===
'newcomer';
160 'label' =>
'rcfilters-filter-user-experience-level-learner-label',
161 'description' =>
'rcfilters-filter-user-experience-level-learner-description',
162 'cssClassSuffix' =>
'user-learner',
163 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
164 $performer = $rc->getPerformer();
165 return $performer && $performer->isLoggedIn() &&
166 $performer->getExperienceLevel() ===
'learner';
170 'name' =>
'experienced',
171 'label' =>
'rcfilters-filter-user-experience-level-experienced-label',
172 'description' =>
'rcfilters-filter-user-experience-level-experienced-description',
173 'cssClassSuffix' =>
'user-experienced',
174 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
175 $performer = $rc->getPerformer();
176 return $performer && $performer->isLoggedIn() &&
177 $performer->getExperienceLevel() ===
'experienced';
182 'queryCallable' => [ $this,
'filterOnUserExperienceLevel' ],
186 'name' =>
'authorship',
187 'title' =>
'rcfilters-filtergroup-authorship',
191 'name' =>
'hidemyself',
192 'label' =>
'rcfilters-filter-editsbyself-label',
193 'description' =>
'rcfilters-filter-editsbyself-description',
196 'showHideSuffix' =>
'showhidemine',
198 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
199 &$query_options, &$join_conds
201 $user = $ctx->getUser();
202 $conds[] =
'rc_user_text != ' .
$dbr->addQuotes(
$user->getName() );
204 'cssClassSuffix' =>
'self',
205 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
206 return $ctx->getUser()->equals( $rc->getPerformer() );
210 'name' =>
'hidebyothers',
211 'label' =>
'rcfilters-filter-editsbyother-label',
212 'description' =>
'rcfilters-filter-editsbyother-description',
214 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
215 &$query_options, &$join_conds
217 $user = $ctx->getUser();
218 $conds[] =
'rc_user_text = ' .
$dbr->addQuotes(
$user->getName() );
220 'cssClassSuffix' =>
'others',
221 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
222 return !$ctx->getUser()->equals( $rc->getPerformer() );
229 'name' =>
'automated',
230 'title' =>
'rcfilters-filtergroup-automated',
234 'name' =>
'hidebots',
235 'label' =>
'rcfilters-filter-bots-label',
236 'description' =>
'rcfilters-filter-bots-description',
239 'showHideSuffix' =>
'showhidebots',
241 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
242 &$query_options, &$join_conds
244 $conds[] =
'rc_bot = 0';
246 'cssClassSuffix' =>
'bot',
247 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
248 return $rc->getAttribute(
'rc_bot' );
252 'name' =>
'hidehumans',
253 'label' =>
'rcfilters-filter-humans-label',
254 'description' =>
'rcfilters-filter-humans-description',
256 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
257 &$query_options, &$join_conds
259 $conds[] =
'rc_bot = 1';
261 'cssClassSuffix' =>
'human',
262 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
263 return !$rc->getAttribute(
'rc_bot' );
272 'name' =>
'significance',
273 'title' =>
'rcfilters-filtergroup-significance',
278 'name' =>
'hideminor',
279 'label' =>
'rcfilters-filter-minor-label',
280 'description' =>
'rcfilters-filter-minor-description',
283 'showHideSuffix' =>
'showhideminor',
285 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
286 &$query_options, &$join_conds
288 $conds[] =
'rc_minor = 0';
290 'cssClassSuffix' =>
'minor',
291 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
292 return $rc->getAttribute(
'rc_minor' );
296 'name' =>
'hidemajor',
297 'label' =>
'rcfilters-filter-major-label',
298 'description' =>
'rcfilters-filter-major-description',
300 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
301 &$query_options, &$join_conds
303 $conds[] =
'rc_minor = 1';
305 'cssClassSuffix' =>
'major',
306 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
307 return !$rc->getAttribute(
'rc_minor' );
314 'name' =>
'lastRevision',
315 'title' =>
'rcfilters-filtergroup-lastRevision',
320 'name' =>
'hidelastrevision',
321 'label' =>
'rcfilters-filter-lastrevision-label',
322 'description' =>
'rcfilters-filter-lastrevision-description',
324 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
325 &$query_options, &$join_conds ) {
326 $conds[] =
'rc_this_oldid <> page_latest';
328 'cssClassSuffix' =>
'last',
329 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
330 return $rc->getAttribute(
'rc_this_oldid' ) === $rc->getAttribute(
'page_latest' );
334 'name' =>
'hidepreviousrevisions',
335 'label' =>
'rcfilters-filter-previousrevision-label',
336 'description' =>
'rcfilters-filter-previousrevision-description',
338 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
339 &$query_options, &$join_conds ) {
340 $conds[] =
'rc_this_oldid = page_latest';
342 'cssClassSuffix' =>
'previous',
343 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
344 return $rc->getAttribute(
'rc_this_oldid' ) !== $rc->getAttribute(
'page_latest' );
352 'name' =>
'changeType',
353 'title' =>
'rcfilters-filtergroup-changetype',
358 'name' =>
'hidepageedits',
359 'label' =>
'rcfilters-filter-pageedits-label',
360 'description' =>
'rcfilters-filter-pageedits-description',
363 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
364 &$query_options, &$join_conds
366 $conds[] =
'rc_type != ' .
$dbr->addQuotes(
RC_EDIT );
368 'cssClassSuffix' =>
'src-mw-edit',
369 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
374 'name' =>
'hidenewpages',
375 'label' =>
'rcfilters-filter-newpages-label',
376 'description' =>
'rcfilters-filter-newpages-description',
379 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
380 &$query_options, &$join_conds
382 $conds[] =
'rc_type != ' .
$dbr->addQuotes(
RC_NEW );
384 'cssClassSuffix' =>
'src-mw-new',
385 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
394 'label' =>
'rcfilters-filter-logactions-label',
395 'description' =>
'rcfilters-filter-logactions-description',
398 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
399 &$query_options, &$join_conds
401 $conds[] =
'rc_type != ' .
$dbr->addQuotes(
RC_LOG );
403 'cssClassSuffix' =>
'src-mw-log',
404 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
413 $this->reviewStatusFilterGroupDefinition = [
415 'name' =>
'reviewStatus',
416 'title' =>
'rcfilters-filtergroup-reviewstatus',
421 'name' =>
'hidepatrolled',
422 'label' =>
'rcfilters-filter-patrolled-label',
423 'description' =>
'rcfilters-filter-patrolled-description',
426 'showHideSuffix' =>
'showhidepatr',
428 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
429 &$query_options, &$join_conds
431 $conds[] =
'rc_patrolled = 0';
433 'cssClassSuffix' =>
'patrolled',
434 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
435 return $rc->getAttribute(
'rc_patrolled' );
439 'name' =>
'hideunpatrolled',
440 'label' =>
'rcfilters-filter-unpatrolled-label',
441 'description' =>
'rcfilters-filter-unpatrolled-description',
443 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
444 &$query_options, &$join_conds
446 $conds[] =
'rc_patrolled = 1';
448 'cssClassSuffix' =>
'unpatrolled',
449 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
450 return !$rc->getAttribute(
'rc_patrolled' );
457 $this->hideCategorizationFilterDefinition = [
458 'name' =>
'hidecategorization',
459 'label' =>
'rcfilters-filter-categorization-label',
460 'description' =>
'rcfilters-filter-categorization-description',
463 'showHideSuffix' =>
'showhidecategorization',
466 'queryCallable' =>
function ( $specialClassName, $ctx,
$dbr, &
$tables, &$fields, &$conds,
467 &$query_options, &$join_conds
471 'cssClassSuffix' =>
'src-mw-categorize',
472 'isRowApplicableCallable' =>
function ( $ctx, $rc ) {
487 if ( $group->getConflictingGroups() ) {
490 " specifies conflicts with other groups but these are not supported yet."
495 foreach ( $group->getConflictingFilters()
as $conflictingFilter ) {
496 if ( $conflictingFilter->activelyInConflictWithGroup( $group, $opts ) ) {
502 foreach ( $group->getFilters()
as $filter ) {
504 foreach ( $filter->getConflictingFilters()
as $conflictingFilter ) {
506 $conflictingFilter->activelyInConflictWithFilter( $filter, $opts ) &&
507 $filter->activelyInConflictWithFilter( $conflictingFilter, $opts )
526 $this->rcSubpage = $subpage;
530 if (
$rows ===
false ) {
535 if ( $this->
getRequest()->getVal(
'action' ) ===
'render' ) {
536 $this->
getOutput()->setArticleBodyOnly(
true );
541 if ( $this->
getRequest()->getBool(
'peek' ) ) {
551 $batch->add( $row->rc_namespace, $row->rc_title );
554 foreach ( $formatter->getPreloadTitles()
as $title ) {
568 if ( $this->
getConfig()->
get(
'EnableWANCacheReaper' ) ) {
572 LoggerFactory::getInstance(
'objectcache' )
591 foreach ( $jsData[
'messageKeys']
as $key ) {
595 $out->addBodyClasses(
'mw-rcfilters-enabled' );
598 ResourceLoader::makeInlineScript(
599 ResourceLoader::makeMessageSetScript(
$messages )
603 $experimentalStructuredChangeFilters =
604 $this->
getConfig()->get(
'StructuredChangeFiltersEnableExperimentalViews' );
606 $out->addJsConfigVars(
'wgStructuredChangeFilters', $jsData[
'groups'] );
607 $out->addJsConfigVars(
608 'wgStructuredChangeFiltersEnableExperimentalViews',
609 $experimentalStructuredChangeFilters
612 $out->addJsConfigVars(
613 'wgRCFiltersChangeTags',
616 $out->addJsConfigVars(
617 'StructuredChangeFiltersDisplayConfig',
619 'maxDays' => (
int)$this->
getConfig()->
get(
'RCMaxAge' ) / ( 24 * 3600 ),
620 'limitArray' => $this->
getConfig()->
get(
'RCLinkLimits' ),
622 'daysArray' => $this->
getConfig()->
get(
'RCLinkDays' ),
627 if ( static::$savedQueriesPreferenceName ) {
629 $this->
getUser()->getOption( static::$savedQueriesPreferenceName )
631 if ( $savedQueries && isset( $savedQueries->default ) ) {
634 $out->addBodyClasses(
'mw-rcfilters-ui-loading' );
636 $out->addJsConfigVars(
637 'wgStructuredChangeFiltersSavedQueriesPreferenceName',
638 static::$savedQueriesPreferenceName
642 $out->addBodyClasses(
'mw-rcfilters-disabled' );
663 $tagHitCounts = array_merge( $explicitlyDefinedTags, $softwareActivatedTags );
667 foreach ( $tagHitCounts
as $tagName => $hits ) {
670 isset( $explicitlyDefinedTags[ $tagName ] ) ||
671 isset( $softwareActivatedTags[ $tagName ] )
678 'label' => Sanitizer::stripAllTags(
681 'description' => $desc ? Sanitizer::stripAllTags( $desc->parse() ) :
'',
682 'cssClass' => Sanitizer::escapeClass(
'mw-tag-' . $tagName ),
689 usort(
$result,
function ( $a, $b ) {
690 return strcasecmp( $a[
'label'], $b[
'label'] );
701 '<div class="mw-changeslist-empty">' .
702 $this->
msg(
'recentchanges-noresult' )->parse() .
720 $this->
buildQuery(
$tables, $fields, $conds, $query_options, $join_conds, $opts );
722 return $this->
doMainQuery(
$tables, $fields, $conds, $query_options, $join_conds, $opts );
731 if ( $this->rcOptions ===
null ) {
732 $this->rcOptions = $this->
setup( $this->rcSubpage );
759 if ( $this->
getConfig()->
get(
'RCWatchCategoryMembership' ) ) {
761 $this->hideCategorizationFilterDefinition
764 $transformedHideCategorizationDef[
'group'] = $changeTypeGroup;
767 $transformedHideCategorizationDef
771 Hooks::run(
'ChangesListSpecialPageStructuredFilters', [ $this ] );
773 $unstructuredGroupDefinition =
780 $registered = $userExperienceLevel->getFilter(
'registered' );
781 $registered->setAsSupersetOf( $userExperienceLevel->getFilter(
'newcomer' ) );
782 $registered->setAsSupersetOf( $userExperienceLevel->getFilter(
'learner' ) );
783 $registered->setAsSupersetOf( $userExperienceLevel->getFilter(
'experienced' ) );
785 $categoryFilter = $changeTypeGroup->getFilter(
'hidecategorization' );
786 $logactionsFilter = $changeTypeGroup->getFilter(
'hidelog' );
787 $pagecreationFilter = $changeTypeGroup->getFilter(
'hidenewpages' );
790 $hideMinorFilter = $significanceTypeGroup->getFilter(
'hideminor' );
793 if ( $categoryFilter !==
null ) {
794 $hideMinorFilter->conflictsWith(
796 'rcfilters-hideminor-conflicts-typeofchange-global',
797 'rcfilters-hideminor-conflicts-typeofchange',
798 'rcfilters-typeofchange-conflicts-hideminor'
801 $hideMinorFilter->conflictsWith(
803 'rcfilters-hideminor-conflicts-typeofchange-global',
804 'rcfilters-hideminor-conflicts-typeofchange',
805 'rcfilters-typeofchange-conflicts-hideminor'
807 $hideMinorFilter->conflictsWith(
809 'rcfilters-hideminor-conflicts-typeofchange-global',
810 'rcfilters-hideminor-conflicts-typeofchange',
811 'rcfilters-typeofchange-conflicts-hideminor'
825 return $filterDefinition;
838 $autoFillPriority = -1;
839 foreach ( $definition
as $groupDefinition ) {
840 if ( !isset( $groupDefinition[
'priority'] ) ) {
841 $groupDefinition[
'priority'] = $autoFillPriority;
844 $autoFillPriority = $groupDefinition[
'priority'];
849 $className = $groupDefinition[
'class'];
850 unset( $groupDefinition[
'class'] );
852 foreach ( $groupDefinition[
'filters']
as &$filterDefinition ) {
868 $unstructuredGroupDefinition = [
869 'name' =>
'unstructured',
876 $unstructuredGroupDefinition[
'filters'][] = [
879 'default' =>
$params[
'default'],
883 return $unstructuredGroupDefinition;
894 public function setup( $parameters ) {
902 if ( $parameters !==
null ) {
924 $useDefaults = $this->
getRequest()->getInt(
'urlversion' ) !== 2;
928 foreach ( $this->filterGroups
as $filterGroup ) {
931 if ( $filterGroup->isPerGroupRequestParameter() ) {
932 $opts->add( $filterGroup->getName(), $useDefaults ? $filterGroup->getDefault() :
'' );
935 foreach ( $filterGroup->getFilters()
as $filter ) {
936 $opts->add( $filter->getName(), $useDefaults ? $filter->getDefault( $structuredUI ) :
false );
942 $opts->add(
'invert',
false );
943 $opts->add(
'associated',
false );
944 $opts->add(
'urlversion', 1 );
945 $opts->add(
'tagfilter',
'' );
956 $groupName = $group->
getName();
958 $this->filterGroups[$groupName] = $group;
978 return isset( $this->filterGroups[$groupName] ) ?
979 $this->filterGroups[$groupName] :
1001 usort( $this->filterGroups,
function ( $a, $b ) {
1002 return $b->getPriority() - $a->getPriority();
1005 foreach ( $this->filterGroups
as $groupName => $group ) {
1006 $groupOutput = $group->getJsData( $this );
1007 if ( $groupOutput !==
null ) {
1008 $output[
'messageKeys'] = array_merge(
1010 $groupOutput[
'messageKeys']
1013 unset( $groupOutput[
'messageKeys'] );
1014 $output[
'groups'][] = $groupOutput;
1028 if ( $this->customFilters ===
null ) {
1029 $this->customFilters = [];
1030 Hooks::run(
'ChangesListSpecialPageFilters', [ $this, &$this->customFilters ],
'1.29' );
1045 $opts->fetchValuesFromRequest( $this->
getRequest() );
1057 $stringParameterNameSet = [];
1058 $hideParameterNameSet = [];
1063 foreach ( $this->filterGroups
as $filterGroup ) {
1064 if ( $filterGroup->isPerGroupRequestParameter() ) {
1065 $stringParameterNameSet[$filterGroup->getName()] =
true;
1067 foreach ( $filterGroup->getFilters()
as $filter ) {
1068 $hideParameterNameSet[$filter->getName()] =
true;
1073 $bits = preg_split(
'/\s*,\s*/', trim( $par ) );
1074 foreach ( $bits
as $bit ) {
1076 if ( isset( $hideParameterNameSet[$bit] ) ) {
1079 } elseif ( isset( $hideParameterNameSet[
"hide$bit"] ) ) {
1081 $opts[
"hide$bit"] =
false;
1082 } elseif ( preg_match(
'/^(.*)=(.*)$/', $bit, $m ) ) {
1083 if ( isset( $stringParameterNameSet[$m[1]] ) ) {
1084 $opts[$m[1]] = $m[2];
1111 foreach ( $this->filterGroups
as $filterGroup ) {
1113 $filters = $filterGroup->getFilters();
1115 if (
count( $filters ) === 1 ) {
1120 $allInGroupEnabled = array_reduce(
1122 function ( $carry, $filter )
use ( $opts ) {
1123 return $carry && $opts[ $filter->getName() ];
1125 count( $filters ) > 0
1128 if ( $allInGroupEnabled ) {
1129 foreach ( $filters
as $filter ) {
1130 $opts[ $filter->getName() ] =
false;
1151 if ( $opts[
'hideanons'] && $opts[
'hideliu'] ) {
1152 $opts->
reset(
'hideanons' );
1153 if ( !$opts[
'hidebots'] ) {
1154 $opts->
reset(
'hideliu' );
1155 $opts[
'hidehumans'] = 1;
1174 if (
$value ===
false ) {
1199 foreach ( $this->filterGroups
as $filterGroup ) {
1202 if ( $filterGroup->isPerGroupRequestParameter() ) {
1203 $filterGroup->modifyQuery(
$dbr, $this,
$tables, $fields, $conds,
1204 $query_options, $join_conds, $opts[$filterGroup->getName()] );
1206 foreach ( $filterGroup->getFilters()
as $filter ) {
1207 if ( $filter->isActive( $opts, $isStructuredUI ) ) {
1208 $filter->modifyQuery(
$dbr, $this,
$tables, $fields, $conds,
1209 $query_options, $join_conds );
1216 if ( $opts[
'namespace' ] !==
'' ) {
1217 $namespaces = explode(
';', $opts[
'namespace' ] );
1219 if ( $opts[
'associated' ] ) {
1220 $associatedNamespaces = array_map(
1230 $operator = $opts[
'invert' ] ?
'!=' :
'=';
1233 $operator = $opts[
'invert' ] ?
'NOT IN' :
'IN';
1237 $conds[] =
"rc_namespace $operator $value";
1275 return $dbr->select(
1286 &$query_options, &$join_conds, $opts
1289 'ChangesListSpecialPageQuery',
1290 [ $this->
getName(), &
$tables, &$fields, &$conds, &$query_options, &$join_conds, $opts ]
1388 # The legend showing what the letters and stuff mean
1390 # Iterates through them and gets the messages for both letter and tooltip
1391 $legendItems =
$context->getConfig()->get(
'RecentChangesFlags' );
1392 if ( !(
$user->useRCPatrol() ||
$user->useNPPatrol() ) ) {
1393 unset( $legendItems[
'unpatrolled'] );
1395 foreach ( $legendItems
as $key => $item ) { # generate items
of the legend
1396 $label = isset( $item[
'legend'] ) ? $item[
'legend'] : $item[
'title'];
1397 $letter = $item[
'letter'];
1398 $cssClass = isset( $item[
'class'] ) ? $item[
'class'] : $key;
1401 [
'class' => $cssClass ],
$context->msg( $letter )->text()
1404 [
'class' => Sanitizer::escapeClass(
'mw-changeslist-legend-' . $key ) ],
1410 [
'class' =>
'mw-plusminus-pos' ],
1411 $context->msg(
'recentchanges-legend-plusminus' )->parse()
1415 [
'class' =>
'mw-changeslist-legend-plusminus' ],
1416 $context->msg(
'recentchanges-label-plusminus' )->text()
1421 $context->msg(
'rcfilters-legend-heading' )->parse() :
1422 $context->msg(
'recentchanges-legend-heading' )->parse();
1426 '<div class="mw-changeslist-legend">' .
1428 '<div class="mw-collapsible-content">' . $legend .
'</div>' .
1440 $out->addModuleStyles( [
1441 'mediawiki.special.changeslist.legend',
1442 'mediawiki.special.changeslist',
1444 $out->addModules(
'mediawiki.special.changeslist.legend.js' );
1447 $out->addModules(
'mediawiki.rcfilters.filters.ui' );
1448 $out->addModuleStyles(
'mediawiki.rcfilters.filters.base.styles' );
1473 &
$tables, &$fields, &$conds, &$query_options, &$join_conds, $selectedExpLevels, $now = 0
1483 if (
count( $selectedExpLevels ) === $LEVEL_COUNT ) {
1489 in_array(
'registered', $selectedExpLevels ) &&
1490 in_array(
'unregistered', $selectedExpLevels )
1497 in_array(
'registered', $selectedExpLevels ) &&
1498 !in_array(
'unregistered', $selectedExpLevels )
1500 $conds[] =
'rc_user != 0';
1504 if ( $selectedExpLevels === [
'unregistered' ] ) {
1505 $conds[] =
'rc_user = 0';
1510 $join_conds[
'user'] = [
'LEFT JOIN',
'rc_user = user_id' ];
1515 $secondsPerDay = 86400;
1519 $aboveNewcomer =
$dbr->makeList(
1522 'user_registration <= ' .
$dbr->addQuotes(
$dbr->timestamp( $learnerCutoff ) ),
1527 $aboveLearner =
$dbr->makeList(
1530 'user_registration <= ' .
1531 $dbr->addQuotes(
$dbr->timestamp( $experiencedUserCutoff ) ),
1538 if ( in_array(
'unregistered', $selectedExpLevels ) ) {
1539 $selectedExpLevels = array_diff( $selectedExpLevels, [
'unregistered' ] );
1540 $conditions[] =
'rc_user = 0';
1543 if ( $selectedExpLevels === [
'newcomer' ] ) {
1544 $conditions[] =
"NOT ( $aboveNewcomer )";
1545 } elseif ( $selectedExpLevels === [
'learner' ] ) {
1546 $conditions[] =
$dbr->makeList(
1547 [ $aboveNewcomer,
"NOT ( $aboveLearner )" ],
1550 } elseif ( $selectedExpLevels === [
'experienced' ] ) {
1551 $conditions[] = $aboveLearner;
1552 } elseif ( $selectedExpLevels === [
'learner',
'newcomer' ] ) {
1553 $conditions[] =
"NOT ( $aboveLearner )";
1554 } elseif ( $selectedExpLevels === [
'experienced',
'newcomer' ] ) {
1555 $conditions[] =
$dbr->makeList(
1556 [
"NOT ( $aboveNewcomer )", $aboveLearner ],
1559 } elseif ( $selectedExpLevels === [
'experienced',
'learner' ] ) {
1560 $conditions[] = $aboveNewcomer;
1561 } elseif ( $selectedExpLevels === [
'experienced',
'learner',
'newcomer' ] ) {
1562 $conditions[] =
'rc_user != 0';
1565 if (
count( $conditions ) > 1 ) {
1567 } elseif (
count( $conditions ) === 1 ) {
1568 $conds[] = reset( $conditions );
1578 if ( $this->
getRequest()->getBool(
'rcfilters' ) ) {
1582 if ( $this->
getConfig()->
get(
'StructuredChangeFiltersShowPreference' ) ) {
1583 return !$this->
getUser()->getOption(
'rcenhancedfilters-disable' );
1585 return $this->
getUser()->getOption(
'rcenhancedfilters' );
1596 if ( $this->
getConfig()->
get(
'StructuredChangeFiltersShowPreference' ) ) {
1597 return !$this->
getUser()->getDefaultOption(
'rcenhancedfilters-disable' );
1599 return $this->
getUser()->getDefaultOption(
'rcenhancedfilters' );