* Added external url search within wiki pages (list=exturlusage)
* Added link enumeration (list=alllinks)
* Added registered users enumeration (list=allusers)
+* Added full text search in titles and content (list=search)
+* (bug 10684) Expanded list=allusers functionality
+* Possible breaking change: prop=revisions no longer includes pageid for rvprop=ids
== Maintenance script changes since 1.10 ==
*/
$processor = new ApiMain($wgRequest, $wgEnableWriteAPI);
-// Generate the output.
+// Process data & print results
$processor->execute();
// Log what the user did, for book-keeping purposes.
'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php',
'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php',
'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
+ 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
'ApiResult' => 'includes/api/ApiResult.php',
'imageusage' => 'ApiQueryBacklinks',
'logevents' => 'ApiQueryLogEvents',
'recentchanges' => 'ApiQueryRecentChanges',
+ 'search' => 'ApiQuerySearch',
'usercontribs' => 'ApiQueryContributions',
'watchlist' => 'ApiQueryWatchlist',
'exturlusage' => 'ApiQueryExtLinksUsage',
if (!is_null($params['from']))
$this->addWhere('pl_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
if (isset ($params['prefix']))
- $this->addWhere("pl_title LIKE '" . $db->strencode(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("pl_title LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
if (is_null($resultPageSet)) {
$this->addFields(array (
*
* API for MediaWiki 1.8+
*
- * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
+ * Copyright (C) 2007 Yuri Astrakhan <Firstname><Lastname>@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
if (!is_null($prop)) {
$prop = array_flip($prop);
$fld_editcount = isset($prop['editcount']);
+ $fld_groups = isset($prop['groups']);
} else {
- $fld_editcount = false;
+ $fld_editcount = $fld_groups = false;
}
- $this->addTables('user');
+ $limit = $params['limit'];
+ $tables = $db->tableName('user');
if (!is_null($params['from']))
$this->addWhere('user_name>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
+ if (isset($params['prefix']))
+ $this->addWhere("user_name LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
+
+ if (!is_null($params['group'])) {
+ // Filter only users that belong to a given group
+ $tblName = $db->tableName('user_groups');
+ $tables = "$tables INNER JOIN $tblName ug1 ON ug1.ug_user=user_id";
+ $this->addWhereFld('ug1.ug_group', $params['group']);
+ }
+
+ if ($fld_groups) {
+ // Show the groups the given users belong to
+ // request more than needed to avoid not getting all rows that belong to one user
+ $groupCount = count(User::getAllGroups());
+ $sqlLimit = $limit+$groupCount+1;
+
+ $tblName = $db->tableName('user_groups');
+ $tables = "$tables LEFT JOIN $tblName ug2 ON ug2.ug_user=user_id";
+ $this->addFields('ug2.ug_group ug_group2');
+ } else {
+ $sqlLimit = $limit+1;
+ }
+
+ $this->addOption('LIMIT', $sqlLimit);
+ $this->addTables($tables);
+
$this->addFields('user_name');
$this->addFieldsIf('user_editcount', $fld_editcount);
- $limit = $params['limit'];
- $this->addOption('LIMIT', $limit+1);
$this->addOption('ORDER BY', 'user_name');
$res = $this->select(__METHOD__);
$data = array ();
$count = 0;
- 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('from', ApiQueryBase :: keyToTitle($row->user_name));
- break;
+ $lastUserData = false;
+ $lastUser = false;
+ $result = $this->getResult();
+
+ //
+ // This loop keeps track of the last entry.
+ // For each new row, if the new row is for different user then the last, the last entry is added to results.
+ // Otherwise, the group of the new row is appended to the last entry.
+ // The setContinue... is more complex because of this, and takes into account the higher sql limit
+ // to make sure all rows that belong to the same user are received.
+ //
+ while (true) {
+
+ $row = $db->fetchObject($res);
+ $count++;
+
+ if (!$row || $lastUser != $row->user_name) {
+ // Save the last pass's user data
+ if (is_array($lastUserData))
+ $data[] = $lastUserData;
+
+ // No more rows left
+ if (!$row)
+ break;
+
+ if ($count > $limit) {
+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
+ $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->user_name));
+ break;
+ }
+
+ // Record new user's data
+ $lastUser = $row->user_name;
+ $lastUserData = array( 'name' => $lastUser );
+ if ($fld_editcount)
+ $lastUserData['editcount'] = intval($row->user_editcount);
+
}
-
- $vals = array( 'name' => $row->user_name );
- if ($fld_editcount) {
- $vals['editcount'] = intval($row->user_editcount);
+
+ if ($sqlLimit == $count) {
+ // BUG! database contains group name that User::getAllGroups() does not return
+ // TODO: should handle this more gracefully
+ ApiBase :: dieDebug(__METHOD__,
+ 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function');
+ }
+
+ // Add user's group info
+ if ($fld_groups && !is_null($row->ug_group2)) {
+ $lastUserData['groups'][] = $row->ug_group2;
+ $result->setIndexedTagName($lastUserData['groups'], 'g');
}
- $data[] = $vals;
}
+
$db->freeResult($res);
- $result = $this->getResult();
$result->setIndexedTagName($data, 'u');
$result->addValue('query', $this->getModuleName(), $data);
}
protected function getAllowedParams() {
return array (
'from' => null,
+ 'prefix' => null,
+ 'group' => array(
+ ApiBase :: PARAM_TYPE => User::getAllGroups()
+ ),
'prop' => array (
ApiBase :: PARAM_ISMULTI => true,
ApiBase :: PARAM_TYPE => array (
- 'editcount'
+ 'editcount',
+ 'groups',
)
),
'limit' => array (
protected function getParamDescription() {
return array (
'from' => 'The user name to start enumerating from.',
- 'prop' => 'What pieces of information to include',
- 'limit' => 'How many total user names to return.'
+ 'prefix' => 'Search for all page titles that begin with this value.',
+ 'group' => 'Limit users to a given group name',
+ 'prop' => array(
+ 'What pieces of information to include.',
+ '`groups` property uses more server resources and may return fewer results than the limit.'),
+ 'limit' => 'How many total user names to return.',
);
}
if (!is_null($params['from']))
$this->addWhere('page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
if (isset ($params['prefix']))
- $this->addWhere("page_title LIKE '" . $db->strencode(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
+ $this->addWhere("page_title LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
if (is_null($resultPageSet)) {
$this->addFields(array (
$this->options = array ();
}
- protected function addTables($value) {
- if (is_array($value))
- $this->tables = array_merge($this->tables, $value);
- else
- $this->tables[] = $value;
+ protected function addTables($tables, $alias = null) {
+ if (is_array($tables)) {
+ if (!is_null($alias))
+ ApiBase :: dieDebug(__METHOD__, 'Multiple table aliases not supported');
+ $this->tables = array_merge($this->tables, $tables);
+ } else {
+ if (!is_null($alias))
+ $tables = $this->getDB()->tableName($tables) . ' ' . $alias;
+ $this->tables[] = $tables;
+ }
}
protected function addFields($value) {
if ($fld_timestamp)
$vals['timestamp'] = wfTimestamp(TS_ISO_8601, $line->img_timestamp);
- if ($fld_user)
+ if ($fld_user) {
$vals['user'] = $line->img_user_text;
+ if(!$line->img_user)
+ $vals['anon'] = '';
+ }
if ($fld_size) {
$vals['size'] = $line->img_size;
$vals['width'] = $line->img_width;
if ($this->fld_ids) {
$vals['revid'] = intval($row->rev_id);
- $vals['pageid'] = intval($row->rev_page);
// $vals['oldid'] = intval($row->rev_text_id); // todo: should this be exposed?
}
--- /dev/null
+<?php
+
+/*
+ * Created on July 30, 2007
+ *
+ * API for MediaWiki 1.8+
+ *
+ * Copyright (C) 2007 Yuri Astrakhan <Firstname><Lastname>@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');
+}
+
+/**
+ * Query module to perform full text search within wiki titles and content
+ *
+ * @addtogroup API
+ */
+class ApiQuerySearch extends ApiQueryGeneratorBase {
+
+ public function __construct($query, $moduleName) {
+ parent :: __construct($query, $moduleName, 'sr');
+ }
+
+ public function execute() {
+ $this->run();
+ }
+
+ public function executeGenerator($resultPageSet) {
+ $this->run($resultPageSet);
+ }
+
+ private function run($resultPageSet = null) {
+
+ $params = $this->extractRequestParams();
+
+ $limit = $params['limit'];
+ $query = $params['search'];
+ if (is_null($query) || empty($query))
+ $this->dieUsage("empty search string is not allowed", 'param-search');
+
+ $search = SearchEngine::create();
+ $search->setLimitOffset( $limit+1, $params['offset'] );
+ $search->setNamespaces( $params['namespace'] );
+ $search->showRedirects = $params['redirects'];
+
+ if ($params['what'] == 'text')
+ $matches = $search->searchText( $query );
+ else
+ $matches = $search->searchTitle( $query );
+
+ $data = array ();
+ $count = 0;
+ while( $result = $matches->next() ) {
+ if (++ $count > $limit) {
+ // We've reached the one extra which shows that there are additional items to be had. Stop here...
+ $this->setContinueEnumParameter('offset', $params['offset'] + $params['limit']);
+ break;
+ }
+
+ $title = $result->getTitle();
+ if (is_null($resultPageSet)) {
+ $data[] = array(
+ 'ns' => intval($title->getNamespace()),
+ 'title' => $title->getPrefixedText());
+ } else {
+ $data[] = $title;
+ }
+ }
+
+ if (is_null($resultPageSet)) {
+ $result = $this->getResult();
+ $result->setIndexedTagName($data, 'p');
+ $result->addValue('query', $this->getModuleName(), $data);
+ } else {
+ $resultPageSet->populateFromTitles($data);
+ }
+ }
+
+ protected function getAllowedParams() {
+ return array (
+ 'search' => null,
+ 'namespace' => array (
+ ApiBase :: PARAM_DFLT => 0,
+ ApiBase :: PARAM_TYPE => 'namespace',
+ ApiBase :: PARAM_ISMULTI => true,
+ ),
+ 'what' => array (
+ ApiBase :: PARAM_DFLT => 'title',
+ ApiBase :: PARAM_TYPE => array (
+ 'title',
+ 'text',
+ )
+ ),
+ 'redirects' => false,
+ 'offset' => 0,
+ 'limit' => array (
+ ApiBase :: PARAM_DFLT => 10,
+ ApiBase :: PARAM_TYPE => 'limit',
+ ApiBase :: PARAM_MIN => 1,
+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
+ )
+ );
+ }
+
+ protected function getParamDescription() {
+ return array (
+ 'search' => 'Search for all page titles (or content) that has this value.',
+ 'namespace' => 'The namespace(s) to enumerate.',
+ 'what' => 'Search inside the text or titles.',
+ 'redirects' => 'Include redirect pages in the search.',
+ 'offset' => 'Use this value to continue paging (return by query)',
+ 'limit' => 'How many total pages to return.'
+ );
+ }
+
+ protected function getDescription() {
+ return 'Perform a full text search';
+ }
+
+ protected function getExamples() {
+ return array (
+ 'api.php?action=query&list=search&srsearch=meaning',
+ 'api.php?action=query&list=search&srwhat=text&srsearch=meaning',
+ 'api.php?action=query&generator=search&gsrsearch=meaning&prop=info',
+ );
+ }
+
+ public function getVersion() {
+ return __CLASS__ . ': $Id$';
+ }
+}
+