MediaWiki
REL1_23
|
00001 <?php 00032 class ApiQueryInfo extends ApiQueryBase { 00033 00034 private $fld_protection = false, $fld_talkid = false, 00035 $fld_subjectid = false, $fld_url = false, 00036 $fld_readable = false, $fld_watched = false, $fld_watchers = false, 00037 $fld_notificationtimestamp = false, 00038 $fld_preload = false, $fld_displaytitle = false; 00039 00040 private $params, $titles, $missing, $everything, $pageCounter; 00041 00042 private $pageRestrictions, $pageIsRedir, $pageIsNew, $pageTouched, 00043 $pageLatest, $pageLength; 00044 00045 private $protections, $watched, $watchers, $notificationtimestamps, 00046 $talkids, $subjectids, $displaytitles; 00047 private $showZeroWatchers = false; 00048 00049 private $tokenFunctions; 00050 00051 public function __construct( $query, $moduleName ) { 00052 parent::__construct( $query, $moduleName, 'in' ); 00053 } 00054 00059 public function requestExtraData( $pageSet ) { 00060 global $wgDisableCounters, $wgContentHandlerUseDB; 00061 00062 $pageSet->requestField( 'page_restrictions' ); 00063 // when resolving redirects, no page will have this field 00064 if ( !$pageSet->isResolvingRedirects() ) { 00065 $pageSet->requestField( 'page_is_redirect' ); 00066 } 00067 $pageSet->requestField( 'page_is_new' ); 00068 if ( !$wgDisableCounters ) { 00069 $pageSet->requestField( 'page_counter' ); 00070 } 00071 $pageSet->requestField( 'page_touched' ); 00072 $pageSet->requestField( 'page_latest' ); 00073 $pageSet->requestField( 'page_len' ); 00074 if ( $wgContentHandlerUseDB ) { 00075 $pageSet->requestField( 'page_content_model' ); 00076 } 00077 } 00078 00085 protected function getTokenFunctions() { 00086 // Don't call the hooks twice 00087 if ( isset( $this->tokenFunctions ) ) { 00088 return $this->tokenFunctions; 00089 } 00090 00091 // If we're in JSON callback mode, no tokens can be obtained 00092 if ( !is_null( $this->getMain()->getRequest()->getVal( 'callback' ) ) ) { 00093 return array(); 00094 } 00095 00096 $this->tokenFunctions = array( 00097 'edit' => array( 'ApiQueryInfo', 'getEditToken' ), 00098 'delete' => array( 'ApiQueryInfo', 'getDeleteToken' ), 00099 'protect' => array( 'ApiQueryInfo', 'getProtectToken' ), 00100 'move' => array( 'ApiQueryInfo', 'getMoveToken' ), 00101 'block' => array( 'ApiQueryInfo', 'getBlockToken' ), 00102 'unblock' => array( 'ApiQueryInfo', 'getUnblockToken' ), 00103 'email' => array( 'ApiQueryInfo', 'getEmailToken' ), 00104 'import' => array( 'ApiQueryInfo', 'getImportToken' ), 00105 'watch' => array( 'ApiQueryInfo', 'getWatchToken' ), 00106 ); 00107 wfRunHooks( 'APIQueryInfoTokens', array( &$this->tokenFunctions ) ); 00108 00109 return $this->tokenFunctions; 00110 } 00111 00112 static protected $cachedTokens = array(); 00113 00114 public static function resetTokenCache() { 00115 ApiQueryInfo::$cachedTokens = array(); 00116 } 00117 00118 public static function getEditToken( $pageid, $title ) { 00119 // We could check for $title->userCan('edit') here, 00120 // but that's too expensive for this purpose 00121 // and would break caching 00122 global $wgUser; 00123 if ( !$wgUser->isAllowed( 'edit' ) ) { 00124 return false; 00125 } 00126 00127 // The token is always the same, let's exploit that 00128 if ( !isset( ApiQueryInfo::$cachedTokens['edit'] ) ) { 00129 ApiQueryInfo::$cachedTokens['edit'] = $wgUser->getEditToken(); 00130 } 00131 00132 return ApiQueryInfo::$cachedTokens['edit']; 00133 } 00134 00135 public static function getDeleteToken( $pageid, $title ) { 00136 global $wgUser; 00137 if ( !$wgUser->isAllowed( 'delete' ) ) { 00138 return false; 00139 } 00140 00141 // The token is always the same, let's exploit that 00142 if ( !isset( ApiQueryInfo::$cachedTokens['delete'] ) ) { 00143 ApiQueryInfo::$cachedTokens['delete'] = $wgUser->getEditToken(); 00144 } 00145 00146 return ApiQueryInfo::$cachedTokens['delete']; 00147 } 00148 00149 public static function getProtectToken( $pageid, $title ) { 00150 global $wgUser; 00151 if ( !$wgUser->isAllowed( 'protect' ) ) { 00152 return false; 00153 } 00154 00155 // The token is always the same, let's exploit that 00156 if ( !isset( ApiQueryInfo::$cachedTokens['protect'] ) ) { 00157 ApiQueryInfo::$cachedTokens['protect'] = $wgUser->getEditToken(); 00158 } 00159 00160 return ApiQueryInfo::$cachedTokens['protect']; 00161 } 00162 00163 public static function getMoveToken( $pageid, $title ) { 00164 global $wgUser; 00165 if ( !$wgUser->isAllowed( 'move' ) ) { 00166 return false; 00167 } 00168 00169 // The token is always the same, let's exploit that 00170 if ( !isset( ApiQueryInfo::$cachedTokens['move'] ) ) { 00171 ApiQueryInfo::$cachedTokens['move'] = $wgUser->getEditToken(); 00172 } 00173 00174 return ApiQueryInfo::$cachedTokens['move']; 00175 } 00176 00177 public static function getBlockToken( $pageid, $title ) { 00178 global $wgUser; 00179 if ( !$wgUser->isAllowed( 'block' ) ) { 00180 return false; 00181 } 00182 00183 // The token is always the same, let's exploit that 00184 if ( !isset( ApiQueryInfo::$cachedTokens['block'] ) ) { 00185 ApiQueryInfo::$cachedTokens['block'] = $wgUser->getEditToken(); 00186 } 00187 00188 return ApiQueryInfo::$cachedTokens['block']; 00189 } 00190 00191 public static function getUnblockToken( $pageid, $title ) { 00192 // Currently, this is exactly the same as the block token 00193 return self::getBlockToken( $pageid, $title ); 00194 } 00195 00196 public static function getEmailToken( $pageid, $title ) { 00197 global $wgUser; 00198 if ( !$wgUser->canSendEmail() || $wgUser->isBlockedFromEmailUser() ) { 00199 return false; 00200 } 00201 00202 // The token is always the same, let's exploit that 00203 if ( !isset( ApiQueryInfo::$cachedTokens['email'] ) ) { 00204 ApiQueryInfo::$cachedTokens['email'] = $wgUser->getEditToken(); 00205 } 00206 00207 return ApiQueryInfo::$cachedTokens['email']; 00208 } 00209 00210 public static function getImportToken( $pageid, $title ) { 00211 global $wgUser; 00212 if ( !$wgUser->isAllowedAny( 'import', 'importupload' ) ) { 00213 return false; 00214 } 00215 00216 // The token is always the same, let's exploit that 00217 if ( !isset( ApiQueryInfo::$cachedTokens['import'] ) ) { 00218 ApiQueryInfo::$cachedTokens['import'] = $wgUser->getEditToken(); 00219 } 00220 00221 return ApiQueryInfo::$cachedTokens['import']; 00222 } 00223 00224 public static function getWatchToken( $pageid, $title ) { 00225 global $wgUser; 00226 if ( !$wgUser->isLoggedIn() ) { 00227 return false; 00228 } 00229 00230 // The token is always the same, let's exploit that 00231 if ( !isset( ApiQueryInfo::$cachedTokens['watch'] ) ) { 00232 ApiQueryInfo::$cachedTokens['watch'] = $wgUser->getEditToken( 'watch' ); 00233 } 00234 00235 return ApiQueryInfo::$cachedTokens['watch']; 00236 } 00237 00238 public static function getOptionsToken( $pageid, $title ) { 00239 global $wgUser; 00240 if ( !$wgUser->isLoggedIn() ) { 00241 return false; 00242 } 00243 00244 // The token is always the same, let's exploit that 00245 if ( !isset( ApiQueryInfo::$cachedTokens['options'] ) ) { 00246 ApiQueryInfo::$cachedTokens['options'] = $wgUser->getEditToken(); 00247 } 00248 00249 return ApiQueryInfo::$cachedTokens['options']; 00250 } 00251 00252 public function execute() { 00253 $this->params = $this->extractRequestParams(); 00254 if ( !is_null( $this->params['prop'] ) ) { 00255 $prop = array_flip( $this->params['prop'] ); 00256 $this->fld_protection = isset( $prop['protection'] ); 00257 $this->fld_watched = isset( $prop['watched'] ); 00258 $this->fld_watchers = isset( $prop['watchers'] ); 00259 $this->fld_notificationtimestamp = isset( $prop['notificationtimestamp'] ); 00260 $this->fld_talkid = isset( $prop['talkid'] ); 00261 $this->fld_subjectid = isset( $prop['subjectid'] ); 00262 $this->fld_url = isset( $prop['url'] ); 00263 $this->fld_readable = isset( $prop['readable'] ); 00264 $this->fld_preload = isset( $prop['preload'] ); 00265 $this->fld_displaytitle = isset( $prop['displaytitle'] ); 00266 } 00267 00268 $pageSet = $this->getPageSet(); 00269 $this->titles = $pageSet->getGoodTitles(); 00270 $this->missing = $pageSet->getMissingTitles(); 00271 $this->everything = $this->titles + $this->missing; 00272 $result = $this->getResult(); 00273 00274 uasort( $this->everything, array( 'Title', 'compare' ) ); 00275 if ( !is_null( $this->params['continue'] ) ) { 00276 // Throw away any titles we're gonna skip so they don't 00277 // clutter queries 00278 $cont = explode( '|', $this->params['continue'] ); 00279 $this->dieContinueUsageIf( count( $cont ) != 2 ); 00280 $conttitle = Title::makeTitleSafe( $cont[0], $cont[1] ); 00281 foreach ( $this->everything as $pageid => $title ) { 00282 if ( Title::compare( $title, $conttitle ) >= 0 ) { 00283 break; 00284 } 00285 unset( $this->titles[$pageid] ); 00286 unset( $this->missing[$pageid] ); 00287 unset( $this->everything[$pageid] ); 00288 } 00289 } 00290 00291 $this->pageRestrictions = $pageSet->getCustomField( 'page_restrictions' ); 00292 // when resolving redirects, no page will have this field 00293 $this->pageIsRedir = !$pageSet->isResolvingRedirects() 00294 ? $pageSet->getCustomField( 'page_is_redirect' ) 00295 : array(); 00296 $this->pageIsNew = $pageSet->getCustomField( 'page_is_new' ); 00297 00298 global $wgDisableCounters; 00299 00300 if ( !$wgDisableCounters ) { 00301 $this->pageCounter = $pageSet->getCustomField( 'page_counter' ); 00302 } 00303 $this->pageTouched = $pageSet->getCustomField( 'page_touched' ); 00304 $this->pageLatest = $pageSet->getCustomField( 'page_latest' ); 00305 $this->pageLength = $pageSet->getCustomField( 'page_len' ); 00306 00307 // Get protection info if requested 00308 if ( $this->fld_protection ) { 00309 $this->getProtectionInfo(); 00310 } 00311 00312 if ( $this->fld_watched || $this->fld_notificationtimestamp ) { 00313 $this->getWatchedInfo(); 00314 } 00315 00316 if ( $this->fld_watchers ) { 00317 $this->getWatcherInfo(); 00318 } 00319 00320 // Run the talkid/subjectid query if requested 00321 if ( $this->fld_talkid || $this->fld_subjectid ) { 00322 $this->getTSIDs(); 00323 } 00324 00325 if ( $this->fld_displaytitle ) { 00326 $this->getDisplayTitle(); 00327 } 00328 00330 foreach ( $this->everything as $pageid => $title ) { 00331 $pageInfo = $this->extractPageInfo( $pageid, $title ); 00332 $fit = $result->addValue( array( 00333 'query', 00334 'pages' 00335 ), $pageid, $pageInfo ); 00336 if ( !$fit ) { 00337 $this->setContinueEnumParameter( 'continue', 00338 $title->getNamespace() . '|' . 00339 $title->getText() ); 00340 break; 00341 } 00342 } 00343 } 00344 00351 private function extractPageInfo( $pageid, $title ) { 00352 $pageInfo = array(); 00353 // $title->exists() needs pageid, which is not set for all title objects 00354 $titleExists = $pageid > 0; 00355 $ns = $title->getNamespace(); 00356 $dbkey = $title->getDBkey(); 00357 00358 $pageInfo['contentmodel'] = $title->getContentModel(); 00359 $pageInfo['pagelanguage'] = $title->getPageLanguage()->getCode(); 00360 00361 if ( $titleExists ) { 00362 global $wgDisableCounters; 00363 00364 $pageInfo['touched'] = wfTimestamp( TS_ISO_8601, $this->pageTouched[$pageid] ); 00365 $pageInfo['lastrevid'] = intval( $this->pageLatest[$pageid] ); 00366 $pageInfo['counter'] = $wgDisableCounters 00367 ? '' 00368 : intval( $this->pageCounter[$pageid] ); 00369 $pageInfo['length'] = intval( $this->pageLength[$pageid] ); 00370 00371 if ( isset( $this->pageIsRedir[$pageid] ) && $this->pageIsRedir[$pageid] ) { 00372 $pageInfo['redirect'] = ''; 00373 } 00374 if ( $this->pageIsNew[$pageid] ) { 00375 $pageInfo['new'] = ''; 00376 } 00377 } 00378 00379 if ( !is_null( $this->params['token'] ) ) { 00380 $tokenFunctions = $this->getTokenFunctions(); 00381 $pageInfo['starttimestamp'] = wfTimestamp( TS_ISO_8601, time() ); 00382 foreach ( $this->params['token'] as $t ) { 00383 $val = call_user_func( $tokenFunctions[$t], $pageid, $title ); 00384 if ( $val === false ) { 00385 $this->setWarning( "Action '$t' is not allowed for the current user" ); 00386 } else { 00387 $pageInfo[$t . 'token'] = $val; 00388 } 00389 } 00390 } 00391 00392 if ( $this->fld_protection ) { 00393 $pageInfo['protection'] = array(); 00394 if ( isset( $this->protections[$ns][$dbkey] ) ) { 00395 $pageInfo['protection'] = 00396 $this->protections[$ns][$dbkey]; 00397 } 00398 $this->getResult()->setIndexedTagName( $pageInfo['protection'], 'pr' ); 00399 } 00400 00401 if ( $this->fld_watched && isset( $this->watched[$ns][$dbkey] ) ) { 00402 $pageInfo['watched'] = ''; 00403 } 00404 00405 if ( $this->fld_watchers ) { 00406 if ( isset( $this->watchers[$ns][$dbkey] ) ) { 00407 $pageInfo['watchers'] = $this->watchers[$ns][$dbkey]; 00408 } elseif ( $this->showZeroWatchers ) { 00409 $pageInfo['watchers'] = 0; 00410 } 00411 } 00412 00413 if ( $this->fld_notificationtimestamp ) { 00414 $pageInfo['notificationtimestamp'] = ''; 00415 if ( isset( $this->notificationtimestamps[$ns][$dbkey] ) ) { 00416 $pageInfo['notificationtimestamp'] = 00417 wfTimestamp( TS_ISO_8601, $this->notificationtimestamps[$ns][$dbkey] ); 00418 } 00419 } 00420 00421 if ( $this->fld_talkid && isset( $this->talkids[$ns][$dbkey] ) ) { 00422 $pageInfo['talkid'] = $this->talkids[$ns][$dbkey]; 00423 } 00424 00425 if ( $this->fld_subjectid && isset( $this->subjectids[$ns][$dbkey] ) ) { 00426 $pageInfo['subjectid'] = $this->subjectids[$ns][$dbkey]; 00427 } 00428 00429 if ( $this->fld_url ) { 00430 $pageInfo['fullurl'] = wfExpandUrl( $title->getFullURL(), PROTO_CURRENT ); 00431 $pageInfo['editurl'] = wfExpandUrl( $title->getFullURL( 'action=edit' ), PROTO_CURRENT ); 00432 } 00433 if ( $this->fld_readable && $title->userCan( 'read', $this->getUser() ) ) { 00434 $pageInfo['readable'] = ''; 00435 } 00436 00437 if ( $this->fld_preload ) { 00438 if ( $titleExists ) { 00439 $pageInfo['preload'] = ''; 00440 } else { 00441 $text = null; 00442 wfRunHooks( 'EditFormPreloadText', array( &$text, &$title ) ); 00443 00444 $pageInfo['preload'] = $text; 00445 } 00446 } 00447 00448 if ( $this->fld_displaytitle ) { 00449 if ( isset( $this->displaytitles[$pageid] ) ) { 00450 $pageInfo['displaytitle'] = $this->displaytitles[$pageid]; 00451 } else { 00452 $pageInfo['displaytitle'] = $title->getPrefixedText(); 00453 } 00454 } 00455 00456 return $pageInfo; 00457 } 00458 00462 private function getProtectionInfo() { 00463 global $wgContLang; 00464 $this->protections = array(); 00465 $db = $this->getDB(); 00466 00467 // Get normal protections for existing titles 00468 if ( count( $this->titles ) ) { 00469 $this->resetQueryParams(); 00470 $this->addTables( 'page_restrictions' ); 00471 $this->addFields( array( 'pr_page', 'pr_type', 'pr_level', 00472 'pr_expiry', 'pr_cascade' ) ); 00473 $this->addWhereFld( 'pr_page', array_keys( $this->titles ) ); 00474 00475 $res = $this->select( __METHOD__ ); 00476 foreach ( $res as $row ) { 00478 $title = $this->titles[$row->pr_page]; 00479 $a = array( 00480 'type' => $row->pr_type, 00481 'level' => $row->pr_level, 00482 'expiry' => $wgContLang->formatExpiry( $row->pr_expiry, TS_ISO_8601 ) 00483 ); 00484 if ( $row->pr_cascade ) { 00485 $a['cascade'] = ''; 00486 } 00487 $this->protections[$title->getNamespace()][$title->getDBkey()][] = $a; 00488 } 00489 // Also check old restrictions 00490 foreach ( $this->titles as $pageId => $title ) { 00491 if ( $this->pageRestrictions[$pageId] ) { 00492 $namespace = $title->getNamespace(); 00493 $dbKey = $title->getDBkey(); 00494 $restrictions = explode( ':', trim( $this->pageRestrictions[$pageId] ) ); 00495 foreach ( $restrictions as $restrict ) { 00496 $temp = explode( '=', trim( $restrict ) ); 00497 if ( count( $temp ) == 1 ) { 00498 // old old format should be treated as edit/move restriction 00499 $restriction = trim( $temp[0] ); 00500 00501 if ( $restriction == '' ) { 00502 continue; 00503 } 00504 $this->protections[$namespace][$dbKey][] = array( 00505 'type' => 'edit', 00506 'level' => $restriction, 00507 'expiry' => 'infinity', 00508 ); 00509 $this->protections[$namespace][$dbKey][] = array( 00510 'type' => 'move', 00511 'level' => $restriction, 00512 'expiry' => 'infinity', 00513 ); 00514 } else { 00515 $restriction = trim( $temp[1] ); 00516 if ( $restriction == '' ) { 00517 continue; 00518 } 00519 $this->protections[$namespace][$dbKey][] = array( 00520 'type' => $temp[0], 00521 'level' => $restriction, 00522 'expiry' => 'infinity', 00523 ); 00524 } 00525 } 00526 } 00527 } 00528 } 00529 00530 // Get protections for missing titles 00531 if ( count( $this->missing ) ) { 00532 $this->resetQueryParams(); 00533 $lb = new LinkBatch( $this->missing ); 00534 $this->addTables( 'protected_titles' ); 00535 $this->addFields( array( 'pt_title', 'pt_namespace', 'pt_create_perm', 'pt_expiry' ) ); 00536 $this->addWhere( $lb->constructSet( 'pt', $db ) ); 00537 $res = $this->select( __METHOD__ ); 00538 foreach ( $res as $row ) { 00539 $this->protections[$row->pt_namespace][$row->pt_title][] = array( 00540 'type' => 'create', 00541 'level' => $row->pt_create_perm, 00542 'expiry' => $wgContLang->formatExpiry( $row->pt_expiry, TS_ISO_8601 ) 00543 ); 00544 } 00545 } 00546 00547 // Cascading protections 00548 $images = $others = array(); 00549 foreach ( $this->everything as $title ) { 00550 if ( $title->getNamespace() == NS_FILE ) { 00551 $images[] = $title->getDBkey(); 00552 } else { 00553 $others[] = $title; 00554 } 00555 } 00556 00557 if ( count( $others ) ) { 00558 // Non-images: check templatelinks 00559 $lb = new LinkBatch( $others ); 00560 $this->resetQueryParams(); 00561 $this->addTables( array( 'page_restrictions', 'page', 'templatelinks' ) ); 00562 $this->addFields( array( 'pr_type', 'pr_level', 'pr_expiry', 00563 'page_title', 'page_namespace', 00564 'tl_title', 'tl_namespace' ) ); 00565 $this->addWhere( $lb->constructSet( 'tl', $db ) ); 00566 $this->addWhere( 'pr_page = page_id' ); 00567 $this->addWhere( 'pr_page = tl_from' ); 00568 $this->addWhereFld( 'pr_cascade', 1 ); 00569 00570 $res = $this->select( __METHOD__ ); 00571 foreach ( $res as $row ) { 00572 $source = Title::makeTitle( $row->page_namespace, $row->page_title ); 00573 $this->protections[$row->tl_namespace][$row->tl_title][] = array( 00574 'type' => $row->pr_type, 00575 'level' => $row->pr_level, 00576 'expiry' => $wgContLang->formatExpiry( $row->pr_expiry, TS_ISO_8601 ), 00577 'source' => $source->getPrefixedText() 00578 ); 00579 } 00580 } 00581 00582 if ( count( $images ) ) { 00583 // Images: check imagelinks 00584 $this->resetQueryParams(); 00585 $this->addTables( array( 'page_restrictions', 'page', 'imagelinks' ) ); 00586 $this->addFields( array( 'pr_type', 'pr_level', 'pr_expiry', 00587 'page_title', 'page_namespace', 'il_to' ) ); 00588 $this->addWhere( 'pr_page = page_id' ); 00589 $this->addWhere( 'pr_page = il_from' ); 00590 $this->addWhereFld( 'pr_cascade', 1 ); 00591 $this->addWhereFld( 'il_to', $images ); 00592 00593 $res = $this->select( __METHOD__ ); 00594 foreach ( $res as $row ) { 00595 $source = Title::makeTitle( $row->page_namespace, $row->page_title ); 00596 $this->protections[NS_FILE][$row->il_to][] = array( 00597 'type' => $row->pr_type, 00598 'level' => $row->pr_level, 00599 'expiry' => $wgContLang->formatExpiry( $row->pr_expiry, TS_ISO_8601 ), 00600 'source' => $source->getPrefixedText() 00601 ); 00602 } 00603 } 00604 } 00605 00610 private function getTSIDs() { 00611 $getTitles = $this->talkids = $this->subjectids = array(); 00612 00614 foreach ( $this->everything as $t ) { 00615 if ( MWNamespace::isTalk( $t->getNamespace() ) ) { 00616 if ( $this->fld_subjectid ) { 00617 $getTitles[] = $t->getSubjectPage(); 00618 } 00619 } elseif ( $this->fld_talkid ) { 00620 $getTitles[] = $t->getTalkPage(); 00621 } 00622 } 00623 if ( !count( $getTitles ) ) { 00624 return; 00625 } 00626 00627 $db = $this->getDB(); 00628 00629 // Construct a custom WHERE clause that matches 00630 // all titles in $getTitles 00631 $lb = new LinkBatch( $getTitles ); 00632 $this->resetQueryParams(); 00633 $this->addTables( 'page' ); 00634 $this->addFields( array( 'page_title', 'page_namespace', 'page_id' ) ); 00635 $this->addWhere( $lb->constructSet( 'page', $db ) ); 00636 $res = $this->select( __METHOD__ ); 00637 foreach ( $res as $row ) { 00638 if ( MWNamespace::isTalk( $row->page_namespace ) ) { 00639 $this->talkids[MWNamespace::getSubject( $row->page_namespace )][$row->page_title] = 00640 intval( $row->page_id ); 00641 } else { 00642 $this->subjectids[MWNamespace::getTalk( $row->page_namespace )][$row->page_title] = 00643 intval( $row->page_id ); 00644 } 00645 } 00646 } 00647 00648 private function getDisplayTitle() { 00649 $this->displaytitles = array(); 00650 00651 $pageIds = array_keys( $this->titles ); 00652 00653 if ( !count( $pageIds ) ) { 00654 return; 00655 } 00656 00657 $this->resetQueryParams(); 00658 $this->addTables( 'page_props' ); 00659 $this->addFields( array( 'pp_page', 'pp_value' ) ); 00660 $this->addWhereFld( 'pp_page', $pageIds ); 00661 $this->addWhereFld( 'pp_propname', 'displaytitle' ); 00662 $res = $this->select( __METHOD__ ); 00663 00664 foreach ( $res as $row ) { 00665 $this->displaytitles[$row->pp_page] = $row->pp_value; 00666 } 00667 } 00668 00673 private function getWatchedInfo() { 00674 $user = $this->getUser(); 00675 00676 if ( $user->isAnon() || count( $this->everything ) == 0 00677 || !$user->isAllowed( 'viewmywatchlist' ) 00678 ) { 00679 return; 00680 } 00681 00682 $this->watched = array(); 00683 $this->notificationtimestamps = array(); 00684 $db = $this->getDB(); 00685 00686 $lb = new LinkBatch( $this->everything ); 00687 00688 $this->resetQueryParams(); 00689 $this->addTables( array( 'watchlist' ) ); 00690 $this->addFields( array( 'wl_title', 'wl_namespace' ) ); 00691 $this->addFieldsIf( 'wl_notificationtimestamp', $this->fld_notificationtimestamp ); 00692 $this->addWhere( array( 00693 $lb->constructSet( 'wl', $db ), 00694 'wl_user' => $user->getID() 00695 ) ); 00696 00697 $res = $this->select( __METHOD__ ); 00698 00699 foreach ( $res as $row ) { 00700 if ( $this->fld_watched ) { 00701 $this->watched[$row->wl_namespace][$row->wl_title] = true; 00702 } 00703 if ( $this->fld_notificationtimestamp ) { 00704 $this->notificationtimestamps[$row->wl_namespace][$row->wl_title] = 00705 $row->wl_notificationtimestamp; 00706 } 00707 } 00708 } 00709 00713 private function getWatcherInfo() { 00714 global $wgUnwatchedPageThreshold; 00715 00716 if ( count( $this->everything ) == 0 ) { 00717 return; 00718 } 00719 00720 $user = $this->getUser(); 00721 $canUnwatchedpages = $user->isAllowed( 'unwatchedpages' ); 00722 if ( !$canUnwatchedpages && !is_int( $wgUnwatchedPageThreshold ) ) { 00723 return; 00724 } 00725 00726 $this->watchers = array(); 00727 $this->showZeroWatchers = $canUnwatchedpages; 00728 $db = $this->getDB(); 00729 00730 $lb = new LinkBatch( $this->everything ); 00731 00732 $this->resetQueryParams(); 00733 $this->addTables( array( 'watchlist' ) ); 00734 $this->addFields( array( 'wl_title', 'wl_namespace', 'count' => 'COUNT(*)' ) ); 00735 $this->addWhere( array( 00736 $lb->constructSet( 'wl', $db ) 00737 ) ); 00738 $this->addOption( 'GROUP BY', array( 'wl_namespace', 'wl_title' ) ); 00739 if ( !$canUnwatchedpages ) { 00740 $this->addOption( 'HAVING', "COUNT(*) >= $wgUnwatchedPageThreshold" ); 00741 } 00742 00743 $res = $this->select( __METHOD__ ); 00744 00745 foreach ( $res as $row ) { 00746 $this->watchers[$row->wl_namespace][$row->wl_title] = (int)$row->count; 00747 } 00748 } 00749 00750 public function getCacheMode( $params ) { 00751 $publicProps = array( 00752 'protection', 00753 'talkid', 00754 'subjectid', 00755 'url', 00756 'preload', 00757 'displaytitle', 00758 ); 00759 if ( !is_null( $params['prop'] ) ) { 00760 foreach ( $params['prop'] as $prop ) { 00761 if ( !in_array( $prop, $publicProps ) ) { 00762 return 'private'; 00763 } 00764 } 00765 } 00766 if ( !is_null( $params['token'] ) ) { 00767 return 'private'; 00768 } 00769 00770 return 'public'; 00771 } 00772 00773 public function getAllowedParams() { 00774 return array( 00775 'prop' => array( 00776 ApiBase::PARAM_DFLT => null, 00777 ApiBase::PARAM_ISMULTI => true, 00778 ApiBase::PARAM_TYPE => array( 00779 'protection', 00780 'talkid', 00781 'watched', # private 00782 'watchers', # private 00783 'notificationtimestamp', # private 00784 'subjectid', 00785 'url', 00786 'readable', # private 00787 'preload', 00788 'displaytitle', 00789 // If you add more properties here, please consider whether they 00790 // need to be added to getCacheMode() 00791 ) ), 00792 'token' => array( 00793 ApiBase::PARAM_DFLT => null, 00794 ApiBase::PARAM_ISMULTI => true, 00795 ApiBase::PARAM_TYPE => array_keys( $this->getTokenFunctions() ) 00796 ), 00797 'continue' => null, 00798 ); 00799 } 00800 00801 public function getParamDescription() { 00802 return array( 00803 'prop' => array( 00804 'Which additional properties to get:', 00805 ' protection - List the protection level of each page', 00806 ' talkid - The page ID of the talk page for each non-talk page', 00807 ' watched - List the watched status of each page', 00808 ' watchers - The number of watchers, if allowed', 00809 ' notificationtimestamp - The watchlist notification timestamp of each page', 00810 ' subjectid - The page ID of the parent page for each talk page', 00811 ' url - Gives a full URL to the page, and also an edit URL', 00812 ' readable - Whether the user can read this page', 00813 ' preload - Gives the text returned by EditFormPreloadText', 00814 ' displaytitle - Gives the way the page title is actually displayed', 00815 ), 00816 'token' => 'Request a token to perform a data-modifying action on a page', 00817 'continue' => 'When more results are available, use this to continue', 00818 ); 00819 } 00820 00821 public function getResultProperties() { 00822 $props = array( 00823 ApiBase::PROP_LIST => false, 00824 '' => array( 00825 'touched' => 'timestamp', 00826 'lastrevid' => 'integer', 00827 'counter' => array( 00828 ApiBase::PROP_TYPE => 'integer', 00829 ApiBase::PROP_NULLABLE => true 00830 ), 00831 'length' => 'integer', 00832 'redirect' => 'boolean', 00833 'new' => 'boolean', 00834 'starttimestamp' => array( 00835 ApiBase::PROP_TYPE => 'timestamp', 00836 ApiBase::PROP_NULLABLE => true 00837 ), 00838 'contentmodel' => 'string', 00839 ), 00840 'watched' => array( 00841 'watched' => 'boolean' 00842 ), 00843 'watchers' => array( 00844 'watchers' => array( 00845 ApiBase::PROP_TYPE => 'integer', 00846 ApiBase::PROP_NULLABLE => true 00847 ) 00848 ), 00849 'notificationtimestamp' => array( 00850 'notificationtimestamp' => array( 00851 ApiBase::PROP_TYPE => 'timestamp', 00852 ApiBase::PROP_NULLABLE => true 00853 ) 00854 ), 00855 'talkid' => array( 00856 'talkid' => array( 00857 ApiBase::PROP_TYPE => 'integer', 00858 ApiBase::PROP_NULLABLE => true 00859 ) 00860 ), 00861 'subjectid' => array( 00862 'subjectid' => array( 00863 ApiBase::PROP_TYPE => 'integer', 00864 ApiBase::PROP_NULLABLE => true 00865 ) 00866 ), 00867 'url' => array( 00868 'fullurl' => 'string', 00869 'editurl' => 'string' 00870 ), 00871 'readable' => array( 00872 'readable' => 'boolean' 00873 ), 00874 'preload' => array( 00875 'preload' => 'string' 00876 ), 00877 'displaytitle' => array( 00878 'displaytitle' => 'string' 00879 ) 00880 ); 00881 00882 self::addTokenProperties( $props, $this->getTokenFunctions() ); 00883 00884 return $props; 00885 } 00886 00887 public function getDescription() { 00888 return 'Get basic page information such as namespace, title, last touched date, ...'; 00889 } 00890 00891 public function getExamples() { 00892 return array( 00893 'api.php?action=query&prop=info&titles=Main%20Page', 00894 'api.php?action=query&prop=info&inprop=protection&titles=Main%20Page' 00895 ); 00896 } 00897 00898 public function getHelpUrls() { 00899 return 'https://www.mediawiki.org/wiki/API:Properties#info_.2F_in'; 00900 } 00901 }