Index: trunk/phase3/includes/api/ApiQueryAllLinks.php |
— | — | @@ -70,7 +70,7 @@ |
71 | 71 | if (!is_null($params['from'])) |
72 | 72 | $this->addWhere('pl_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from']))); |
73 | 73 | 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'])) . "%'"); |
75 | 75 | |
76 | 76 | if (is_null($resultPageSet)) { |
77 | 77 | $this->addFields(array ( |
Index: trunk/phase3/includes/api/ApiQuery.php |
— | — | @@ -67,6 +67,7 @@ |
68 | 68 | 'imageusage' => 'ApiQueryBacklinks', |
69 | 69 | 'logevents' => 'ApiQueryLogEvents', |
70 | 70 | 'recentchanges' => 'ApiQueryRecentChanges', |
| 71 | + 'search' => 'ApiQuerySearch', |
71 | 72 | 'usercontribs' => 'ApiQueryContributions', |
72 | 73 | 'watchlist' => 'ApiQueryWatchlist', |
73 | 74 | 'exturlusage' => 'ApiQueryExtLinksUsage', |
Index: trunk/phase3/includes/api/ApiQueryAllpages.php |
— | — | @@ -63,7 +63,7 @@ |
64 | 64 | if (!is_null($params['from'])) |
65 | 65 | $this->addWhere('page_title>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from']))); |
66 | 66 | 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'])) . "%'"); |
68 | 68 | |
69 | 69 | if (is_null($resultPageSet)) { |
70 | 70 | $this->addFields(array ( |
Index: trunk/phase3/includes/api/ApiQueryImageInfo.php |
— | — | @@ -71,8 +71,11 @@ |
72 | 72 | |
73 | 73 | if ($fld_timestamp) |
74 | 74 | $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $line->img_timestamp); |
75 | | - if ($fld_user) |
| 75 | + if ($fld_user) { |
76 | 76 | $vals['user'] = $line->img_user_text; |
| 77 | + if(!$line->img_user) |
| 78 | + $vals['anon'] = ''; |
| 79 | + } |
77 | 80 | if ($fld_size) { |
78 | 81 | $vals['size'] = $line->img_size; |
79 | 82 | $vals['width'] = $line->img_width; |
Index: trunk/phase3/includes/api/ApiQueryBase.php |
— | — | @@ -52,11 +52,16 @@ |
53 | 53 | $this->options = array (); |
54 | 54 | } |
55 | 55 | |
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 | + } |
61 | 66 | } |
62 | 67 | |
63 | 68 | protected function addFields($value) { |
Index: trunk/phase3/includes/api/ApiQueryRevisions.php |
— | — | @@ -219,7 +219,6 @@ |
220 | 220 | |
221 | 221 | if ($this->fld_ids) { |
222 | 222 | $vals['revid'] = intval($row->rev_id); |
223 | | - $vals['pageid'] = intval($row->rev_page); |
224 | 223 | // $vals['oldid'] = intval($row->rev_text_id); // todo: should this be exposed? |
225 | 224 | } |
226 | 225 | |
Index: trunk/phase3/includes/api/ApiQueryAllUsers.php |
— | — | @@ -5,7 +5,7 @@ |
6 | 6 | * |
7 | 7 | * API for MediaWiki 1.8+ |
8 | 8 | * |
9 | | - * Copyright (C) 2006 Yuri Astrakhan <Firstname><Lastname>@gmail.com |
| 9 | + * Copyright (C) 2007 Yuri Astrakhan <Firstname><Lastname>@gmail.com |
10 | 10 | * |
11 | 11 | * This program is free software; you can redistribute it and/or modify |
12 | 12 | * it under the terms of the GNU General Public License as published by |
— | — | @@ -47,42 +47,107 @@ |
48 | 48 | if (!is_null($prop)) { |
49 | 49 | $prop = array_flip($prop); |
50 | 50 | $fld_editcount = isset($prop['editcount']); |
| 51 | + $fld_groups = isset($prop['groups']); |
51 | 52 | } else { |
52 | | - $fld_editcount = false; |
| 53 | + $fld_editcount = $fld_groups = false; |
53 | 54 | } |
54 | 55 | |
55 | | - $this->addTables('user'); |
| 56 | + $limit = $params['limit']; |
| 57 | + $tables = $db->tableName('user'); |
56 | 58 | |
57 | 59 | if (!is_null($params['from'])) |
58 | 60 | $this->addWhere('user_name>=' . $db->addQuotes(ApiQueryBase :: titleToKey($params['from']))); |
59 | 61 | |
| 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 | + |
60 | 88 | $this->addFields('user_name'); |
61 | 89 | $this->addFieldsIf('user_editcount', $fld_editcount); |
62 | 90 | |
63 | | - $limit = $params['limit']; |
64 | | - $this->addOption('LIMIT', $limit+1); |
65 | 91 | $this->addOption('ORDER BY', 'user_name'); |
66 | 92 | |
67 | 93 | $res = $this->select(__METHOD__); |
68 | 94 | |
69 | 95 | $data = array (); |
70 | 96 | $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; |
77 | 121 | |
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 | + |
81 | 134 | } |
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 | + } |
83 | 148 | } |
| 149 | + |
84 | 150 | $db->freeResult($res); |
85 | 151 | |
86 | | - $result = $this->getResult(); |
87 | 152 | $result->setIndexedTagName($data, 'u'); |
88 | 153 | $result->addValue('query', $this->getModuleName(), $data); |
89 | 154 | } |
— | — | @@ -90,10 +155,15 @@ |
91 | 156 | protected function getAllowedParams() { |
92 | 157 | return array ( |
93 | 158 | 'from' => null, |
| 159 | + 'prefix' => null, |
| 160 | + 'group' => array( |
| 161 | + ApiBase :: PARAM_TYPE => User::getAllGroups() |
| 162 | + ), |
94 | 163 | 'prop' => array ( |
95 | 164 | ApiBase :: PARAM_ISMULTI => true, |
96 | 165 | ApiBase :: PARAM_TYPE => array ( |
97 | | - 'editcount' |
| 166 | + 'editcount', |
| 167 | + 'groups', |
98 | 168 | ) |
99 | 169 | ), |
100 | 170 | 'limit' => array ( |
— | — | @@ -109,8 +179,12 @@ |
110 | 180 | protected function getParamDescription() { |
111 | 181 | return array ( |
112 | 182 | '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.', |
115 | 189 | ); |
116 | 190 | } |
117 | 191 | |
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 |
1 | 153 | + native |
Added: svn:keywords |
2 | 154 | + Id |
Index: trunk/phase3/includes/AutoLoader.php |
— | — | @@ -328,6 +328,7 @@ |
329 | 329 | 'ApiQueryLogEvents' => 'includes/api/ApiQueryLogEvents.php', |
330 | 330 | 'ApiQueryRecentChanges'=> 'includes/api/ApiQueryRecentChanges.php', |
331 | 331 | 'ApiQueryRevisions' => 'includes/api/ApiQueryRevisions.php', |
| 332 | + 'ApiQuerySearch' => 'includes/api/ApiQuerySearch.php', |
332 | 333 | 'ApiQuerySiteinfo' => 'includes/api/ApiQuerySiteinfo.php', |
333 | 334 | 'ApiQueryWatchlist' => 'includes/api/ApiQueryWatchlist.php', |
334 | 335 | 'ApiResult' => 'includes/api/ApiResult.php', |
Index: trunk/phase3/api.php |
— | — | @@ -50,7 +50,7 @@ |
51 | 51 | */ |
52 | 52 | $processor = new ApiMain($wgRequest, $wgEnableWriteAPI); |
53 | 53 | |
54 | | -// Generate the output. |
| 54 | +// Process data & print results |
55 | 55 | $processor->execute(); |
56 | 56 | |
57 | 57 | // Log what the user did, for book-keeping purposes. |
Index: trunk/phase3/RELEASE-NOTES |
— | — | @@ -380,6 +380,9 @@ |
381 | 381 | * Added external url search within wiki pages (list=exturlusage) |
382 | 382 | * Added link enumeration (list=alllinks) |
383 | 383 | * 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 |
384 | 387 | |
385 | 388 | == Maintenance script changes since 1.10 == |
386 | 389 | |