r24453 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r24452‎ | r24453 | r24454 >
Date:08:09, 30 July 2007
Author:yurik
Status:old
Tags:
Comment:
API:
* 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
* Bug fix: proper search escaping for SQL LIKE queries.
Modified paths:
  • /trunk/phase3/RELEASE-NOTES (modified) (history)
  • /trunk/phase3/api.php (modified) (history)
  • /trunk/phase3/includes/AutoLoader.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQuery.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryAllLinks.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryAllUsers.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryAllpages.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryBase.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryImageInfo.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQueryRevisions.php (modified) (history)
  • /trunk/phase3/includes/api/ApiQuerySearch.php (added) (history)

Diff [purge]

Index: trunk/phase3/includes/api/ApiQueryAllLinks.php
@@ -70,7 +70,7 @@
7171 if (!is_null($params['from']))
7272 $this->addWhere('pl_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
7373 if (isset ($params['prefix']))
74 - $this->addWhere("pl_title LIKE '" . $db->strencode(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
 74+ $this->addWhere("pl_title LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
7575
7676 if (is_null($resultPageSet)) {
7777 $this->addFields(array (
Index: trunk/phase3/includes/api/ApiQuery.php
@@ -67,6 +67,7 @@
6868 'imageusage' => 'ApiQueryBacklinks',
6969 'logevents' => 'ApiQueryLogEvents',
7070 'recentchanges' => 'ApiQueryRecentChanges',
 71+ 'search' => 'ApiQuerySearch',
7172 'usercontribs' => 'ApiQueryContributions',
7273 'watchlist' => 'ApiQueryWatchlist',
7374 'exturlusage' => 'ApiQueryExtLinksUsage',
Index: trunk/phase3/includes/api/ApiQueryAllpages.php
@@ -63,7 +63,7 @@
6464 if (!is_null($params['from']))
6565 $this->addWhere('page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
6666 if (isset ($params['prefix']))
67 - $this->addWhere("page_title LIKE '" . $db->strencode(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
 67+ $this->addWhere("page_title LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
6868
6969 if (is_null($resultPageSet)) {
7070 $this->addFields(array (
Index: trunk/phase3/includes/api/ApiQueryImageInfo.php
@@ -71,8 +71,11 @@
7272
7373 if ($fld_timestamp)
7474 $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $line->img_timestamp);
75 - if ($fld_user)
 75+ if ($fld_user) {
7676 $vals['user'] = $line->img_user_text;
 77+ if(!$line->img_user)
 78+ $vals['anon'] = '';
 79+ }
7780 if ($fld_size) {
7881 $vals['size'] = $line->img_size;
7982 $vals['width'] = $line->img_width;
Index: trunk/phase3/includes/api/ApiQueryBase.php
@@ -52,11 +52,16 @@
5353 $this->options = array ();
5454 }
5555
56 - protected function addTables($value) {
57 - if (is_array($value))
58 - $this->tables = array_merge($this->tables, $value);
59 - else
60 - $this->tables[] = $value;
 56+ protected function addTables($tables, $alias = null) {
 57+ if (is_array($tables)) {
 58+ if (!is_null($alias))
 59+ ApiBase :: dieDebug(__METHOD__, 'Multiple table aliases not supported');
 60+ $this->tables = array_merge($this->tables, $tables);
 61+ } else {
 62+ if (!is_null($alias))
 63+ $tables = $this->getDB()->tableName($tables) . ' ' . $alias;
 64+ $this->tables[] = $tables;
 65+ }
6166 }
6267
6368 protected function addFields($value) {
Index: trunk/phase3/includes/api/ApiQueryRevisions.php
@@ -219,7 +219,6 @@
220220
221221 if ($this->fld_ids) {
222222 $vals['revid'] = intval($row->rev_id);
223 - $vals['pageid'] = intval($row->rev_page);
224223 // $vals['oldid'] = intval($row->rev_text_id); // todo: should this be exposed?
225224 }
226225
Index: trunk/phase3/includes/api/ApiQueryAllUsers.php
@@ -5,7 +5,7 @@
66 *
77 * API for MediaWiki 1.8+
88 *
9 - * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com
 9+ * Copyright (C) 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
1010 *
1111 * This program is free software; you can redistribute it and/or modify
1212 * it under the terms of the GNU General Public License as published by
@@ -47,42 +47,107 @@
4848 if (!is_null($prop)) {
4949 $prop = array_flip($prop);
5050 $fld_editcount = isset($prop['editcount']);
 51+ $fld_groups = isset($prop['groups']);
5152 } else {
52 - $fld_editcount = false;
 53+ $fld_editcount = $fld_groups = false;
5354 }
5455
55 - $this->addTables('user');
 56+ $limit = $params['limit'];
 57+ $tables = $db->tableName('user');
5658
5759 if (!is_null($params['from']))
5860 $this->addWhere('user_name>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from'])));
5961
 62+ if (isset($params['prefix']))
 63+ $this->addWhere("user_name LIKE '" . $db->escapeLike(ApiQueryBase :: titleToKey($params['prefix'])) . "%'");
 64+
 65+ if (!is_null($params['group'])) {
 66+ // Filter only users that belong to a given group
 67+ $tblName = $db->tableName('user_groups');
 68+ $tables = "$tables INNER JOIN $tblName ug1 ON ug1.ug_user=user_id";
 69+ $this->addWhereFld('ug1.ug_group', $params['group']);
 70+ }
 71+
 72+ if ($fld_groups) {
 73+ // Show the groups the given users belong to
 74+ // request more than needed to avoid not getting all rows that belong to one user
 75+ $groupCount = count(User::getAllGroups());
 76+ $sqlLimit = $limit+$groupCount+1;
 77+
 78+ $tblName = $db->tableName('user_groups');
 79+ $tables = "$tables LEFT JOIN $tblName ug2 ON ug2.ug_user=user_id";
 80+ $this->addFields('ug2.ug_group ug_group2');
 81+ } else {
 82+ $sqlLimit = $limit+1;
 83+ }
 84+
 85+ $this->addOption('LIMIT', $sqlLimit);
 86+ $this->addTables($tables);
 87+
6088 $this->addFields('user_name');
6189 $this->addFieldsIf('user_editcount', $fld_editcount);
6290
63 - $limit = $params['limit'];
64 - $this->addOption('LIMIT', $limit+1);
6591 $this->addOption('ORDER BY', 'user_name');
6692
6793 $res = $this->select(__METHOD__);
6894
6995 $data = array ();
7096 $count = 0;
71 - while ($row = $db->fetchObject($res)) {
72 - if (++ $count > $limit) {
73 - // We've reached the one extra which shows that there are additional pages to be had. Stop here...
74 - $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->user_name));
75 - break;
76 - }
 97+ $lastUserData = false;
 98+ $lastUser = false;
 99+ $result = $this->getResult();
 100+
 101+ //
 102+ // This loop keeps track of the last entry.
 103+ // For each new row, if the new row is for different user then the last, the last entry is added to results.
 104+ // Otherwise, the group of the new row is appended to the last entry.
 105+ // The setContinue... is more complex because of this, and takes into account the higher sql limit
 106+ // to make sure all rows that belong to the same user are received.
 107+ //
 108+ while (true) {
 109+
 110+ $row = $db->fetchObject($res);
 111+ $count++;
 112+
 113+ if (!$row || $lastUser != $row->user_name) {
 114+ // Save the last pass's user data
 115+ if (is_array($lastUserData))
 116+ $data[] = $lastUserData;
 117+
 118+ // No more rows left
 119+ if (!$row)
 120+ break;
77121
78 - $vals = array( 'name' => $row->user_name );
79 - if ($fld_editcount) {
80 - $vals['editcount'] = intval($row->user_editcount);
 122+ if ($count > $limit) {
 123+ // We've reached the one extra which shows that there are additional pages to be had. Stop here...
 124+ $this->setContinueEnumParameter('from', ApiQueryBase :: keyToTitle($row->user_name));
 125+ break;
 126+ }
 127+
 128+ // Record new user's data
 129+ $lastUser = $row->user_name;
 130+ $lastUserData = array( 'name' => $lastUser );
 131+ if ($fld_editcount)
 132+ $lastUserData['editcount'] = intval($row->user_editcount);
 133+
81134 }
82 - $data[] = $vals;
 135+
 136+ if ($sqlLimit == $count) {
 137+ // BUG! database contains group name that User::getAllGroups() does not return
 138+ // TODO: should handle this more gracefully
 139+ ApiBase :: dieDebug(__METHOD__,
 140+ 'MediaWiki configuration error: the database contains more user groups than known to User::getAllGroups() function');
 141+ }
 142+
 143+ // Add user's group info
 144+ if ($fld_groups && !is_null($row->ug_group2)) {
 145+ $lastUserData['groups'][] = $row->ug_group2;
 146+ $result->setIndexedTagName($lastUserData['groups'], 'g');
 147+ }
83148 }
 149+
84150 $db->freeResult($res);
85151
86 - $result = $this->getResult();
87152 $result->setIndexedTagName($data, 'u');
88153 $result->addValue('query', $this->getModuleName(), $data);
89154 }
@@ -90,10 +155,15 @@
91156 protected function getAllowedParams() {
92157 return array (
93158 'from' => null,
 159+ 'prefix' => null,
 160+ 'group' => array(
 161+ ApiBase :: PARAM_TYPE => User::getAllGroups()
 162+ ),
94163 'prop' => array (
95164 ApiBase :: PARAM_ISMULTI => true,
96165 ApiBase :: PARAM_TYPE => array (
97 - 'editcount'
 166+ 'editcount',
 167+ 'groups',
98168 )
99169 ),
100170 'limit' => array (
@@ -109,8 +179,12 @@
110180 protected function getParamDescription() {
111181 return array (
112182 'from' => 'The user name to start enumerating from.',
113 - 'prop' => 'What pieces of information to include',
114 - 'limit' => 'How many total user names to return.'
 183+ 'prefix' => 'Search for all page titles that begin with this value.',
 184+ 'group' => 'Limit users to a given group name',
 185+ 'prop' => array(
 186+ 'What pieces of information to include.',
 187+ '`groups` property uses more server resources and may return fewer results than the limit.'),
 188+ 'limit' => 'How many total user names to return.',
115189 );
116190 }
117191
Index: trunk/phase3/includes/api/ApiQuerySearch.php
@@ -0,0 +1,151 @@
 2+<?php
 3+
 4+/*
 5+ * Created on July 30, 2007
 6+ *
 7+ * API for MediaWiki 1.8+
 8+ *
 9+ * Copyright (C) 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com
 10+ *
 11+ * This program is free software; you can redistribute it and/or modify
 12+ * it under the terms of the GNU General Public License as published by
 13+ * the Free Software Foundation; either version 2 of the License, or
 14+ * (at your option) any later version.
 15+ *
 16+ * This program is distributed in the hope that it will be useful,
 17+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
 18+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 19+ * GNU General Public License for more details.
 20+ *
 21+ * You should have received a copy of the GNU General Public License along
 22+ * with this program; if not, write to the Free Software Foundation, Inc.,
 23+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 24+ * http://www.gnu.org/copyleft/gpl.html
 25+ */
 26+
 27+if (!defined('MEDIAWIKI')) {
 28+ // Eclipse helper - will be ignored in production
 29+ require_once ('ApiQueryBase.php');
 30+}
 31+
 32+/**
 33+ * Query module to perform full text search within wiki titles and content
 34+ *
 35+ * @addtogroup API
 36+ */
 37+class ApiQuerySearch extends ApiQueryGeneratorBase {
 38+
 39+ public function __construct($query, $moduleName) {
 40+ parent :: __construct($query, $moduleName, 'sr');
 41+ }
 42+
 43+ public function execute() {
 44+ $this->run();
 45+ }
 46+
 47+ public function executeGenerator($resultPageSet) {
 48+ $this->run($resultPageSet);
 49+ }
 50+
 51+ private function run($resultPageSet = null) {
 52+
 53+ $params = $this->extractRequestParams();
 54+
 55+ $limit = $params['limit'];
 56+ $query = $params['search'];
 57+ if (is_null($query) || empty($query))
 58+ $this->dieUsage("empty search string is not allowed", 'param-search');
 59+
 60+ $search = SearchEngine::create();
 61+ $search->setLimitOffset( $limit+1, $params['offset'] );
 62+ $search->setNamespaces( $params['namespace'] );
 63+ $search->showRedirects = $params['redirects'];
 64+
 65+ if ($params['what'] == 'text')
 66+ $matches = $search->searchText( $query );
 67+ else
 68+ $matches = $search->searchTitle( $query );
 69+
 70+ $data = array ();
 71+ $count = 0;
 72+ while( $result = $matches->next() ) {
 73+ if (++ $count > $limit) {
 74+ // We've reached the one extra which shows that there are additional items to be had. Stop here...
 75+ $this->setContinueEnumParameter('offset', $params['offset'] + $params['limit']);
 76+ break;
 77+ }
 78+
 79+ $title = $result->getTitle();
 80+ if (is_null($resultPageSet)) {
 81+ $data[] = array(
 82+ 'ns' => intval($title->getNamespace()),
 83+ 'title' => $title->getPrefixedText());
 84+ } else {
 85+ $data[] = $title;
 86+ }
 87+ }
 88+
 89+ if (is_null($resultPageSet)) {
 90+ $result = $this->getResult();
 91+ $result->setIndexedTagName($data, 'p');
 92+ $result->addValue('query', $this->getModuleName(), $data);
 93+ } else {
 94+ $resultPageSet->populateFromTitles($data);
 95+ }
 96+ }
 97+
 98+ protected function getAllowedParams() {
 99+ return array (
 100+ 'search' => null,
 101+ 'namespace' => array (
 102+ ApiBase :: PARAM_DFLT => 0,
 103+ ApiBase :: PARAM_TYPE => 'namespace',
 104+ ApiBase :: PARAM_ISMULTI => true,
 105+ ),
 106+ 'what' => array (
 107+ ApiBase :: PARAM_DFLT => 'title',
 108+ ApiBase :: PARAM_TYPE => array (
 109+ 'title',
 110+ 'text',
 111+ )
 112+ ),
 113+ 'redirects' => false,
 114+ 'offset' => 0,
 115+ 'limit' => array (
 116+ ApiBase :: PARAM_DFLT => 10,
 117+ ApiBase :: PARAM_TYPE => 'limit',
 118+ ApiBase :: PARAM_MIN => 1,
 119+ ApiBase :: PARAM_MAX => ApiBase :: LIMIT_BIG1,
 120+ ApiBase :: PARAM_MAX2 => ApiBase :: LIMIT_BIG2
 121+ )
 122+ );
 123+ }
 124+
 125+ protected function getParamDescription() {
 126+ return array (
 127+ 'search' => 'Search for all page titles (or content) that has this value.',
 128+ 'namespace' => 'The namespace(s) to enumerate.',
 129+ 'what' => 'Search inside the text or titles.',
 130+ 'redirects' => 'Include redirect pages in the search.',
 131+ 'offset' => 'Use this value to continue paging (return by query)',
 132+ 'limit' => 'How many total pages to return.'
 133+ );
 134+ }
 135+
 136+ protected function getDescription() {
 137+ return 'Perform a full text search';
 138+ }
 139+
 140+ protected function getExamples() {
 141+ return array (
 142+ 'api.php?action=query&list=search&srsearch=meaning',
 143+ 'api.php?action=query&list=search&srwhat=text&srsearch=meaning',
 144+ 'api.php?action=query&generator=search&gsrsearch=meaning&prop=info',
 145+ );
 146+ }
 147+
 148+ public function getVersion() {
 149+ return __CLASS__ . ': $Id$';
 150+ }
 151+}
 152+
Property changes on: trunk/phase3/includes/api/ApiQuerySearch.php
___________________________________________________________________
Added: svn:eol-style
1153 + native
Added: svn:keywords
2154 + Id
Index: trunk/phase3/includes/AutoLoader.php
@@ -328,6 +328,7 @@
329329 'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php',
330330 'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php',
331331 'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php',
 332+ 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php',
332333 'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php',
333334 'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php',
334335 'ApiResult' => 'includes/api/ApiResult.php',
Index: trunk/phase3/api.php
@@ -50,7 +50,7 @@
5151 */
5252 $processor = new ApiMain($wgRequest, $wgEnableWriteAPI);
5353
54 -// Generate the output.
 54+// Process data & print results
5555 $processor->execute();
5656
5757 // Log what the user did, for book-keeping purposes.
Index: trunk/phase3/RELEASE-NOTES
@@ -380,6 +380,9 @@
381381 * Added external url search within wiki pages (list=exturlusage)
382382 * Added link enumeration (list=alllinks)
383383 * Added registered users enumeration (list=allusers)
 384+* Added full text search in titles and content (list=search)
 385+* (bug 10684) Expanded list=allusers functionality
 386+* Possible breaking change: prop=revisions no longer includes pageid for rvprop=ids
384387
385388 == Maintenance script changes since 1.10 ==
386389

Follow-up revisions

RevisionCommit summaryAuthorDate
r24502Merged revisions 24415-24479 via svnmerge from...david22:31, 31 July 2007

Status & tagging log