From c426593d8a9d802727d24abc84a31e15a01393c7 Mon Sep 17 00:00:00 2001 From: Roan Kattouw Date: Fri, 13 Feb 2009 14:44:19 +0000 Subject: [PATCH] --- includes/api/ApiQueryInfo.php | 1134 +++++++++++++++++++---------------------- 1 file changed, 527 insertions(+), 607 deletions(-) rewrite includes/api/ApiQueryInfo.php (61%) diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php dissimilarity index 61% index 9c87b77943b..86a6af71a67 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -1,607 +1,527 @@ -@gmail.com - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - * http://www.gnu.org/copyleft/gpl.html - */ - -if (!defined('MEDIAWIKI')) { - // Eclipse helper - will be ignored in production - require_once ('ApiQueryBase.php'); -} - -/** - * A query module to show basic page information. - * - * @ingroup API - */ -class ApiQueryInfo extends ApiQueryBase { - - public function __construct($query, $moduleName) { - parent :: __construct($query, $moduleName, 'in'); - } - - public function requestExtraData($pageSet) { - $pageSet->requestField('page_restrictions'); - $pageSet->requestField('page_is_redirect'); - $pageSet->requestField('page_is_new'); - $pageSet->requestField('page_counter'); - $pageSet->requestField('page_touched'); - $pageSet->requestField('page_latest'); - $pageSet->requestField('page_len'); - } - - protected function getTokenFunctions() { - // tokenname => function - // function prototype is func($pageid, $title) - // should return token or false - - // Don't call the hooks twice - if(isset($this->tokenFunctions)) - return $this->tokenFunctions; - - // If we're in JSON callback mode, no tokens can be obtained - if(!is_null($this->getMain()->getRequest()->getVal('callback'))) - return array(); - - $this->tokenFunctions = array( - 'edit' => array( 'ApiQueryInfo', 'getEditToken' ), - 'delete' => array( 'ApiQueryInfo', 'getDeleteToken' ), - 'protect' => array( 'ApiQueryInfo', 'getProtectToken' ), - 'move' => array( 'ApiQueryInfo', 'getMoveToken' ), - 'block' => array( 'ApiQueryInfo', 'getBlockToken' ), - 'unblock' => array( 'ApiQueryInfo', 'getUnblockToken' ), - 'email' => array( 'ApiQueryInfo', 'getEmailToken' ), - 'import' => array( 'ApiQueryInfo', 'getImportToken' ), - ); - wfRunHooks('APIQueryInfoTokens', array(&$this->tokenFunctions)); - return $this->tokenFunctions; - } - - public static function getEditToken($pageid, $title) - { - // We could check for $title->userCan('edit') here, - // but that's too expensive for this purpose - global $wgUser; - if(!$wgUser->isAllowed('edit')) - return false; - - // The edit token is always the same, let's exploit that - static $cachedEditToken = null; - if(!is_null($cachedEditToken)) - return $cachedEditToken; - - $cachedEditToken = $wgUser->editToken(); - return $cachedEditToken; - } - - public static function getDeleteToken($pageid, $title) - { - global $wgUser; - if(!$wgUser->isAllowed('delete')) - return false; - - static $cachedDeleteToken = null; - if(!is_null($cachedDeleteToken)) - return $cachedDeleteToken; - - $cachedDeleteToken = $wgUser->editToken(); - return $cachedDeleteToken; - } - - public static function getProtectToken($pageid, $title) - { - global $wgUser; - if(!$wgUser->isAllowed('protect')) - return false; - - static $cachedProtectToken = null; - if(!is_null($cachedProtectToken)) - return $cachedProtectToken; - - $cachedProtectToken = $wgUser->editToken(); - return $cachedProtectToken; - } - - public static function getMoveToken($pageid, $title) - { - global $wgUser; - if(!$wgUser->isAllowed('move')) - return false; - - static $cachedMoveToken = null; - if(!is_null($cachedMoveToken)) - return $cachedMoveToken; - - $cachedMoveToken = $wgUser->editToken(); - return $cachedMoveToken; - } - - public static function getBlockToken($pageid, $title) - { - global $wgUser; - if(!$wgUser->isAllowed('block')) - return false; - - static $cachedBlockToken = null; - if(!is_null($cachedBlockToken)) - return $cachedBlockToken; - - $cachedBlockToken = $wgUser->editToken(); - return $cachedBlockToken; - } - - public static function getUnblockToken($pageid, $title) - { - // Currently, this is exactly the same as the block token - return self::getBlockToken($pageid, $title); - } - - public static function getEmailToken($pageid, $title) - { - global $wgUser; - if(!$wgUser->canSendEmail() || $wgUser->isBlockedFromEmailUser()) - return false; - - static $cachedEmailToken = null; - if(!is_null($cachedEmailToken)) - return $cachedEmailToken; - - $cachedEmailToken = $wgUser->editToken(); - return $cachedEmailToken; - } - - public static function getImportToken($pageid, $title) - { - global $wgUser; - if(!$wgUser->isAllowed('import')) - return false; - - static $cachedImportToken = null; - if(!is_null($cachedImportToken)) - return $cachedImportToken; - - $cachedImportToken = $wgUser->editToken(); - return $cachedImportToken; - } - - public function execute() { - - global $wgUser; - - $params = $this->extractRequestParams(); - $fld_protection = $fld_talkid = $fld_subjectid = $fld_url = $fld_readable = false; - if(!is_null($params['prop'])) { - $prop = array_flip($params['prop']); - $fld_protection = isset($prop['protection']); - $fld_talkid = isset($prop['talkid']); - $fld_subjectid = isset($prop['subjectid']); - $fld_url = isset($prop['url']); - $fld_readable = isset($prop['readable']); - } - - $pageSet = $this->getPageSet(); - $titles = $pageSet->getGoodTitles(); - $missing = $pageSet->getMissingTitles(); - $result = $this->getResult(); - - $pageRestrictions = $pageSet->getCustomField('page_restrictions'); - $pageIsRedir = $pageSet->getCustomField('page_is_redirect'); - $pageIsNew = $pageSet->getCustomField('page_is_new'); - $pageCounter = $pageSet->getCustomField('page_counter'); - $pageTouched = $pageSet->getCustomField('page_touched'); - $pageLatest = $pageSet->getCustomField('page_latest'); - $pageLength = $pageSet->getCustomField('page_len'); - - $db = $this->getDB(); - if ($fld_protection && count($titles)) { - $this->addTables('page_restrictions'); - $this->addFields(array('pr_page', 'pr_type', 'pr_level', 'pr_expiry', 'pr_cascade')); - $this->addWhereFld('pr_page', array_keys($titles)); - - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) { - $a = array( - 'type' => $row->pr_type, - 'level' => $row->pr_level, - 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ) - ); - if($row->pr_cascade) - $a['cascade'] = ''; - $protections[$row->pr_page][] = $a; - - # Also check old restrictions - if($pageRestrictions[$row->pr_page]) { - foreach(explode(':', trim($pageRestrictions[$pageid])) as $restrict) { - $temp = explode('=', trim($restrict)); - if(count($temp) == 1) { - // old old format should be treated as edit/move restriction - $restriction = trim( $temp[0] ); - if($restriction == '') - continue; - $protections[$row->pr_page][] = array( - 'type' => 'edit', - 'level' => $restriction, - 'expiry' => 'infinity', - ); - $protections[$row->pr_page][] = array( - 'type' => 'move', - 'level' => $restriction, - 'expiry' => 'infinity', - ); - } else { - $restriction = trim( $temp[1] ); - if($restriction == '') - continue; - $protections[$row->pr_page][] = array( - 'type' => $temp[0], - 'level' => $restriction, - 'expiry' => 'infinity', - ); - } - } - } - } - $db->freeResult($res); - - $imageIds = array(); - foreach ($titles as $id => $title) - if ($title->getNamespace() == NS_FILE) - $imageIds[] = $id; - // To avoid code duplication - $cascadeTypes = array( - array( - 'prefix' => 'tl', - 'table' => 'templatelinks', - 'ns' => 'tl_namespace', - 'title' => 'tl_title', - 'ids' => array_diff(array_keys($titles), $imageIds) - ), - array( - 'prefix' => 'il', - 'table' => 'imagelinks', - 'ns' => NS_FILE, - 'title' => 'il_to', - 'ids' => $imageIds - ) - ); - - foreach ($cascadeTypes as $type) - { - if (count($type['ids']) != 0) { - $this->resetQueryParams(); - $this->addTables(array('page_restrictions', $type['table'])); - $this->addTables('page', 'page_source'); - $this->addTables('page', 'page_target'); - $this->addFields(array('pr_type', 'pr_level', 'pr_expiry', - 'page_target.page_id AS page_target_id', - 'page_source.page_namespace AS page_source_namespace', - 'page_source.page_title AS page_source_title')); - $this->addWhere(array("{$type['prefix']}_from = pr_page", - 'page_target.page_namespace = '.$type['ns'], - 'page_target.page_title = '.$type['title'], - 'page_source.page_id = pr_page' - )); - $this->addWhereFld('pr_cascade', 1); - $this->addWhereFld('page_target.page_id', $type['ids']); - - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) { - $source = Title::makeTitle($row->page_source_namespace, $row->page_source_title); - $a = array( - 'type' => $row->pr_type, - 'level' => $row->pr_level, - 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ), - 'source' => $source->getPrefixedText() - ); - $protections[$row->page_target_id][] = $a; - } - $db->freeResult($res); - } - } - } - - // We don't need to check for pt stuff if there are no nonexistent titles - if($fld_protection && count($missing)) - { - $this->resetQueryParams(); - // Construct a custom WHERE clause that matches all titles in $missing - $lb = new LinkBatch($missing); - $this->addTables('protected_titles'); - $this->addFields(array('pt_title', 'pt_namespace', 'pt_create_perm', 'pt_expiry')); - $this->addWhere($lb->constructSet('pt', $db)); - $res = $this->select(__METHOD__); - $prottitles = array(); - while($row = $db->fetchObject($res)) { - $prottitles[$row->pt_namespace][$row->pt_title][] = array( - 'type' => 'create', - 'level' => $row->pt_create_perm, - 'expiry' => Block::decodeExpiry($row->pt_expiry, TS_ISO_8601) - ); - } - $db->freeResult($res); - - $images = array(); - $others = array(); - foreach ($missing as $title) - if ($title->getNamespace() == NS_FILE) - $images[] = $title->getDBKey(); - else - $others[] = $title; - - if (count($others) != 0) { - $lb = new LinkBatch($others); - $this->resetQueryParams(); - $this->addTables(array('page_restrictions', 'page', 'templatelinks')); - $this->addFields(array('pr_type', 'pr_level', 'pr_expiry', - 'page_title', 'page_namespace', - 'tl_title', 'tl_namespace')); - $this->addWhere($lb->constructSet('tl', $db)); - $this->addWhere('pr_page = page_id'); - $this->addWhere('pr_page = tl_from'); - $this->addWhereFld('pr_cascade', 1); - - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) { - $source = Title::makeTitle($row->page_namespace, $row->page_title); - $a = array( - 'type' => $row->pr_type, - 'level' => $row->pr_level, - 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ), - 'source' => $source->getPrefixedText() - ); - $prottitles[$row->tl_namespace][$row->tl_title][] = $a; - } - $db->freeResult($res); - } - - if (count($images) != 0) { - $this->resetQueryParams(); - $this->addTables(array('page_restrictions', 'page', 'imagelinks')); - $this->addFields(array('pr_type', 'pr_level', 'pr_expiry', - 'page_title', 'page_namespace', 'il_to')); - $this->addWhere('pr_page = page_id'); - $this->addWhere('pr_page = il_from'); - $this->addWhereFld('pr_cascade', 1); - $this->addWhereFld('il_to', $images); - - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) { - $source = Title::makeTitle($row->page_namespace, $row->page_title); - $a = array( - 'type' => $row->pr_type, - 'level' => $row->pr_level, - 'expiry' => Block::decodeExpiry( $row->pr_expiry, TS_ISO_8601 ), - 'source' => $source->getPrefixedText() - ); - $prottitles[NS_FILE][$row->il_to][] = $a; - } - $db->freeResult($res); - } - } - - // Run the talkid/subjectid query - if($fld_talkid || $fld_subjectid) - { - $talktitles = $subjecttitles = - $talkids = $subjectids = array(); - $everything = array_merge($titles, $missing); - foreach($everything as $t) - { - if(MWNamespace::isTalk($t->getNamespace())) - { - if($fld_subjectid) - $subjecttitles[] = $t->getSubjectPage(); - } - else if($fld_talkid) - $talktitles[] = $t->getTalkPage(); - } - if(count($talktitles) || count($subjecttitles)) - { - // Construct a custom WHERE clause that matches - // all titles in $talktitles and $subjecttitles - $lb = new LinkBatch(array_merge($talktitles, $subjecttitles)); - $this->resetQueryParams(); - $this->addTables('page'); - $this->addFields(array('page_title', 'page_namespace', 'page_id')); - $this->addWhere($lb->constructSet('page', $db)); - $res = $this->select(__METHOD__); - while($row = $db->fetchObject($res)) - { - if(MWNamespace::isTalk($row->page_namespace)) - $talkids[MWNamespace::getSubject($row->page_namespace)][$row->page_title] = $row->page_id; - else - $subjectids[MWNamespace::getTalk($row->page_namespace)][$row->page_title] = $row->page_id; - } - } - } - - $count = 0; - $fit = true; - ksort($titles); // Ensure they're always in the same order - foreach ( $titles as $pageid => $title ) { - $count++; - if(!is_null($params['continue'])) - if($count < $params['continue']) - continue; - $pageInfo = array ( - 'touched' => wfTimestamp(TS_ISO_8601, $pageTouched[$pageid]), - 'lastrevid' => intval($pageLatest[$pageid]), - 'counter' => intval($pageCounter[$pageid]), - 'length' => intval($pageLength[$pageid]), - ); - - if ($pageIsRedir[$pageid]) - $pageInfo['redirect'] = ''; - - if ($pageIsNew[$pageid]) - $pageInfo['new'] = ''; - - if (!is_null($params['token'])) { - $tokenFunctions = $this->getTokenFunctions(); - $pageInfo['starttimestamp'] = wfTimestamp(TS_ISO_8601, time()); - foreach($params['token'] as $t) - { - $val = call_user_func($tokenFunctions[$t], $pageid, $title); - if($val === false) - $this->setWarning("Action '$t' is not allowed for the current user"); - else - $pageInfo[$t . 'token'] = $val; - } - } - - if($fld_protection) { - $pageInfo['protection'] = array(); - if (isset($protections[$pageid])) { - $pageInfo['protection'] = $protections[$pageid]; - $result->setIndexedTagName($pageInfo['protection'], 'pr'); - } - } - if($fld_talkid && isset($talkids[$title->getNamespace()][$title->getDBKey()])) - $pageInfo['talkid'] = $talkids[$title->getNamespace()][$title->getDBKey()]; - if($fld_subjectid && isset($subjectids[$title->getNamespace()][$title->getDBKey()])) - $pageInfo['subjectid'] = $subjectids[$title->getNamespace()][$title->getDBKey()]; - if($fld_url) { - $pageInfo['fullurl'] = $title->getFullURL(); - $pageInfo['editurl'] = $title->getFullURL('action=edit'); - } - if($fld_readable) - if($title->userCanRead()) - $pageInfo['readable'] = ''; - - $fit = $result->addValue(array ( - 'query', - 'pages' - ), $pageid, $pageInfo); - if(!$fit) - { - $this->setContinueEnumParameter('continue', $count); - break; - } - } - - // Get properties for missing titles if requested - if($fit && (!is_null($params['token']) || $fld_protection || - $fld_talkid || $fld_subjectid || $fld_url || - $fld_readable)) - { - foreach($missing as $pageid => $title) { - $count++; - if(!is_null($params['continue'])) - if($count < $params['continue']) - continue; - $r = array(); - if(!is_null($params['token'])) - { - $tokenFunctions = $this->getTokenFunctions(); - $res['query']['pages'][$pageid]['starttimestamp'] = wfTimestamp(TS_ISO_8601, time()); - foreach($params['token'] as $t) - { - $val = call_user_func($tokenFunctions[$t], $pageid, $title); - if($val === false) - $this->setWarning("Action '$t' is not allowed for the current user"); - else - $r[$t . 'token'] = $val; - } - } - if($fld_protection) - { - // Apparently the XML formatting code doesn't like array(null) - // This is painful to fix, so we'll just work around it - if(isset($prottitles[$title->getNamespace()][$title->getDBkey()])) - $val = $prottitles[$title->getNamespace()][$title->getDBkey()]; - else - $val = array(); - $result->setIndexedTagName($val, 'pr'); - $r['protection'] = $val; - } - if($fld_talkid && isset($talkids[$title->getNamespace()][$title->getDbKey()])) - $r['talkid'] = $talkids[$title->getNamespace()][$title->getDbKey()]; - if($fld_subjectid && isset($subjectids[$title->getNamespace()][$title->getDbKey()])) - $r['subjectid'] = $subjectids[$title->getNamespace()][$title->getDbKey()]; - if($fld_url) - { - $r['fullurl'] = $title->getFullURL(); - $r['editurl'] = $title->getFullURL('action=edit'); - } - if($fld_readable) - if($title->userCanRead()) - $r['readable'] = ''; - $fit = $result->addValue(array('query', 'pages'), $pageid, $r); - if(!$fit) - { - $this->setContinueEnumParameter('continue', $count); - break; - } - } - } - } - - public function getAllowedParams() { - return array ( - 'prop' => array ( - ApiBase :: PARAM_DFLT => NULL, - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array ( - 'protection', - 'talkid', - 'subjectid', - 'url', - 'readable', - )), - 'token' => array ( - ApiBase :: PARAM_DFLT => NULL, - ApiBase :: PARAM_ISMULTI => true, - ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()) - ), - 'continue' => null, - ); - } - - public function getParamDescription() { - return array ( - 'prop' => array ( - 'Which additional properties to get:', - ' protection - List the protection level of each page', - ' talkid - The page ID of the talk page for each non-talk page', - ' subjectid - The page ID of the parent page for each talk page' - ), - 'token' => 'Request a token to perform a data-modifying action on a page', - 'continue' => 'When more results are available, use this to continue', - ); - } - - - public function getDescription() { - return 'Get basic page information such as namespace, title, last touched date, ...'; - } - - protected function getExamples() { - return array ( - 'api.php?action=query&prop=info&titles=Main%20Page', - 'api.php?action=query&prop=info&inprop=protection&titles=Main%20Page' - ); - } - - public function getVersion() { - return __CLASS__ . ': $Id$'; - } -} +@gmail.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * http://www.gnu.org/copyleft/gpl.html + */ + +if (!defined('MEDIAWIKI')) { + // Eclipse helper - will be ignored in production + require_once ('ApiQueryBase.php'); +} + +/** + * A query module to show basic page information. + * + * @ingroup API + */ +class ApiQueryInfo extends ApiQueryBase { + + private $fld_protection = false, $fld_talkid = false, + $fld_subjectid = false, $fld_url = false, + $fld_readable = false; + + public function __construct($query, $moduleName) { + parent :: __construct($query, $moduleName, 'in'); + } + + public function requestExtraData($pageSet) { + $pageSet->requestField('page_restrictions'); + $pageSet->requestField('page_is_redirect'); + $pageSet->requestField('page_is_new'); + $pageSet->requestField('page_counter'); + $pageSet->requestField('page_touched'); + $pageSet->requestField('page_latest'); + $pageSet->requestField('page_len'); + } + + /** + * Get an array mapping token names to their handler functions. + * The prototype for a token function is func($pageid, $title) + * it should return a token or false (permission denied) + * @return array(tokenname => function) + */ + protected function getTokenFunctions() { + // Don't call the hooks twice + if(isset($this->tokenFunctions)) + return $this->tokenFunctions; + + // If we're in JSON callback mode, no tokens can be obtained + if(!is_null($this->getMain()->getRequest()->getVal('callback'))) + return array(); + + $this->tokenFunctions = array( + 'edit' => array( 'ApiQueryInfo', 'getEditToken' ), + 'delete' => array( 'ApiQueryInfo', 'getDeleteToken' ), + 'protect' => array( 'ApiQueryInfo', 'getProtectToken' ), + 'move' => array( 'ApiQueryInfo', 'getMoveToken' ), + 'block' => array( 'ApiQueryInfo', 'getBlockToken' ), + 'unblock' => array( 'ApiQueryInfo', 'getUnblockToken' ), + 'email' => array( 'ApiQueryInfo', 'getEmailToken' ), + 'import' => array( 'ApiQueryInfo', 'getImportToken' ), + ); + wfRunHooks('APIQueryInfoTokens', array(&$this->tokenFunctions)); + return $this->tokenFunctions; + } + + public static function getEditToken($pageid, $title) + { + // We could check for $title->userCan('edit') here, + // but that's too expensive for this purpose + // and would break caching + global $wgUser; + if(!$wgUser->isAllowed('edit')) + return false; + + // The edit token is always the same, let's exploit that + static $cachedEditToken = null; + if(!is_null($cachedEditToken)) + return $cachedEditToken; + + $cachedEditToken = $wgUser->editToken(); + return $cachedEditToken; + } + + public static function getDeleteToken($pageid, $title) + { + global $wgUser; + if(!$wgUser->isAllowed('delete')) + return false; + + static $cachedDeleteToken = null; + if(!is_null($cachedDeleteToken)) + return $cachedDeleteToken; + + $cachedDeleteToken = $wgUser->editToken(); + return $cachedDeleteToken; + } + + public static function getProtectToken($pageid, $title) + { + global $wgUser; + if(!$wgUser->isAllowed('protect')) + return false; + + static $cachedProtectToken = null; + if(!is_null($cachedProtectToken)) + return $cachedProtectToken; + + $cachedProtectToken = $wgUser->editToken(); + return $cachedProtectToken; + } + + public static function getMoveToken($pageid, $title) + { + global $wgUser; + if(!$wgUser->isAllowed('move')) + return false; + + static $cachedMoveToken = null; + if(!is_null($cachedMoveToken)) + return $cachedMoveToken; + + $cachedMoveToken = $wgUser->editToken(); + return $cachedMoveToken; + } + + public static function getBlockToken($pageid, $title) + { + global $wgUser; + if(!$wgUser->isAllowed('block')) + return false; + + static $cachedBlockToken = null; + if(!is_null($cachedBlockToken)) + return $cachedBlockToken; + + $cachedBlockToken = $wgUser->editToken(); + return $cachedBlockToken; + } + + public static function getUnblockToken($pageid, $title) + { + // Currently, this is exactly the same as the block token + return self::getBlockToken($pageid, $title); + } + + public static function getEmailToken($pageid, $title) + { + global $wgUser; + if(!$wgUser->canSendEmail() || $wgUser->isBlockedFromEmailUser()) + return false; + + static $cachedEmailToken = null; + if(!is_null($cachedEmailToken)) + return $cachedEmailToken; + + $cachedEmailToken = $wgUser->editToken(); + return $cachedEmailToken; + } + + public static function getImportToken($pageid, $title) + { + global $wgUser; + if(!$wgUser->isAllowed('import')) + return false; + + static $cachedImportToken = null; + if(!is_null($cachedImportToken)) + return $cachedImportToken; + + $cachedImportToken = $wgUser->editToken(); + return $cachedImportToken; + } + + public function execute() { + $this->params = $this->extractRequestParams(); + if(!is_null($this->params['prop'])) { + $prop = array_flip($this->params['prop']); + $this->fld_protection = isset($prop['protection']); + $this->fld_talkid = isset($prop['talkid']); + $this->fld_subjectid = isset($prop['subjectid']); + $this->fld_url = isset($prop['url']); + $this->fld_readable = isset($prop['readable']); + } + + $pageSet = $this->getPageSet(); + $this->titles = $pageSet->getGoodTitles(); + $this->missing = $pageSet->getMissingTitles(); + $this->everything = array_merge($this->titles, $this->missing); + $result = $this->getResult(); + + $this->pageRestrictions = $pageSet->getCustomField('page_restrictions'); + $this->pageIsRedir = $pageSet->getCustomField('page_is_redirect'); + $this->pageIsNew = $pageSet->getCustomField('page_is_new'); + $this->pageCounter = $pageSet->getCustomField('page_counter'); + $this->pageTouched = $pageSet->getCustomField('page_touched'); + $this->pageLatest = $pageSet->getCustomField('page_latest'); + $this->pageLength = $pageSet->getCustomField('page_len'); + + $db = $this->getDB(); + // Get protection info if requested + if ($this->fld_protection) + $this->getProtectionInfo(); + + // Run the talkid/subjectid query if requested + if($this->fld_talkid || $this->fld_subjectid) + $this->getTSIDs(); + + $count = 0; + ksort($this->everything); // Ensure they're always in the same order + foreach($this->everything as $pageid => $title) { + $count++; + if(!is_null($params['continue'])) + if($count < $params['continue']) + continue; + $pageInfo = $this->extractPageInfo($pageid, $title); + $fit = $result->addValue(array ( + 'query', + 'pages' + ), $pageid, $pageInfo); + if(!$fit) + { + $this->setContinueEnumParameter('continue', $count); + break; + } + } + } + + /** + * Get a result array with information about a title + * @param $pageid int Page ID (negative for missing titles) + * @param $title Title object + * @return array + */ + private function extractPageInfo($pageid, $title) + { + $pageInfo = array(); + if($title->exists()) + { + $pageInfo['touched'] = wfTimestamp(TS_ISO_8601, $this->pageTouched[$pageid]); + $pageInfo['lastrevid'] = intval($this->pageLatest[$pageid]); + $pageInfo['counter'] = intval($this->pageCounter[$pageid]); + $pageInfo['length'] = intval($this->pageLength[$pageid]); + if ($this->pageIsRedir[$pageid]) + $pageInfo['redirect'] = ''; + if ($this->pageIsNew[$pageid]) + $pageInfo['new'] = ''; + } + + if (!is_null($this->params['token'])) { + $tokenFunctions = $this->getTokenFunctions(); + $pageInfo['starttimestamp'] = wfTimestamp(TS_ISO_8601, time()); + foreach($this->params['token'] as $t) + { + $val = call_user_func($tokenFunctions[$t], $pageid, $title); + if($val === false) + $this->setWarning("Action '$t' is not allowed for the current user"); + else + $pageInfo[$t . 'token'] = $val; + } + } + + if($this->fld_protection) { + $pageInfo['protection'] = array(); + if (isset($this->protections[$title->getNamespace()][$title->getDBkey()])) + $pageInfo['protection'] = + $this->protections[$title->getNamespace()][$title->getDBkey()]; + $result->setIndexedTagName($pageInfo['protection'], 'pr'); + } + if($this->fld_talkid && isset($this->talkids[$title->getNamespace()][$title->getDBKey()])) + $pageInfo['talkid'] = $this->talkids[$title->getNamespace()][$title->getDBKey()]; + if($this->fld_subjectid && isset($this->subjectids[$title->getNamespace()][$title->getDBKey()])) + $pageInfo['subjectid'] = $this->subjectids[$title->getNamespace()][$title->getDBKey()]; + if($this->fld_url) { + $pageInfo['fullurl'] = $title->getFullURL(); + $pageInfo['editurl'] = $title->getFullURL('action=edit'); + } + if($this->fld_readable) + if($title->userCanRead()) + $pageInfo['readable'] = ''; + return $pageInfo; + } + + /** + * Get information about protections and put it in $protections + */ + private function getProtectionInfo() + { + $this->protections = array(); + $db = $this->getDB(); + + // Get normal protections for existing titles + $this->addTables('page_restrictions', 'page'); + $this->addWhere('page_id=pr_page'); + $this->addFields(array('pr_page', 'pr_type', 'pr_level', + 'pr_expiry', 'pr_cascade', 'page_namespace', + 'page_title')); + $this->addWhereFld('pr_page', array_keys($this->titles)); + + $res = $this->select(__METHOD__); + while($row = $db->fetchObject($res)) { + $a = array( + 'type' => $row->pr_type, + 'level' => $row->pr_level, + 'expiry' => Block::decodeExpiry($row->pr_expiry, TS_ISO_8601) + ); + if($row->pr_cascade) + $a['cascade'] = ''; + $this->protections[$row->page_namespace][$row->page_title][] = $a; + + # Also check old restrictions + if($pageRestrictions[$row->pr_page]) { + foreach(explode(':', trim($this->pageRestrictions[$row->pr_page])) as $restrict) { + $temp = explode('=', trim($restrict)); + if(count($temp) == 1) { + // old old format should be treated as edit/move restriction + $restriction = trim( $temp[0] ); + + if($restriction == '') + continue; + $this->protections[$row->page_namespace][$row->page_title][] = array( + 'type' => 'edit', + 'level' => $restriction, + 'expiry' => 'infinity', + ); + $this->protections[$row->page_namespace][$row->page_title][] = array( + 'type' => 'move', + 'level' => $restriction, + 'expiry' => 'infinity', + ); + } else { + $restriction = trim($temp[1]); + if($restriction == '') + continue; + $this->protections[$row->page_namespace][$row->page_title][] = array( + 'type' => $temp[0], + 'level' => $restriction, + 'expiry' => 'infinity', + ); + } + } + } + } + $db->freeResult($res); + + // Get protections for missing titles + $this->resetQueryParams(); + $lb = new LinkBatch($this->missing); + $this->addTables('protected_titles'); + $this->addFields(array('pt_title', 'pt_namespace', 'pt_create_perm', 'pt_expiry')); + $this->addWhere($lb->constructSet('pt', $db)); + $res = $this->select(__METHOD__); + while($row = $db->fetchObject($res)) { + $this->protections[$row->pt_namespace][$row->pt_title][] = array( + 'type' => 'create', + 'level' => $row->pt_create_perm, + 'expiry' => Block::decodeExpiry($row->pt_expiry, TS_ISO_8601) + ); + } + $db->freeResult($res); + + // Cascading protections + $images = $others = array(); + foreach ($this->everything as $title) + if ($title->getNamespace() == NS_FILE) + $images[] = $title->getDBKey(); + else + $others[] = $title; + + if (count($others)) { + // Non-images: check templatelinks + $lb = new LinkBatch($others); + $this->resetQueryParams(); + $this->addTables(array('page_restrictions', 'page', 'templatelinks')); + $this->addFields(array('pr_type', 'pr_level', 'pr_expiry', + 'page_title', 'page_namespace', + 'tl_title', 'tl_namespace')); + $this->addWhere($lb->constructSet('tl', $db)); + $this->addWhere('pr_page = page_id'); + $this->addWhere('pr_page = tl_from'); + $this->addWhereFld('pr_cascade', 1); + + $res = $this->select(__METHOD__); + while($row = $db->fetchObject($res)) { + $source = Title::makeTitle($row->page_namespace, $row->page_title); + $this->protections[$row->pt_namespace][$row->pt_title][] = array( + 'type' => $row->pr_type, + 'level' => $row->pr_level, + 'expiry' => Block::decodeExpiry($row->pr_expiry, TS_ISO_8601), + 'source' => $source->getPrefixedText() + ); + } + $db->freeResult($res); + } + + if (count($images)) { + // Images: check imagelinks + $this->resetQueryParams(); + $this->addTables(array('page_restrictions', 'page', 'imagelinks')); + $this->addFields(array('pr_type', 'pr_level', 'pr_expiry', + 'page_title', 'page_namespace', 'il_to')); + $this->addWhere('pr_page = page_id'); + $this->addWhere('pr_page = il_from'); + $this->addWhereFld('pr_cascade', 1); + $this->addWhereFld('il_to', $images); + + $res = $this->select(__METHOD__); + while($row = $db->fetchObject($res)) { + $source = Title::makeTitle($row->page_namespace, $row->page_title); + $this->protections[NS_FILE][$row->il_to][] = array( + 'type' => $row->pr_type, + 'level' => $row->pr_level, + 'expiry' => Block::decodeExpiry($row->pr_expiry, TS_ISO_8601), + 'source' => $source->getPrefixedText() + ); + } + $db->freeResult($res); + } + } + + /** + * Get talk page IDs (if requested) and subject page IDs (if requested) + * and put them in $talkids and $subjectids + */ + private function getTSIDs() + { + $getTitles = $this->talkids = $this->subjectids = array(); + $db = $this->getDB(); + foreach($this->everything as $t) + { + if(MWNamespace::isTalk($t->getNamespace())) + { + if($this->fld_subjectid) + $getTitles[] = $t->getSubjectPage(); + } + else if($this->fld_talkid) + $getTitles[] = $t->getTalkPage(); + } + if(!count($getTitles)) + return; + + // Construct a custom WHERE clause that matches + // all titles in $getTitles + $lb = new LinkBatch($getTitles); + $this->resetQueryParams(); + $this->addTables('page'); + $this->addFields(array('page_title', 'page_namespace', 'page_id')); + $this->addWhere($lb->constructSet('page', $db)); + $res = $this->select(__METHOD__); + while($row = $db->fetchObject($res)) + { + if(MWNamespace::isTalk($row->page_namespace)) + $this->talkids[MWNamespace::getSubject($row->page_namespace)][$row->page_title] = + $row->page_id; + else + $this->subjectids[MWNamespace::getTalk($row->page_namespace)][$row->page_title] = + $row->page_id; + } + } + + public function getAllowedParams() { + return array ( + 'prop' => array ( + ApiBase :: PARAM_DFLT => NULL, + ApiBase :: PARAM_ISMULTI => true, + ApiBase :: PARAM_TYPE => array ( + 'protection', + 'talkid', + 'subjectid', + 'url', + 'readable', + )), + 'token' => array ( + ApiBase :: PARAM_DFLT => NULL, + ApiBase :: PARAM_ISMULTI => true, + ApiBase :: PARAM_TYPE => array_keys($this->getTokenFunctions()) + ), + 'continue' => null, + ); + } + + public function getParamDescription() { + return array ( + 'prop' => array ( + 'Which additional properties to get:', + ' protection - List the protection level of each page', + ' talkid - The page ID of the talk page for each non-talk page', + ' subjectid - The page ID of the parent page for each talk page' + ), + 'token' => 'Request a token to perform a data-modifying action on a page', + 'continue' => 'When more results are available, use this to continue', + ); + } + + public function getDescription() { + return 'Get basic page information such as namespace, title, last touched date, ...'; + } + + protected function getExamples() { + return array ( + 'api.php?action=query&prop=info&titles=Main%20Page', + 'api.php?action=query&prop=info&inprop=protection&titles=Main%20Page' + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id$'; + } +} -- 2.11.4.GIT