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 ) {
531 $pageInfo[
'actions'] = [];
532 foreach ( $this->params[
'testactions']
as $action ) {
533 $this->countTestedActions++;
534 $pageInfo[
'actions'][$action] =
$title->userCan( $action,
$user );
545 $this->protections = [];
546 $db = $this->
getDB();
549 if (
count( $this->titles ) ) {
552 $this->
addFields( [
'pr_page',
'pr_type',
'pr_level',
553 'pr_expiry',
'pr_cascade' ] );
554 $this->
addWhereFld(
'pr_page', array_keys( $this->titles ) );
557 foreach (
$res as $row ) {
559 $title = $this->titles[$row->pr_page];
561 'type' => $row->pr_type,
562 'level' => $row->pr_level,
565 if ( $row->pr_cascade ) {
566 $a[
'cascade'] =
true;
568 $this->protections[
$title->getNamespace()][
$title->getDBkey()][] = $a;
571 foreach ( $this->titles
as $pageId =>
$title ) {
572 if ( $this->pageRestrictions[$pageId] ) {
573 $namespace =
$title->getNamespace();
574 $dbKey =
$title->getDBkey();
575 $restrictions = explode(
':', trim( $this->pageRestrictions[$pageId] ) );
576 foreach ( $restrictions
as $restrict ) {
577 $temp = explode(
'=', trim( $restrict ) );
578 if (
count( $temp ) == 1 ) {
580 $restriction = trim( $temp[0] );
582 if ( $restriction ==
'' ) {
585 $this->protections[$namespace][$dbKey][] = [
587 'level' => $restriction,
588 'expiry' =>
'infinity',
590 $this->protections[$namespace][$dbKey][] = [
592 'level' => $restriction,
593 'expiry' =>
'infinity',
596 $restriction = trim( $temp[1] );
597 if ( $restriction ==
'' ) {
600 $this->protections[$namespace][$dbKey][] = [
602 'level' => $restriction,
603 'expiry' =>
'infinity',
612 if (
count( $this->missing ) ) {
616 $this->
addFields( [
'pt_title',
'pt_namespace',
'pt_create_perm',
'pt_expiry' ] );
617 $this->
addWhere( $lb->constructSet(
'pt', $db ) );
619 foreach (
$res as $row ) {
620 $this->protections[$row->pt_namespace][$row->pt_title][] = [
622 'level' => $row->pt_create_perm,
630 $images = $others = [];
631 foreach ( $this->everything
as $title ) {
633 $images[] =
$title->getDBkey();
638 $this->restrictionTypes[
$title->getNamespace()][
$title->getDBkey()] =
639 array_values(
$title->getRestrictionTypes() );
642 if (
count( $others ) ) {
646 $this->
addTables( [
'page_restrictions',
'page',
'templatelinks' ] );
647 $this->
addFields( [
'pr_type',
'pr_level',
'pr_expiry',
648 'page_title',
'page_namespace',
649 'tl_title',
'tl_namespace' ] );
650 $this->
addWhere( $lb->constructSet(
'tl', $db ) );
651 $this->
addWhere(
'pr_page = page_id' );
652 $this->
addWhere(
'pr_page = tl_from' );
656 foreach (
$res as $row ) {
658 $this->protections[$row->tl_namespace][$row->tl_title][] = [
659 'type' => $row->pr_type,
660 'level' => $row->pr_level,
662 'source' =>
$source->getPrefixedText()
667 if (
count( $images ) ) {
670 $this->
addTables( [
'page_restrictions',
'page',
'imagelinks' ] );
671 $this->
addFields( [
'pr_type',
'pr_level',
'pr_expiry',
672 'page_title',
'page_namespace',
'il_to' ] );
673 $this->
addWhere(
'pr_page = page_id' );
674 $this->
addWhere(
'pr_page = il_from' );
679 foreach (
$res as $row ) {
681 $this->protections[
NS_FILE][$row->il_to][] = [
682 'type' => $row->pr_type,
683 'level' => $row->pr_level,
685 'source' =>
$source->getPrefixedText()
696 $getTitles = $this->talkids = $this->subjectids = [];
699 foreach ( $this->everything
as $t ) {
701 if ( $this->fld_subjectid ) {
702 $getTitles[] =
$t->getSubjectPage();
704 } elseif ( $this->fld_talkid ) {
705 $getTitles[] =
$t->getTalkPage();
708 if ( !
count( $getTitles ) ) {
712 $db = $this->
getDB();
719 $this->
addFields( [
'page_title',
'page_namespace',
'page_id' ] );
720 $this->
addWhere( $lb->constructSet(
'page', $db ) );
722 foreach (
$res as $row ) {
725 intval( $row->page_id );
728 intval( $row->page_id );
734 $this->displaytitles = [];
736 $pageIds = array_keys( $this->titles );
738 if ( !
count( $pageIds ) ) {
744 $this->
addFields( [
'pp_page',
'pp_value' ] );
746 $this->
addWhereFld(
'pp_propname',
'displaytitle' );
749 foreach (
$res as $row ) {
750 $this->displaytitles[$row->pp_page] = $row->pp_value;
755 if ( !
count( $this->titles ) ) {
758 $this->variantTitles = [];
759 foreach ( $this->titles
as $pageId =>
$t ) {
760 $this->variantTitles[$pageId] = isset( $this->displaytitles[$pageId] )
770 $convertTitle =
$wgContLang->autoConvert( $text, $variant );
772 $convertNs =
$wgContLang->convertNamespace( $ns, $variant );
773 $convertTitle = $convertNs .
':' . $convertTitle;
775 $result[$variant] = $convertTitle;
787 if (
$user->isAnon() ||
count( $this->everything ) == 0
788 || !
$user->isAllowed(
'viewmywatchlist' )
794 $this->notificationtimestamps = [];
796 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
797 $timestamps = $store->getNotificationTimestampsBatch(
$user, $this->everything );
799 if ( $this->fld_watched ) {
800 foreach ( $timestamps
as $namespaceId => $dbKeys ) {
801 $this->watched[$namespaceId] = array_map(
809 if ( $this->fld_notificationtimestamp ) {
810 $this->notificationtimestamps = $timestamps;
818 if (
count( $this->everything ) == 0 ) {
823 $canUnwatchedpages =
$user->isAllowed(
'unwatchedpages' );
824 $unwatchedPageThreshold = $this->
getConfig()->get(
'UnwatchedPageThreshold' );
825 if ( !$canUnwatchedpages && !is_int( $unwatchedPageThreshold ) ) {
829 $this->showZeroWatchers = $canUnwatchedpages;
832 if ( !$canUnwatchedpages ) {
833 $countOptions[
'minimumWatchers'] = $unwatchedPageThreshold;
836 $this->watchers = MediaWikiServices::getInstance()->getWatchedItemStore()->countWatchersMultiple(
851 $db = $this->
getDB();
853 $canUnwatchedpages =
$user->isAllowed(
'unwatchedpages' );
854 $unwatchedPageThreshold = $this->
getConfig()->get(
'UnwatchedPageThreshold' );
855 if ( !$canUnwatchedpages && !is_int( $unwatchedPageThreshold ) ) {
859 $this->showZeroWatchers = $canUnwatchedpages;
861 $titlesWithThresholds = [];
862 if ( $this->titles ) {
867 $this->
addTables( [
'page',
'revision' ] );
868 $this->
addFields( [
'page_namespace',
'page_title',
'rev_timestamp' ] );
870 'page_latest = rev_id',
871 $lb->constructSet(
'page', $db ),
873 $this->
addOption(
'GROUP BY', [
'page_namespace',
'page_title' ] );
874 $timestampRes = $this->
select( __METHOD__ );
876 $age = $config->get(
'WatchersMaxAge' );
878 foreach ( $timestampRes
as $row ) {
879 $revTimestamp =
wfTimestamp( TS_UNIX, (
int)$row->rev_timestamp );
880 $timestamps[$row->page_namespace][$row->page_title] = $revTimestamp - $age;
882 $titlesWithThresholds = array_map(
892 if ( $this->missing ) {
893 $titlesWithThresholds = array_merge(
894 $titlesWithThresholds,
897 return [ $target, null ];
903 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
904 $this->visitingwatchers = $store->countVisitingWatchersMultiple(
905 $titlesWithThresholds,
906 !$canUnwatchedpages ? $unwatchedPageThreshold :
null
921 if ( array_diff( (
array)
$params[
'prop'], $publicProps ) ) {
926 if (
$params[
'testactions'] ) {
930 if ( !is_null(
$params[
'token'] ) ) {
945 'watchers', #
private
946 'visitingwatchers', #
private
947 'notificationtimestamp', #
private
950 'readable', #
private
976 'action=query&prop=info&titles=Main%20Page'
977 =>
'apihelp-query+info-example-simple',
978 'action=query&prop=info&inprop=protection&titles=Main%20Page'
979 =>
'apihelp-query+info-example-protection',
984 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Info';