From 10390af521a87e547c73b65e80ba633b96782470 Mon Sep 17 00:00:00 2001 From: Yuri Astrakhan Date: Fri, 20 Oct 2006 07:10:18 +0000 Subject: [PATCH] API * Added rudimentary RC list * More readable query formulation --- includes/AutoLoader.php | 1 + includes/api/ApiOpenSearch.php | 2 +- includes/api/ApiQuery.php | 3 +- includes/api/ApiQueryAllpages.php | 45 +++---- includes/api/ApiQueryBase.php | 77 ++++++++++- includes/api/ApiQueryInfo.php | 5 +- includes/api/ApiQueryLogEvents.php | 43 ++---- includes/api/ApiQueryRecentchanges.php | 180 +++++++++++++++++++++++++ includes/api/ApiQueryWatchlist.php | 86 +++++------- 9 files changed, 324 insertions(+), 118 deletions(-) create mode 100644 includes/api/ApiQueryRecentchanges.php diff --git a/includes/AutoLoader.php b/includes/AutoLoader.php index a5a58ded62..388512861f 100644 --- a/includes/AutoLoader.php +++ b/includes/AutoLoader.php @@ -255,6 +255,7 @@ function __autoload($className) { 'ApiQueryBase' => 'includes/api/ApiQueryBase.php', 'ApiQueryInfo' => 'includes/api/ApiQueryInfo.php', 'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php', + 'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php', 'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php', 'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php', 'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php', diff --git a/includes/api/ApiOpenSearch.php b/includes/api/ApiOpenSearch.php index 8b24af91f9..9e5afcb8be 100644 --- a/includes/api/ApiOpenSearch.php +++ b/includes/api/ApiOpenSearch.php @@ -60,7 +60,7 @@ class ApiOpenSearch extends ApiBase { $module = new ApiMain($params); $module->execute(); - // Get clean data + // Get resulting data $data = & $module->getResultData(); // Reformat useful data for future printing by JSON engine diff --git a/includes/api/ApiQuery.php b/includes/api/ApiQuery.php index 93d60aeef1..9addeb1079 100644 --- a/includes/api/ApiQuery.php +++ b/includes/api/ApiQuery.php @@ -48,7 +48,8 @@ class ApiQuery extends ApiBase { private $mQueryListModules = array ( 'allpages' => 'ApiQueryAllpages', 'logevents' => 'ApiQueryLogEvents', - 'watchlist' => 'ApiQueryWatchlist' + 'watchlist' => 'ApiQueryWatchlist', + 'recentchanges' => 'ApiQueryRecentChanges' ); // 'backlinks' => 'ApiQueryBacklinks', // 'categorymembers' => 'ApiQueryCategorymembers', diff --git a/includes/api/ApiQueryAllpages.php b/includes/api/ApiQueryAllpages.php index 117cfa7c78..123264ff76 100644 --- a/includes/api/ApiQueryAllpages.php +++ b/includes/api/ApiQueryAllpages.php @@ -52,47 +52,32 @@ class ApiQueryAllpages extends ApiQueryGeneratorBase { $db = $this->getDB(); - $where = array ( - 'page_namespace' => $namespace - ); - - if (isset ($from)) { - $where[] = 'page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($from)); - } - - if (isset ($prefix)) { - $where[] = "page_title LIKE '{$db->strencode(ApiQueryBase :: titleToKey($prefix))}%'"; - } - - if ($filterredir === 'redirects') { - $where['page_is_redirect'] = 1; - } - elseif ($filterredir === 'nonredirects') { - $where['page_is_redirect'] = 0; - } + $this->addTables('page'); + $this->addWhereIf('page_is_redirect = 1', $filterredir === 'redirects'); + $this->addWhereIf('page_is_redirect = 0', $filterredir === 'nonredirects'); + $this->addWhereFld('page_namespace', $namespace); + if (isset ($from)) + $this->addWhere('page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($from))); + if (isset ($prefix)) + $this->addWhere("page_title LIKE '{$db->strencode(ApiQueryBase :: titleToKey($prefix))}%'"); if (is_null($resultPageSet)) { - $fields = array ( + $this->addFields( array ( 'page_id', 'page_namespace', 'page_title' - ); + )); } else { - $fields = $resultPageSet->getPageTableFields(); + $this->addFields( $resultPageSet->getPageTableFields()); } - $options = array ( - 'USE INDEX' => 'name_title', - 'LIMIT' => $limit +1, - 'ORDER BY' => 'page_namespace, page_title' - ); - - $this->profileDBIn(); - $res = $db->select('page', $fields, $where, __METHOD__, $options); - $this->profileDBOut(); + $this->addOption( 'USE INDEX', 'name_title'); + $this->addOption( 'LIMIT', $limit +1); + $this->addOption( 'ORDER BY', 'page_namespace, page_title'); $data = array (); $count = 0; + $res = $this->select(__METHOD__); while ($row = $db->fetchObject($res)) { if (++ $count > $limit) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... diff --git a/includes/api/ApiQueryBase.php b/includes/api/ApiQueryBase.php index 4963927358..297f8e11fd 100644 --- a/includes/api/ApiQueryBase.php +++ b/includes/api/ApiQueryBase.php @@ -31,13 +31,88 @@ if (!defined('MEDIAWIKI')) { abstract class ApiQueryBase extends ApiBase { - private $mQueryModule; + private $mQueryModule, $tables, $where, $fields, $options; public function __construct($query, $moduleName, $paramPrefix = '') { parent :: __construct($query->getMain(), $moduleName, $paramPrefix); $this->mQueryModule = $query; + + $this->tables = array (); + $this->where = array (); + $this->fields = array(); + $this->options = array (); + } + + protected function addTables($value) { + if(!is_array($this->tables)) + $this->dieDebug(__METHOD__, 'Must not call setTablesAsExpression() before this method'); + if(is_array($value)) + $this->tables = array_merge($this->tables, $value); + else + $this->tables[] = $value; + } + + protected function setTablesAsExpression($value) { + if(!empty($this->tables)) + $this->dieDebug(__METHOD__, 'Must not call addTables() before this method'); + $this->tables = $value; } + protected function addFields($value) { + if(is_array($value)) + $this->fields = array_merge($this->fields, $value); + else + $this->fields[] = $value; + } + + protected function addFieldsIf($value, $condition) { + if ($condition) + $this->addFields($value); + } + + protected function addWhere($value) { + if(is_array($value)) + $this->where = array_merge($this->where, $value); + else + $this->where[] = $value; + } + + protected function addWhereIf($value, $condition) { + if ($condition) + $this->addWhere($value); + } + + protected function addWhereFld($field, $value) { + if(!is_null($value)) + $this->where[$field] = $value; + } + + protected function addWhereRange($field, $dir, $start, $end) { + $isDirNewer = ($dir === 'newer'); + $after = ($isDirNewer ? '<=' : '>='); + $before = ($isDirNewer ? '>=' : '<='); + $db = $this->getDB(); + + if (!is_null($start)) + $this->addWhere($field . $after . $db->addQuotes($start)); + + if (!is_null($end)) + $this->addWhere($field . $before . $db->addQuotes($end)); + + $this->addOption('ORDER BY', $field . ($isDirNewer ? '' : ' DESC')); + } + + protected function select($method) { + $this->profileDBIn(); + $res = $this->getDB()->select($this->tables, $this->fields, $this->where, $method, $this->options); + $this->profileDBOut(); + return $res; + } + + protected function addOption($name, $value) { + $this->options[$name] = $value; + } + /** * Override this method to request extra fields from the pageSet * using $this->getPageSet()->requestField('fieldName') diff --git a/includes/api/ApiQueryInfo.php b/includes/api/ApiQueryInfo.php index cf3f30e459..9b4918808a 100644 --- a/includes/api/ApiQueryInfo.php +++ b/includes/api/ApiQueryInfo.php @@ -53,7 +53,10 @@ class ApiQueryInfo extends ApiQueryBase { $pageLatest = $pageSet->getCustomField('page_latest'); foreach ($titles as $pageid => $title) { - $pageInfo = array ('touched' => $pageTouched[$pageid], 'lastrevid' => $pageLatest[$pageid]); + $pageInfo = array ( + 'touched' => $pageTouched[$pageid], + 'lastrevid' => $pageLatest[$pageid] + ); if ($pageIsRedir[$pageid]) $pageInfo['redirect'] = ''; diff --git a/includes/api/ApiQueryLogEvents.php b/includes/api/ApiQueryLogEvents.php index a6f88c390d..decde3e969 100644 --- a/includes/api/ApiQueryLogEvents.php +++ b/includes/api/ApiQueryLogEvents.php @@ -42,11 +42,11 @@ class ApiQueryLogEvents extends ApiQueryBase { $db = $this->getDB(); extract($db->tableNames('logging', 'page', 'user'), EXTR_PREFIX_ALL, 'tbl'); - $tables = "$tbl_logging LEFT OUTER JOIN $tbl_page ON " . + $this->setTablesAsExpression("$tbl_logging LEFT OUTER JOIN $tbl_page ON " . "log_namespace=page_namespace AND log_title=page_title " . - "INNER JOIN $tbl_user ON user_id=log_user"; + "INNER JOIN $tbl_user ON user_id=log_user"); - $fields = array ( + $this->addFields(array ( 'log_type', 'log_action', 'log_timestamp', @@ -57,11 +57,11 @@ class ApiQueryLogEvents extends ApiQueryBase { 'page_id', 'log_comment', 'log_params' - ); + )); - $where = array (); - if (!is_null($type)) - $where['log_type'] = $type; + $this->addWhereFld('log_type', $type); + $this->addWhereRange('log_timestamp', $dir, $start, $end); + $this->addOption('LIMIT', $limit +1); if (!is_null($user)) { $userid = $db->selectField('user', 'user_id', array ( @@ -69,37 +69,20 @@ class ApiQueryLogEvents extends ApiQueryBase { )); if (!$userid) $this->dieUsage("User name $user not found", 'param_user'); - $where['log_user'] = $userid; + $this->addWhereFld('log_user', $userid); } if (!is_null($title)) { $titleObj = Title :: newFromText($title); if (is_null($titleObj)) $this->dieUsage("Bad title value '$title'", 'param_title'); - $where['log_namespace'] = $titleObj->getNamespace(); - $where['log_title'] = $titleObj->getDBkey(); + $this->addWhereFld('log_namespace', $titleObj->getNamespace()); + $this->addWhereFld('log_title', $titleObj->getDBkey()); } - $dirNewer = ($dir === 'newer'); - $before = ($dirNewer ? '<=' : '>='); - $after = ($dirNewer ? '>=' : '<='); - - if (!is_null($start)) - $where[] = 'log_timestamp' . $after . $db->addQuotes($start); - if (!is_null($end)) - $where[] = 'log_timestamp' . $before . $db->addQuotes($end); - - $options = array ( - 'LIMIT' => $limit +1, - 'ORDER BY' => 'log_timestamp' . ($dirNewer ? '' : ' DESC' - )); - - $this->profileDBIn(); - $res = $db->select($tables, $fields, $where, __METHOD__, $options); - $this->profileDBOut(); - $data = array (); $count = 0; + $res = $this->select(__METHOD__); while ($row = $db->fetchObject($res)) { if (++ $count > $limit) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... @@ -128,8 +111,8 @@ class ApiQueryLogEvents extends ApiQueryBase { $params = null; } } - - if(!empty($params)) { + + if (!empty ($params)) { $this->getResult()->setIndexedTagName($params, 'param'); $vals = array_merge($vals, $params); } diff --git a/includes/api/ApiQueryRecentchanges.php b/includes/api/ApiQueryRecentchanges.php new file mode 100644 index 0000000000..34f1c46326 --- /dev/null +++ b/includes/api/ApiQueryRecentchanges.php @@ -0,0 +1,180 @@ + + * + * 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'); +} + +class ApiQueryRecentChanges extends ApiQueryBase { + + public function __construct($query, $moduleName) { + parent :: __construct($query, $moduleName, 'rc'); + } + + public function execute() { + $limit = $from = $namespace = $hide = $dir = $start = $end = null; + extract($this->extractRequestParams()); + + $this->addTables('recentchanges'); + $this->addWhereRange('rc_timestamp', $dir, $start, $end); + $this->addWhereFld('rc_namespace', $namespace); + + if (!is_null($hide)) { + $hide = array_flip($hide); + $this->addWhereIf('rc_minor = 0', isset ($hide['minor'])); + $this->addWhereIf('rc_bot = 0', isset ($hide['bots'])); + $this->addWhereIf('rc_user != 0', isset ($hide['anons'])); + $this->addWhereIf('rc_user = 0', isset ($hide['liu'])); + } + + $this->addFields(array ( + 'rc_timestamp', + 'rc_user', + 'rc_user_text', + 'rc_namespace', + 'rc_title', + 'rc_comment', + 'rc_minor', + 'rc_bot', + 'rc_new', + 'rc_cur_id', + 'rc_this_oldid', + 'rc_last_oldid', + 'rc_type', + 'rc_moved_to_ns', + 'rc_moved_to_title' + )); + + $this->addOption('LIMIT', $limit +1); + + $data = array (); + $count = 0; + $db = $this->getDB(); + $res = $this->select(__METHOD__); + while ($row = $db->fetchObject($res)) { + if (++ $count > $limit) { + // We've reached the one extra which shows that there are additional pages to be had. Stop here... + $this->setContinueEnumParameter('start', $row->rc_timestamp); + break; + } + + $title = Title :: makeTitle($row->rc_namespace, $row->rc_title); + // skip any pages that user has no rights to read + if ($title->userCanRead()) { + + $id = intval($row->rc_cur_id); + $data[] = array ( + 'id' => $id, + 'ns' => $title->getNamespace(), 'title' => $title->getPrefixedText(), + 'timestamp' => $row->rc_timestamp, + 'user' => $row->rc_user_text, + 'comment' => $row->rc_comment, + 'this_oldid' => $row->rc_this_oldid, + 'last_oldid' => $row->rc_last_oldid, + 'type' => $row->rc_type, + 'moved_to_ns' => $row->rc_moved_to_ns, + 'moved_to_title' => $row->rc_moved_to_title); + + if (!$row->rc_user) + $vals['anon'] = ''; + if ($row->rc_new) + $vals['new'] = ''; + if ($row->rc_bot) + $vals['bot'] = ''; + if ($row->rc_minor) + $vals['minor'] = ''; + } + } + $db->freeResult($res); + + $result = $this->getResult(); + $result->setIndexedTagName($data, 'rc'); + $result->addValue('query', $this->getModuleName(), $data); + } + + protected function getAllowedParams() { + $namespaces = $this->getQuery()->getValidNamespaces(); + return array ( + 'dir' => array ( + ApiBase :: PARAM_DFLT => 'older', + ApiBase :: PARAM_TYPE => array ( + 'newer', + 'older' + ) + ), + 'start' => array ( + ApiBase :: PARAM_TYPE => 'timestamp' + ), + 'end' => array ( + ApiBase :: PARAM_TYPE => 'timestamp' + ), + 'namespace' => array ( + ApiBase :: PARAM_DFLT => 0, + ApiBase :: PARAM_TYPE => $namespaces + ), + 'hide' => array ( + ApiBase :: PARAM_ISMULTI => true, + ApiBase :: PARAM_TYPE => array ( + 'minor', + 'bots', + 'anons', + 'liu' + ) + ), + 'limit' => array ( + ApiBase :: PARAM_DFLT => 10, + ApiBase :: PARAM_TYPE => 'limit', + ApiBase :: PARAM_MIN => 1, + ApiBase :: PARAM_MAX1 => ApiBase :: LIMIT_BIG1, + ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2 + ) + ); + } + + protected function getParamDescription() { + return array ( + 'start' => 'The timestamp to start enumerating from.', + 'end' => 'The timestamp to end enumerating.', + 'limit' => 'How many total pages to return.' + ); + } + + protected function getDescription() { + return 'Enumerate recent changes'; + } + + protected function getExamples() { + return array ( + 'api.php?action=query&list=recentchanges', + ); + } + + public function getVersion() { + return __CLASS__ . ': $Id$'; + } +} +?> \ No newline at end of file diff --git a/includes/api/ApiQueryWatchlist.php b/includes/api/ApiQueryWatchlist.php index 65335db494..9b42698da2 100644 --- a/includes/api/ApiQueryWatchlist.php +++ b/includes/api/ApiQueryWatchlist.php @@ -52,23 +52,6 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { $allrev = $start = $end = $namespace = $dir = $limit = $prop = null; extract($this->extractRequestParams()); - $db = $this->getDB(); - - $dirNewer = ($dir === 'newer'); - $after = ($dirNewer ? '<=' : '>='); - $before = ($dirNewer ? '>=' : '<='); - - $tables = array ( - 'watchlist', - 'page', - 'recentchanges' - ); - - $options = array ( - 'LIMIT' => $limit +1, - 'ORDER BY' => 'rc_timestamp' . ($dirNewer ? '' : ' DESC' - )); - $patrol = $timestamp = $user = $comment = false; if (!is_null($prop)) { if (!is_null($resultPageSet)) @@ -87,67 +70,62 @@ class ApiQueryWatchlist extends ApiQueryGeneratorBase { } if (is_null($resultPageSet)) { - $fields = array ( + $this->addFields(array ( 'rc_cur_id AS page_id', 'rc_this_oldid AS rev_id', 'rc_namespace AS page_namespace', 'rc_title AS page_title', 'rc_new AS page_is_new', - 'rc_minor AS rev_minor_edit' - ); - if ($user) { - $fields[] = 'rc_user AS rev_user'; - $fields[] = 'rc_user_text AS rev_user_text'; - } - if ($comment) - $fields[] = 'rc_comment AS rev_comment'; - if ($timestamp) - $fields[] = 'rc_timestamp AS rev_timestamp'; - if ($patrol) - $fields[] = 'rc_patrolled'; + 'rc_minor AS rev_minor_edit', + 'rc_timestamp AS rev_timestamp' + )); + + $this->addFieldsIf('rc_user AS rev_user', $user); + $this->addFieldsIf('rc_user_text AS rev_user_text', $user); + $this->addFieldsIf('rc_comment AS rev_comment', $comment); + $this->addFieldsIf('rc_patrolled', $patrol); } elseif ($allrev) { - $fields = array ( + $this->addFields(array ( 'rc_this_oldid AS rev_id', 'rc_namespace AS page_namespace', 'rc_title AS page_title', 'rc_timestamp AS rev_timestamp' - ); + )); } else { - $fields = array ( + $this->addFields(array ( 'rc_cur_id AS page_id', 'rc_namespace AS page_namespace', 'rc_title AS page_title', 'rc_timestamp AS rev_timestamp' - ); + )); } - $where = array ( + $this->addTables(array ( + 'watchlist', + 'page', + 'recentchanges' + )); + + $userId = $wgUser->getID(); + $this->addWhere(array ( 'wl_namespace = rc_namespace', 'wl_title = rc_title', 'rc_cur_id = page_id', - 'wl_user' => $wgUser->getID()); - - if (!$allrev) - $where[] = 'rc_this_oldid=page_latest'; - if (isset ($namespace)) - $where['wl_namespace'] = $namespace; - - if (isset ($start)) - $where[] = 'rc_timestamp' . $after . $db->addQuotes($start); - - if (isset ($end)) - $where[] = 'rc_timestamp' . $before . $db->addQuotes($end); - - if (!isset ($start) && !isset ($end)) - $where[] = "rc_timestamp > ''"; - - $this->profileDBIn(); - $res = $db->select($tables, $fields, $where, __METHOD__, $options); - $this->profileDBOut(); + 'wl_user' => $userId + )); + $this->addWhereRange('rc_timestamp', $dir, $start, $end); + $this->addWhereFld('wl_namespace', $namespace); + $this->addWhereIf('rc_this_oldid=page_latest', !$allrev); + $this->addWhereIf("rc_timestamp > ''", !isset ($start) && !isset ($end)); + + $this->addOption('LIMIT', $limit +1); $data = array (); $count = 0; + $res = $this->select(__METHOD__); + + $db = $this->getDB(); while ($row = $db->fetchObject($res)) { if (++ $count > $limit) { // We've reached the one extra which shows that there are additional pages to be had. Stop here... -- 2.20.1