60 parent::__construct(
$query, $moduleName,
'in' );
68 $pageSet->requestField(
'page_restrictions' );
72 $pageSet->requestField(
'page_is_redirect' );
73 $pageSet->requestField(
'page_is_new' );
75 $pageSet->requestField(
'page_touched' );
76 $pageSet->requestField(
'page_latest' );
77 $pageSet->requestField(
'page_len' );
78 if ( $config->get(
'ContentHandlerUseDB' ) ) {
79 $pageSet->requestField(
'page_content_model' );
81 if ( $config->get(
'PageLanguageUseDB' ) ) {
82 $pageSet->requestField(
'page_lang' );
95 if ( isset( $this->tokenFunctions ) ) {
105 $this->tokenFunctions = [
116 Hooks::run(
'APIQueryInfoTokens', [ &$this->tokenFunctions ] );
127 self::$cachedTokens = [];
138 if ( !$wgUser->isAllowed(
'edit' ) ) {
143 if ( !isset( self::$cachedTokens[
'edit'] ) ) {
144 self::$cachedTokens[
'edit'] = $wgUser->getEditToken();
147 return self::$cachedTokens[
'edit'];
155 if ( !$wgUser->isAllowed(
'delete' ) ) {
160 if ( !isset( self::$cachedTokens[
'delete'] ) ) {
161 self::$cachedTokens[
'delete'] = $wgUser->getEditToken();
164 return self::$cachedTokens[
'delete'];
172 if ( !$wgUser->isAllowed(
'protect' ) ) {
177 if ( !isset( self::$cachedTokens[
'protect'] ) ) {
178 self::$cachedTokens[
'protect'] = $wgUser->getEditToken();
181 return self::$cachedTokens[
'protect'];
189 if ( !$wgUser->isAllowed(
'move' ) ) {
194 if ( !isset( self::$cachedTokens[
'move'] ) ) {
195 self::$cachedTokens[
'move'] = $wgUser->getEditToken();
198 return self::$cachedTokens[
'move'];
206 if ( !$wgUser->isAllowed(
'block' ) ) {
211 if ( !isset( self::$cachedTokens[
'block'] ) ) {
212 self::$cachedTokens[
'block'] = $wgUser->getEditToken();
215 return self::$cachedTokens[
'block'];
231 if ( !$wgUser->canSendEmail() || $wgUser->isBlockedFromEmailuser() ) {
236 if ( !isset( self::$cachedTokens[
'email'] ) ) {
237 self::$cachedTokens[
'email'] = $wgUser->getEditToken();
240 return self::$cachedTokens[
'email'];
248 if ( !$wgUser->isAllowedAny(
'import',
'importupload' ) ) {
253 if ( !isset( self::$cachedTokens[
'import'] ) ) {
254 self::$cachedTokens[
'import'] = $wgUser->getEditToken();
257 return self::$cachedTokens[
'import'];
265 if ( !$wgUser->isLoggedIn() ) {
270 if ( !isset( self::$cachedTokens[
'watch'] ) ) {
271 self::$cachedTokens[
'watch'] = $wgUser->getEditToken(
'watch' );
274 return self::$cachedTokens[
'watch'];
282 if ( !$wgUser->isLoggedIn() ) {
287 if ( !isset( self::$cachedTokens[
'options'] ) ) {
288 self::$cachedTokens[
'options'] = $wgUser->getEditToken();
291 return self::$cachedTokens[
'options'];
296 if ( !is_null( $this->params[
'prop'] ) ) {
297 $prop = array_flip( $this->params[
'prop'] );
298 $this->fld_protection = isset( $prop[
'protection'] );
299 $this->fld_watched = isset( $prop[
'watched'] );
300 $this->fld_watchers = isset( $prop[
'watchers'] );
301 $this->fld_visitingwatchers = isset( $prop[
'visitingwatchers'] );
302 $this->fld_notificationtimestamp = isset( $prop[
'notificationtimestamp'] );
303 $this->fld_talkid = isset( $prop[
'talkid'] );
304 $this->fld_subjectid = isset( $prop[
'subjectid'] );
305 $this->fld_url = isset( $prop[
'url'] );
306 $this->fld_readable = isset( $prop[
'readable'] );
307 $this->fld_preload = isset( $prop[
'preload'] );
308 $this->fld_displaytitle = isset( $prop[
'displaytitle'] );
309 $this->fld_varianttitles = isset( $prop[
'varianttitles'] );
313 $this->titles = $pageSet->getGoodTitles();
314 $this->missing = $pageSet->getMissingTitles();
318 uasort( $this->everything, [
Title::class,
'compare' ] );
319 if ( !is_null( $this->params[
'continue'] ) ) {
322 $cont = explode(
'|', $this->params[
'continue'] );
325 foreach ( $this->everything
as $pageid =>
$title ) {
329 unset( $this->titles[$pageid] );
330 unset( $this->missing[$pageid] );
331 unset( $this->everything[$pageid] );
335 $this->pageRestrictions = $pageSet->getCustomField(
'page_restrictions' );
337 $this->pageIsRedir = !$pageSet->isResolvingRedirects()
338 ? $pageSet->getCustomField(
'page_is_redirect' )
340 $this->pageIsNew = $pageSet->getCustomField(
'page_is_new' );
342 $this->pageTouched = $pageSet->getCustomField(
'page_touched' );
343 $this->pageLatest = $pageSet->getCustomField(
'page_latest' );
344 $this->pageLength = $pageSet->getCustomField(
'page_len' );
347 if ( $this->fld_protection ) {
351 if ( $this->fld_watched || $this->fld_notificationtimestamp ) {
355 if ( $this->fld_watchers ) {
359 if ( $this->fld_visitingwatchers ) {
364 if ( $this->fld_talkid || $this->fld_subjectid ) {
368 if ( $this->fld_displaytitle ) {
372 if ( $this->fld_varianttitles ) {
377 foreach ( $this->everything
as $pageid =>
$title ) {
379 $fit = $pageInfo !==
null &&
$result->addValue( [
382 ], $pageid, $pageInfo );
385 $title->getNamespace() .
'|' .
401 $titleExists = $pageid > 0;
402 $ns =
$title->getNamespace();
403 $dbkey =
$title->getDBkey();
405 $pageInfo[
'contentmodel'] =
$title->getContentModel();
407 $pageLanguage =
$title->getPageLanguage();
408 $pageInfo[
'pagelanguage'] = $pageLanguage->getCode();
409 $pageInfo[
'pagelanguagehtmlcode'] = $pageLanguage->getHtmlCode();
410 $pageInfo[
'pagelanguagedir'] = $pageLanguage->getDir();
412 if ( $titleExists ) {
413 $pageInfo[
'touched'] =
wfTimestamp( TS_ISO_8601, $this->pageTouched[$pageid] );
414 $pageInfo[
'lastrevid'] = intval( $this->pageLatest[$pageid] );
415 $pageInfo[
'length'] = intval( $this->pageLength[$pageid] );
417 if ( isset( $this->pageIsRedir[$pageid] ) && $this->pageIsRedir[$pageid] ) {
418 $pageInfo[
'redirect'] =
true;
420 if ( $this->pageIsNew[$pageid] ) {
421 $pageInfo[
'new'] =
true;
425 if ( !is_null( $this->params[
'token'] ) ) {
427 $pageInfo[
'starttimestamp'] =
wfTimestamp( TS_ISO_8601, time() );
428 foreach ( $this->params[
'token']
as $t ) {
430 if ( $val ===
false ) {
431 $this->
addWarning( [
'apiwarn-tokennotallowed', $t ] );
433 $pageInfo[
$t .
'token'] = $val;
438 if ( $this->fld_protection ) {
439 $pageInfo[
'protection'] = [];
440 if ( isset( $this->protections[$ns][$dbkey] ) ) {
441 $pageInfo[
'protection'] =
442 $this->protections[$ns][$dbkey];
446 $pageInfo[
'restrictiontypes'] = [];
447 if ( isset( $this->restrictionTypes[$ns][$dbkey] ) ) {
448 $pageInfo[
'restrictiontypes'] =
449 $this->restrictionTypes[$ns][$dbkey];
454 if ( $this->fld_watched && $this->watched !==
null ) {
455 $pageInfo[
'watched'] = $this->watched[$ns][$dbkey];
458 if ( $this->fld_watchers ) {
459 if ( $this->watchers !==
null && $this->watchers[$ns][$dbkey] !== 0 ) {
460 $pageInfo[
'watchers'] = $this->watchers[$ns][$dbkey];
461 } elseif ( $this->showZeroWatchers ) {
462 $pageInfo[
'watchers'] = 0;
466 if ( $this->fld_visitingwatchers ) {
467 if ( $this->visitingwatchers !==
null && $this->visitingwatchers[$ns][$dbkey] !== 0 ) {
468 $pageInfo[
'visitingwatchers'] = $this->visitingwatchers[$ns][$dbkey];
469 } elseif ( $this->showZeroWatchers ) {
470 $pageInfo[
'visitingwatchers'] = 0;
474 if ( $this->fld_notificationtimestamp ) {
475 $pageInfo[
'notificationtimestamp'] =
'';
476 if ( $this->notificationtimestamps[$ns][$dbkey] ) {
477 $pageInfo[
'notificationtimestamp'] =
478 wfTimestamp( TS_ISO_8601, $this->notificationtimestamps[$ns][$dbkey] );
482 if ( $this->fld_talkid && isset( $this->talkids[$ns][$dbkey] ) ) {
483 $pageInfo[
'talkid'] = $this->talkids[$ns][$dbkey];
486 if ( $this->fld_subjectid && isset( $this->subjectids[$ns][$dbkey] ) ) {
487 $pageInfo[
'subjectid'] = $this->subjectids[$ns][$dbkey];
490 if ( $this->fld_url ) {
495 if ( $this->fld_readable ) {
496 $pageInfo[
'readable'] =
$title->userCan(
'read', $this->
getUser() );
499 if ( $this->fld_preload ) {
500 if ( $titleExists ) {
501 $pageInfo[
'preload'] =
'';
506 $pageInfo[
'preload'] = $text;
510 if ( $this->fld_displaytitle ) {
511 if ( isset( $this->displaytitles[$pageid] ) ) {
512 $pageInfo[
'displaytitle'] = $this->displaytitles[$pageid];
514 $pageInfo[
'displaytitle'] =
$title->getPrefixedText();
518 if ( $this->fld_varianttitles ) {
519 if ( isset( $this->variantTitles[$pageid] ) ) {
520 $pageInfo[
'varianttitles'] = $this->variantTitles[$pageid];
524 if ( $this->params[
'testactions'] ) {
526 if ( $this->countTestedActions >= $limit ) {
530 $detailLevel = $this->params[
'testactionsdetail'];
531 $rigor = $detailLevel ===
'quick' ?
'quick' :
'secure';
533 if ( $errorFormatter->getFormat() ===
'bc' ) {
535 $errorFormatter = $errorFormatter->newWithFormat(
'plaintext' );
539 $pageInfo[
'actions'] = [];
540 foreach ( $this->params[
'testactions']
as $action ) {
541 $this->countTestedActions++;
543 if ( $detailLevel ===
'boolean' ) {
544 $pageInfo[
'actions'][$action] =
$title->userCan( $action,
$user );
546 $pageInfo[
'actions'][$action] = $errorFormatter->arrayFromStatus( $this->
errorArrayToStatus(
547 $title->getUserPermissionsErrors( $action,
$user, $rigor ),
561 $this->protections = [];
562 $db = $this->
getDB();
565 if (
count( $this->titles ) ) {
568 $this->
addFields( [
'pr_page',
'pr_type',
'pr_level',
569 'pr_expiry',
'pr_cascade' ] );
570 $this->
addWhereFld(
'pr_page', array_keys( $this->titles ) );
573 foreach (
$res as $row ) {
575 $title = $this->titles[$row->pr_page];
577 'type' => $row->pr_type,
578 'level' => $row->pr_level,
581 if ( $row->pr_cascade ) {
582 $a[
'cascade'] =
true;
584 $this->protections[
$title->getNamespace()][
$title->getDBkey()][] = $a;
587 foreach ( $this->titles
as $pageId =>
$title ) {
588 if ( $this->pageRestrictions[$pageId] ) {
589 $namespace =
$title->getNamespace();
590 $dbKey =
$title->getDBkey();
591 $restrictions = explode(
':', trim( $this->pageRestrictions[$pageId] ) );
592 foreach ( $restrictions
as $restrict ) {
593 $temp = explode(
'=', trim( $restrict ) );
594 if (
count( $temp ) == 1 ) {
596 $restriction = trim( $temp[0] );
598 if ( $restriction ==
'' ) {
601 $this->protections[$namespace][$dbKey][] = [
603 'level' => $restriction,
604 'expiry' =>
'infinity',
606 $this->protections[$namespace][$dbKey][] = [
608 'level' => $restriction,
609 'expiry' =>
'infinity',
612 $restriction = trim( $temp[1] );
613 if ( $restriction ==
'' ) {
616 $this->protections[$namespace][$dbKey][] = [
618 'level' => $restriction,
619 'expiry' =>
'infinity',
628 if (
count( $this->missing ) ) {
632 $this->
addFields( [
'pt_title',
'pt_namespace',
'pt_create_perm',
'pt_expiry' ] );
633 $this->
addWhere( $lb->constructSet(
'pt', $db ) );
635 foreach (
$res as $row ) {
636 $this->protections[$row->pt_namespace][$row->pt_title][] = [
638 'level' => $row->pt_create_perm,
646 $images = $others = [];
647 foreach ( $this->everything
as $title ) {
649 $images[] =
$title->getDBkey();
654 $this->restrictionTypes[
$title->getNamespace()][
$title->getDBkey()] =
655 array_values(
$title->getRestrictionTypes() );
658 if (
count( $others ) ) {
662 $this->
addTables( [
'page_restrictions',
'page',
'templatelinks' ] );
663 $this->
addFields( [
'pr_type',
'pr_level',
'pr_expiry',
664 'page_title',
'page_namespace',
665 'tl_title',
'tl_namespace' ] );
666 $this->
addWhere( $lb->constructSet(
'tl', $db ) );
667 $this->
addWhere(
'pr_page = page_id' );
668 $this->
addWhere(
'pr_page = tl_from' );
672 foreach (
$res as $row ) {
674 $this->protections[$row->tl_namespace][$row->tl_title][] = [
675 'type' => $row->pr_type,
676 'level' => $row->pr_level,
678 'source' =>
$source->getPrefixedText()
683 if (
count( $images ) ) {
686 $this->
addTables( [
'page_restrictions',
'page',
'imagelinks' ] );
687 $this->
addFields( [
'pr_type',
'pr_level',
'pr_expiry',
688 'page_title',
'page_namespace',
'il_to' ] );
689 $this->
addWhere(
'pr_page = page_id' );
690 $this->
addWhere(
'pr_page = il_from' );
695 foreach (
$res as $row ) {
697 $this->protections[
NS_FILE][$row->il_to][] = [
698 'type' => $row->pr_type,
699 'level' => $row->pr_level,
701 'source' =>
$source->getPrefixedText()
712 $getTitles = $this->talkids = $this->subjectids = [];
715 foreach ( $this->everything
as $t ) {
717 if ( $this->fld_subjectid ) {
718 $getTitles[] =
$t->getSubjectPage();
720 } elseif ( $this->fld_talkid ) {
721 $getTitles[] =
$t->getTalkPage();
724 if ( !
count( $getTitles ) ) {
728 $db = $this->
getDB();
735 $this->
addFields( [
'page_title',
'page_namespace',
'page_id' ] );
736 $this->
addWhere( $lb->constructSet(
'page', $db ) );
738 foreach (
$res as $row ) {
741 intval( $row->page_id );
744 intval( $row->page_id );
750 $this->displaytitles = [];
752 $pageIds = array_keys( $this->titles );
754 if ( !
count( $pageIds ) ) {
760 $this->
addFields( [
'pp_page',
'pp_value' ] );
762 $this->
addWhereFld(
'pp_propname',
'displaytitle' );
765 foreach (
$res as $row ) {
766 $this->displaytitles[$row->pp_page] = $row->pp_value;
771 if ( !
count( $this->titles ) ) {
774 $this->variantTitles = [];
775 foreach ( $this->titles
as $pageId =>
$t ) {
776 $this->variantTitles[$pageId] = isset( $this->displaytitles[$pageId] )
784 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
785 foreach ( $contLang->getVariants()
as $variant ) {
786 $convertTitle = $contLang->autoConvert( $text, $variant );
788 $convertNs = $contLang->convertNamespace( $ns, $variant );
789 $convertTitle = $convertNs .
':' . $convertTitle;
791 $result[$variant] = $convertTitle;
803 if (
$user->isAnon() ||
count( $this->everything ) == 0
804 || !
$user->isAllowed(
'viewmywatchlist' )
810 $this->notificationtimestamps = [];
812 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
813 $timestamps = $store->getNotificationTimestampsBatch(
$user, $this->everything );
815 if ( $this->fld_watched ) {
816 foreach ( $timestamps
as $namespaceId => $dbKeys ) {
817 $this->watched[$namespaceId] = array_map(
825 if ( $this->fld_notificationtimestamp ) {
826 $this->notificationtimestamps = $timestamps;
834 if (
count( $this->everything ) == 0 ) {
839 $canUnwatchedpages =
$user->isAllowed(
'unwatchedpages' );
840 $unwatchedPageThreshold = $this->
getConfig()->get(
'UnwatchedPageThreshold' );
841 if ( !$canUnwatchedpages && !is_int( $unwatchedPageThreshold ) ) {
845 $this->showZeroWatchers = $canUnwatchedpages;
848 if ( !$canUnwatchedpages ) {
849 $countOptions[
'minimumWatchers'] = $unwatchedPageThreshold;
852 $this->watchers = MediaWikiServices::getInstance()->getWatchedItemStore()->countWatchersMultiple(
867 $db = $this->
getDB();
869 $canUnwatchedpages =
$user->isAllowed(
'unwatchedpages' );
870 $unwatchedPageThreshold = $this->
getConfig()->get(
'UnwatchedPageThreshold' );
871 if ( !$canUnwatchedpages && !is_int( $unwatchedPageThreshold ) ) {
875 $this->showZeroWatchers = $canUnwatchedpages;
877 $titlesWithThresholds = [];
878 if ( $this->titles ) {
883 $this->
addTables( [
'page',
'revision' ] );
884 $this->
addFields( [
'page_namespace',
'page_title',
'rev_timestamp' ] );
886 'page_latest = rev_id',
887 $lb->constructSet(
'page', $db ),
889 $this->
addOption(
'GROUP BY', [
'page_namespace',
'page_title' ] );
890 $timestampRes = $this->
select( __METHOD__ );
892 $age = $config->get(
'WatchersMaxAge' );
894 foreach ( $timestampRes
as $row ) {
895 $revTimestamp =
wfTimestamp( TS_UNIX, (
int)$row->rev_timestamp );
896 $timestamps[$row->page_namespace][$row->page_title] = $revTimestamp - $age;
898 $titlesWithThresholds = array_map(
908 if ( $this->missing ) {
909 $titlesWithThresholds = array_merge(
910 $titlesWithThresholds,
913 return [ $target, null ];
919 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
920 $this->visitingwatchers = $store->countVisitingWatchersMultiple(
921 $titlesWithThresholds,
922 !$canUnwatchedpages ? $unwatchedPageThreshold :
null
937 if ( array_diff( (
array)
$params[
'prop'], $publicProps ) ) {
942 if (
$params[
'testactions'] ) {
946 if ( !is_null(
$params[
'token'] ) ) {
961 'watchers', #
private
962 'visitingwatchers', #
private
963 'notificationtimestamp', #
private
966 'readable', #
private
982 'testactionsdetail' => [
1000 'action=query&prop=info&titles=Main%20Page'
1001 =>
'apihelp-query+info-example-simple',
1002 'action=query&prop=info&inprop=protection&titles=Main%20Page'
1003 =>
'apihelp-query+info-example-protection',
1008 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Info';