884473eac162c8b2eaba1535403458e55fccdeec
4 * Created on Sep 25, 2006
6 * API for MediaWiki 1.8+
8 * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
23 * http://www.gnu.org/copyleft/gpl.html
26 if (!defined('MEDIAWIKI')) {
27 // Eclipse helper - will be ignored in production
28 require_once ('ApiQueryBase.php');
32 * A query module to show basic page information.
36 class ApiQueryInfo
extends ApiQueryBase
{
38 public function __construct($query, $moduleName) {
39 parent
:: __construct($query, $moduleName, 'in');
42 public function requestExtraData($pageSet) {
43 $pageSet->requestField('page_restrictions');
44 $pageSet->requestField('page_is_redirect');
45 $pageSet->requestField('page_is_new');
46 $pageSet->requestField('page_counter');
47 $pageSet->requestField('page_touched');
48 $pageSet->requestField('page_latest');
49 $pageSet->requestField('page_len');
52 protected function getTokenFunctions() {
53 // tokenname => function
54 // function prototype is func($pageid, $title)
55 // should return token or false
57 // Don't call the hooks twice
58 if(isset($this->tokenFunctions
))
59 return $this->tokenFunctions
;
61 // If we're in JSON callback mode, no tokens can be obtained
62 if(!is_null($this->getMain()->getRequest()->getVal('callback')))
65 $this->tokenFunctions
= array(
66 'edit' => array( 'ApiQueryInfo', 'getEditToken' ),
67 'delete' => array( 'ApiQueryInfo', 'getDeleteToken' ),
68 'protect' => array( 'ApiQueryInfo', 'getProtectToken' ),
69 'move' => array( 'ApiQueryInfo', 'getMoveToken' ),
70 'block' => array( 'ApiQueryInfo', 'getBlockToken' ),
71 'unblock' => array( 'ApiQueryInfo', 'getUnblockToken' ),
72 'email' => array( 'ApiQueryInfo', 'getEmailToken' ),
74 wfRunHooks('APIQueryInfoTokens', array(&$this->tokenFunctions
));
75 return $this->tokenFunctions
;
78 public static function getEditToken($pageid, $title)
80 // We could check for $title->userCan('edit') here,
81 // but that's too expensive for this purpose
83 if(!$wgUser->isAllowed('edit'))
86 // The edit token is always the same, let's exploit that
87 static $cachedEditToken = null;
88 if(!is_null($cachedEditToken))
89 return $cachedEditToken;
91 $cachedEditToken = $wgUser->editToken();
92 return $cachedEditToken;
95 public static function getDeleteToken($pageid, $title)
98 if(!$wgUser->isAllowed('delete'))
101 static $cachedDeleteToken = null;
102 if(!is_null($cachedDeleteToken))
103 return $cachedDeleteToken;
105 $cachedDeleteToken = $wgUser->editToken();
106 return $cachedDeleteToken;
109 public static function getProtectToken($pageid, $title)
112 if(!$wgUser->isAllowed('protect'))
115 static $cachedProtectToken = null;
116 if(!is_null($cachedProtectToken))
117 return $cachedProtectToken;
119 $cachedProtectToken = $wgUser->editToken();
120 return $cachedProtectToken;
123 public static function getMoveToken($pageid, $title)
126 if(!$wgUser->isAllowed('move'))
129 static $cachedMoveToken = null;
130 if(!is_null($cachedMoveToken))
131 return $cachedMoveToken;
133 $cachedMoveToken = $wgUser->editToken();
134 return $cachedMoveToken;
137 public static function getBlockToken($pageid, $title)
140 if(!$wgUser->isAllowed('block'))
143 static $cachedBlockToken = null;
144 if(!is_null($cachedBlockToken))
145 return $cachedBlockToken;
147 $cachedBlockToken = $wgUser->editToken();
148 return $cachedBlockToken;
151 public static function getUnblockToken($pageid, $title)
153 // Currently, this is exactly the same as the block token
154 return self
::getBlockToken($pageid, $title);
157 public static function getEmailToken($pageid, $title)
160 if(!$wgUser->canSendEmail() ||
$wgUser->isBlockedFromEmailUser())
163 static $cachedEmailToken = null;
164 if(!is_null($cachedEmailToken))
165 return $cachedEmailToken;
167 $cachedEmailToken = $wgUser->editToken();
168 return $cachedEmailToken;
171 public function execute() {
175 $params = $this->extractRequestParams();
176 $fld_protection = $fld_talkid = $fld_subjectid = false;
177 if(!is_null($params['prop'])) {
178 $prop = array_flip($params['prop']);
179 $fld_protection = isset($prop['protection']);
180 $fld_talkid = isset($prop['talkid']);
181 $fld_subjectid = isset($prop['subjectid']);
184 $pageSet = $this->getPageSet();
185 $titles = $pageSet->getGoodTitles();
186 $missing = $pageSet->getMissingTitles();
187 $result = $this->getResult();
189 $pageRestrictions = $pageSet->getCustomField('page_restrictions');
190 $pageIsRedir = $pageSet->getCustomField('page_is_redirect');
191 $pageIsNew = $pageSet->getCustomField('page_is_new');
192 $pageCounter = $pageSet->getCustomField('page_counter');
193 $pageTouched = $pageSet->getCustomField('page_touched');
194 $pageLatest = $pageSet->getCustomField('page_latest');
195 $pageLength = $pageSet->getCustomField('page_len');
197 $db = $this->getDB();
198 if ($fld_protection && !empty($titles)) {
199 $this->addTables('page_restrictions');
200 $this->addFields(array('pr_page', 'pr_type', 'pr_level', 'pr_expiry', 'pr_cascade'));
201 $this->addWhereFld('pr_page', array_keys($titles));
203 $res = $this->select(__METHOD__
);
204 while($row = $db->fetchObject($res)) {
206 'type' => $row->pr_type
,
207 'level' => $row->pr_level
,
208 'expiry' => Block
::decodeExpiry( $row->pr_expiry
, TS_ISO_8601
)
212 $protections[$row->pr_page
][] = $a;
214 $db->freeResult($res);
217 foreach ($titles as $id => $title)
218 if ($title->getNamespace() == NS_IMAGE
)
220 // To avoid code duplication
221 $cascadeTypes = array(
224 'table' => 'templatelinks',
225 'ns' => 'tl_namespace',
226 'title' => 'tl_title',
227 'ids' => array_diff(array_keys($titles), $imageIds)
231 'table' => 'imagelinks',
238 foreach ($cascadeTypes as $type)
240 if (count($type['ids']) != 0) {
241 $this->resetQueryParams();
242 $this->addTables(array('page_restrictions', $type['table']));
243 $this->addTables('page', 'page_source');
244 $this->addTables('page', 'page_target');
245 $this->addFields(array('pr_type', 'pr_level', 'pr_expiry',
246 'page_target.page_id AS page_target_id',
247 'page_source.page_namespace AS page_source_namespace',
248 'page_source.page_title AS page_source_title'));
249 $this->addWhere(array("{$type['prefix']}_from = pr_page",
250 'page_target.page_namespace = '.$type['ns'],
251 'page_target.page_title = '.$type['title'],
252 'page_source.page_id = pr_page'
254 $this->addWhereFld('pr_cascade', 1);
255 $this->addWhereFld('page_target.page_id', $type['ids']);
257 $res = $this->select(__METHOD__
);
258 while($row = $db->fetchObject($res)) {
259 $source = Title
::makeTitle($row->page_source_namespace
, $row->page_source_title
);
261 'type' => $row->pr_type
,
262 'level' => $row->pr_level
,
263 'expiry' => Block
::decodeExpiry( $row->pr_expiry
, TS_ISO_8601
),
264 'source' => $source->getPrefixedText()
266 $protections[$row->page_target_id
][] = $a;
268 $db->freeResult($res);
273 // We don't need to check for pt stuff if there are no nonexistent titles
274 if($fld_protection && !empty($missing))
276 $this->resetQueryParams();
277 // Construct a custom WHERE clause that matches all titles in $missing
278 $lb = new LinkBatch($missing);
279 $this->addTables('protected_titles');
280 $this->addFields(array('pt_title', 'pt_namespace', 'pt_create_perm', 'pt_expiry'));
281 $this->addWhere($lb->constructSet('pt', $db));
282 $res = $this->select(__METHOD__
);
283 $prottitles = array();
284 while($row = $db->fetchObject($res)) {
285 $prottitles[$row->pt_namespace
][$row->pt_title
][] = array(
287 'level' => $row->pt_create_perm
,
288 'expiry' => Block
::decodeExpiry($row->pt_expiry
, TS_ISO_8601
)
291 $db->freeResult($res);
295 foreach ($missing as $title)
296 if ($title->getNamespace() == NS_IMAGE
)
297 $images[] = $title->getDbKey();
301 if (count($others) != 0) {
302 $lb = new LinkBatch($others);
303 $this->resetQueryParams();
304 $this->addTables(array('page_restrictions', 'page', 'templatelinks'));
305 $this->addFields(array('pr_type', 'pr_level', 'pr_expiry',
306 'page_title', 'page_namespace',
307 'tl_title', 'tl_namespace'));
308 $this->addWhere($lb->constructSet('tl', $db));
309 $this->addWhere('pr_page = page_id');
310 $this->addWhere('pr_page = tl_from');
311 $this->addWhereFld('pr_cascade', 1);
313 $res = $this->select(__METHOD__
);
314 while($row = $db->fetchObject($res)) {
315 $source = Title
::makeTitle($row->page_namespace
, $row->page_title
);
317 'type' => $row->pr_type
,
318 'level' => $row->pr_level
,
319 'expiry' => Block
::decodeExpiry( $row->pr_expiry
, TS_ISO_8601
),
320 'source' => $source->getPrefixedText()
322 $prottitles[$row->tl_namespace
][$row->tl_title
][] = $a;
324 $db->freeResult($res);
327 if (count($images) != 0) {
328 $this->resetQueryParams();
329 $this->addTables(array('page_restrictions', 'page', 'imagelinks'));
330 $this->addFields(array('pr_type', 'pr_level', 'pr_expiry',
331 'page_title', 'page_namespace', 'il_to'));
332 $this->addWhere('pr_page = page_id');
333 $this->addWhere('pr_page = il_from');
334 $this->addWhereFld('pr_cascade', 1);
335 $this->addWhereFld('il_to', $images);
337 $res = $this->select(__METHOD__
);
338 while($row = $db->fetchObject($res)) {
339 $source = Title
::makeTitle($row->page_namespace
, $row->page_title
);
341 'type' => $row->pr_type
,
342 'level' => $row->pr_level
,
343 'expiry' => Block
::decodeExpiry( $row->pr_expiry
, TS_ISO_8601
),
344 'source' => $source->getPrefixedText()
346 $prottitles[NS_IMAGE
][$row->il_to
][] = $a;
348 $db->freeResult($res);
352 // Run the talkid/subjectid query
353 if($fld_talkid ||
$fld_subjectid)
355 $talktitles = $subjecttitles =
356 $talkids = $subjectids = array();
357 $everything = array_merge($titles, $missing);
358 foreach($everything as $t)
360 if(MWNamespace
::isTalk($t->getNamespace()))
363 $subjecttitles[] = $t->getSubjectPage();
366 $talktitles[] = $t->getTalkPage();
368 if(!empty($talktitles) ||
!empty($subjecttitles))
370 // Construct a custom WHERE clause that matches
371 // all titles in $talktitles and $subjecttitles
372 $lb = new LinkBatch(array_merge($talktitles, $subjecttitles));
373 $this->resetQueryParams();
374 $this->addTables('page');
375 $this->addFields(array('page_title', 'page_namespace', 'page_id'));
376 $this->addWhere($lb->constructSet('page', $db));
377 $res = $this->select(__METHOD__
);
378 while($row = $db->fetchObject($res))
380 if(MWNamespace
::isTalk($row->page_namespace
))
381 $talkids[MWNamespace
::getSubject($row->page_namespace
)][$row->page_title
] = $row->page_id
;
383 $subjectids[MWNamespace
::getTalk($row->page_namespace
)][$row->page_title
] = $row->page_id
;
388 foreach ( $titles as $pageid => $title ) {
390 'touched' => wfTimestamp(TS_ISO_8601
, $pageTouched[$pageid]),
391 'lastrevid' => intval($pageLatest[$pageid]),
392 'counter' => intval($pageCounter[$pageid]),
393 'length' => intval($pageLength[$pageid]),
396 if ($pageIsRedir[$pageid])
397 $pageInfo['redirect'] = '';
399 if ($pageIsNew[$pageid])
400 $pageInfo['new'] = '';
402 if (!is_null($params['token'])) {
403 $tokenFunctions = $this->getTokenFunctions();
404 foreach($params['token'] as $t)
406 $val = call_user_func($tokenFunctions[$t], $pageid, $title);
408 $this->setWarning("Action '$t' is not allowed for the current user");
410 $pageInfo[$t . 'token'] = $val;
414 if($fld_protection) {
415 if (isset($protections[$pageid])) {
416 $pageInfo['protection'] = $protections[$pageid];
417 $result->setIndexedTagName($pageInfo['protection'], 'pr');
419 # Also check old restrictions
420 if( $pageRestrictions[$pageid] ) {
421 foreach( explode( ':', trim( $pageRestrictions[$pageid] ) ) as $restrict ) {
422 $temp = explode( '=', trim( $restrict ) );
423 if(count($temp) == 1) {
424 // old old format should be treated as edit/move restriction
425 $restriction = trim( $temp[0] );
426 $pageInfo['protection'][] = array(
428 'level' => $restriction,
429 'expiry' => 'infinity',
431 $pageInfo['protection'][] = array(
433 'level' => $restriction,
434 'expiry' => 'infinity',
437 $restriction = trim( $temp[1] );
438 $pageInfo['protection'][] = array(
440 'level' => $restriction,
441 'expiry' => 'infinity',
445 $result->setIndexedTagName($pageInfo['protection'], 'pr');
447 $pageInfo['protection'] = array();
451 if($fld_talkid && isset($talkids[$title->getNamespace()][$title->getDbKey()]))
452 $pageInfo['talkid'] = $talkids[$title->getNamespace()][$title->getDbKey()];
453 if($fld_subjectid && isset($subjectids[$title->getNamespace()][$title->getDbKey()]))
454 $pageInfo['subjectid'] = $subjectids[$title->getNamespace()][$title->getDbKey()];
456 $result->addValue(array (
459 ), $pageid, $pageInfo);
462 // Get edit/protect tokens and protection data for missing titles if requested
463 // Delete and move tokens are N/A for missing titles anyway
464 if(!is_null($params['token']) ||
$fld_protection ||
$fld_talkid ||
$fld_subjectid)
466 $res = &$result->getData();
467 foreach($missing as $pageid => $title) {
468 if(!is_null($params['token']))
470 $tokenFunctions = $this->getTokenFunctions();
471 foreach($params['token'] as $t)
473 $val = call_user_func($tokenFunctions[$t], $pageid, $title);
475 $res['query']['pages'][$pageid][$t . 'token'] = $val;
480 // Apparently the XML formatting code doesn't like array(null)
481 // This is painful to fix, so we'll just work around it
482 if(isset($prottitles[$title->getNamespace()][$title->getDBkey()]))
483 $res['query']['pages'][$pageid]['protection'] = $prottitles[$title->getNamespace()][$title->getDBkey()];
485 $res['query']['pages'][$pageid]['protection'] = array();
486 $result->setIndexedTagName($res['query']['pages'][$pageid]['protection'], 'pr');
488 if($fld_talkid && isset($talkids[$title->getNamespace()][$title->getDbKey()]))
489 $res['query']['pages'][$pageid]['talkid'] = $talkids[$title->getNamespace()][$title->getDbKey()];
490 if($fld_subjectid && isset($subjectids[$title->getNamespace()][$title->getDbKey()]))
491 $res['query']['pages'][$pageid]['subjectid'] = $subjectids[$title->getNamespace()][$title->getDbKey()];
496 public function getAllowedParams() {
499 ApiBase
:: PARAM_DFLT
=> NULL,
500 ApiBase
:: PARAM_ISMULTI
=> true,
501 ApiBase
:: PARAM_TYPE
=> array (
507 ApiBase
:: PARAM_DFLT
=> NULL,
508 ApiBase
:: PARAM_ISMULTI
=> true,
509 ApiBase
:: PARAM_TYPE
=> array_keys($this->getTokenFunctions())
514 public function getParamDescription() {
517 'Which additional properties to get:',
518 ' "protection" - List the protection level of each page',
519 ' "talkid" - The page ID of the talk page for each non-talk page',
520 ' "subjectid" - The page ID of the parent page for each talk page'
522 'token' => 'Request a token to perform a data-modifying action on a page',
527 public function getDescription() {
528 return 'Get basic page information such as namespace, title, last touched date, ...';
531 protected function getExamples() {
533 'api.php?action=query&prop=info&titles=Main%20Page',
534 'api.php?action=query&prop=info&inprop=protection&titles=Main%20Page'
538 public function getVersion() {
539 return __CLASS__
. ': $Id$';