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'] = (int)$this->pageLatest[$pageid];
415 $pageInfo[
'length'] = (int)$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 && isset( $this->variantTitles[$pageid] ) ) {
519 $pageInfo[
'varianttitles'] = $this->variantTitles[$pageid];
522 if ( $this->params[
'testactions'] ) {
524 if ( $this->countTestedActions >= $limit ) {
528 $detailLevel = $this->params[
'testactionsdetail'];
529 $rigor = $detailLevel ===
'quick' ?
'quick' :
'secure';
531 if ( $errorFormatter->getFormat() ===
'bc' ) {
533 $errorFormatter = $errorFormatter->newWithFormat(
'plaintext' );
537 $pageInfo[
'actions'] = [];
538 foreach ( $this->params[
'testactions']
as $action ) {
539 $this->countTestedActions++;
541 if ( $detailLevel ===
'boolean' ) {
542 $pageInfo[
'actions'][$action] =
$title->userCan( $action,
$user );
544 $pageInfo[
'actions'][$action] = $errorFormatter->arrayFromStatus( $this->
errorArrayToStatus(
545 $title->getUserPermissionsErrors( $action,
$user, $rigor ),
559 $this->protections = [];
560 $db = $this->
getDB();
563 if (
count( $this->titles ) ) {
566 $this->
addFields( [
'pr_page',
'pr_type',
'pr_level',
567 'pr_expiry',
'pr_cascade' ] );
568 $this->
addWhereFld(
'pr_page', array_keys( $this->titles ) );
571 foreach (
$res as $row ) {
573 $title = $this->titles[$row->pr_page];
575 'type' => $row->pr_type,
576 'level' => $row->pr_level,
579 if ( $row->pr_cascade ) {
580 $a[
'cascade'] =
true;
582 $this->protections[
$title->getNamespace()][
$title->getDBkey()][] = $a;
585 foreach ( $this->titles
as $pageId =>
$title ) {
586 if ( $this->pageRestrictions[$pageId] ) {
587 $namespace =
$title->getNamespace();
588 $dbKey =
$title->getDBkey();
589 $restrictions = explode(
':', trim( $this->pageRestrictions[$pageId] ) );
590 foreach ( $restrictions
as $restrict ) {
591 $temp = explode(
'=', trim( $restrict ) );
592 if (
count( $temp ) == 1 ) {
594 $restriction = trim( $temp[0] );
596 if ( $restriction ==
'' ) {
599 $this->protections[$namespace][$dbKey][] = [
601 'level' => $restriction,
602 'expiry' =>
'infinity',
604 $this->protections[$namespace][$dbKey][] = [
606 'level' => $restriction,
607 'expiry' =>
'infinity',
610 $restriction = trim( $temp[1] );
611 if ( $restriction ==
'' ) {
614 $this->protections[$namespace][$dbKey][] = [
616 'level' => $restriction,
617 'expiry' =>
'infinity',
626 if (
count( $this->missing ) ) {
630 $this->
addFields( [
'pt_title',
'pt_namespace',
'pt_create_perm',
'pt_expiry' ] );
631 $this->
addWhere( $lb->constructSet(
'pt', $db ) );
633 foreach (
$res as $row ) {
634 $this->protections[$row->pt_namespace][$row->pt_title][] = [
636 'level' => $row->pt_create_perm,
644 $images = $others = [];
645 foreach ( $this->everything
as $title ) {
647 $images[] =
$title->getDBkey();
652 $this->restrictionTypes[
$title->getNamespace()][
$title->getDBkey()] =
653 array_values(
$title->getRestrictionTypes() );
656 if (
count( $others ) ) {
660 $this->
addTables( [
'page_restrictions',
'page',
'templatelinks' ] );
661 $this->
addFields( [
'pr_type',
'pr_level',
'pr_expiry',
662 'page_title',
'page_namespace',
663 'tl_title',
'tl_namespace' ] );
664 $this->
addWhere( $lb->constructSet(
'tl', $db ) );
665 $this->
addWhere(
'pr_page = page_id' );
666 $this->
addWhere(
'pr_page = tl_from' );
670 foreach (
$res as $row ) {
672 $this->protections[$row->tl_namespace][$row->tl_title][] = [
673 'type' => $row->pr_type,
674 'level' => $row->pr_level,
676 'source' =>
$source->getPrefixedText()
681 if (
count( $images ) ) {
684 $this->
addTables( [
'page_restrictions',
'page',
'imagelinks' ] );
685 $this->
addFields( [
'pr_type',
'pr_level',
'pr_expiry',
686 'page_title',
'page_namespace',
'il_to' ] );
687 $this->
addWhere(
'pr_page = page_id' );
688 $this->
addWhere(
'pr_page = il_from' );
693 foreach (
$res as $row ) {
695 $this->protections[
NS_FILE][$row->il_to][] = [
696 'type' => $row->pr_type,
697 'level' => $row->pr_level,
699 'source' =>
$source->getPrefixedText()
710 $getTitles = $this->talkids = $this->subjectids = [];
713 foreach ( $this->everything
as $t ) {
715 if ( $this->fld_subjectid ) {
716 $getTitles[] =
$t->getSubjectPage();
718 } elseif ( $this->fld_talkid ) {
719 $getTitles[] =
$t->getTalkPage();
722 if ( $getTitles === [] ) {
726 $db = $this->
getDB();
733 $this->
addFields( [
'page_title',
'page_namespace',
'page_id' ] );
734 $this->
addWhere( $lb->constructSet(
'page', $db ) );
736 foreach (
$res as $row ) {
748 $this->displaytitles = [];
750 $pageIds = array_keys( $this->titles );
752 if ( $pageIds === [] ) {
758 $this->
addFields( [
'pp_page',
'pp_value' ] );
760 $this->
addWhereFld(
'pp_propname',
'displaytitle' );
763 foreach (
$res as $row ) {
764 $this->displaytitles[$row->pp_page] = $row->pp_value;
769 if ( $this->titles === [] ) {
772 $this->variantTitles = [];
773 foreach ( $this->titles
as $pageId =>
$t ) {
774 $this->variantTitles[$pageId] = isset( $this->displaytitles[$pageId] )
782 $contLang = MediaWikiServices::getInstance()->getContentLanguage();
783 foreach ( $contLang->getVariants()
as $variant ) {
784 $convertTitle = $contLang->autoConvert( $text, $variant );
786 $convertNs = $contLang->convertNamespace( $ns, $variant );
787 $convertTitle = $convertNs .
':' . $convertTitle;
789 $result[$variant] = $convertTitle;
801 if (
$user->isAnon() ||
count( $this->everything ) == 0
802 || !
$user->isAllowed(
'viewmywatchlist' )
808 $this->notificationtimestamps = [];
810 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
811 $timestamps = $store->getNotificationTimestampsBatch(
$user, $this->everything );
813 if ( $this->fld_watched ) {
814 foreach ( $timestamps
as $namespaceId => $dbKeys ) {
815 $this->watched[$namespaceId] = array_map(
823 if ( $this->fld_notificationtimestamp ) {
824 $this->notificationtimestamps = $timestamps;
832 if (
count( $this->everything ) == 0 ) {
837 $canUnwatchedpages =
$user->isAllowed(
'unwatchedpages' );
838 $unwatchedPageThreshold = $this->
getConfig()->get(
'UnwatchedPageThreshold' );
839 if ( !$canUnwatchedpages && !is_int( $unwatchedPageThreshold ) ) {
843 $this->showZeroWatchers = $canUnwatchedpages;
846 if ( !$canUnwatchedpages ) {
847 $countOptions[
'minimumWatchers'] = $unwatchedPageThreshold;
850 $this->watchers = MediaWikiServices::getInstance()->getWatchedItemStore()->countWatchersMultiple(
865 $db = $this->
getDB();
867 $canUnwatchedpages =
$user->isAllowed(
'unwatchedpages' );
868 $unwatchedPageThreshold = $this->
getConfig()->get(
'UnwatchedPageThreshold' );
869 if ( !$canUnwatchedpages && !is_int( $unwatchedPageThreshold ) ) {
873 $this->showZeroWatchers = $canUnwatchedpages;
875 $titlesWithThresholds = [];
876 if ( $this->titles ) {
881 $this->
addTables( [
'page',
'revision' ] );
882 $this->
addFields( [
'page_namespace',
'page_title',
'rev_timestamp' ] );
884 'page_latest = rev_id',
885 $lb->constructSet(
'page', $db ),
887 $this->
addOption(
'GROUP BY', [
'page_namespace',
'page_title' ] );
888 $timestampRes = $this->
select( __METHOD__ );
890 $age = $config->get(
'WatchersMaxAge' );
892 foreach ( $timestampRes
as $row ) {
893 $revTimestamp =
wfTimestamp( TS_UNIX, (
int)$row->rev_timestamp );
894 $timestamps[$row->page_namespace][$row->page_title] = $revTimestamp - $age;
896 $titlesWithThresholds = array_map(
906 if ( $this->missing ) {
907 $titlesWithThresholds = array_merge(
908 $titlesWithThresholds,
911 return [ $target,
null ];
917 $store = MediaWikiServices::getInstance()->getWatchedItemStore();
918 $this->visitingwatchers = $store->countVisitingWatchersMultiple(
919 $titlesWithThresholds,
920 !$canUnwatchedpages ? $unwatchedPageThreshold :
null
935 if ( array_diff( (
array)
$params[
'prop'], $publicProps ) ) {
940 if (
$params[
'testactions'] ) {
944 if ( !is_null(
$params[
'token'] ) ) {
959 'watchers', #
private
960 'visitingwatchers', #
private
961 'notificationtimestamp', #
private
964 'readable', #
private
980 'testactionsdetail' => [
998 'action=query&prop=info&titles=Main%20Page'
999 =>
'apihelp-query+info-example-simple',
1000 'action=query&prop=info&inprop=protection&titles=Main%20Page'
1001 =>
'apihelp-query+info-example-protection',
1006 return 'https://www.mediawiki.org/wiki/Special:MyLanguage/API:Info';