* API: implemented generator function
[lhc/web/wiklou.git] / includes / api / ApiQuery.php
1 <?php
2
3
4 /*
5 * Created on Sep 7, 2006
6 *
7 * API for MediaWiki 1.8+
8 *
9 * Copyright (C) 2006 Yuri Astrakhan <FirstnameLastname@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 ('ApiBase.php');
30 }
31
32 class ApiQuery extends ApiBase {
33
34 private $mPropModuleNames, $mListModuleNames, $mMetaModuleNames;
35 private $mPageSet;
36
37 private $mQueryPropModules = array (
38 'info' => 'ApiQueryInfo',
39 'revisions' => 'ApiQueryRevisions'
40 );
41 // 'categories' => 'ApiQueryCategories',
42 // 'imageinfo' => 'ApiQueryImageinfo',
43 // 'langlinks' => 'ApiQueryLanglinks',
44 // 'links' => 'ApiQueryLinks',
45 // 'templates' => 'ApiQueryTemplates',
46
47 private $mQueryListModules = array (
48 'allpages' => 'ApiQueryAllpages'
49 );
50 // 'backlinks' => 'ApiQueryBacklinks',
51 // 'categorymembers' => 'ApiQueryCategorymembers',
52 // 'embeddedin' => 'ApiQueryEmbeddedin',
53 // 'imagelinks' => 'ApiQueryImagelinks',
54 // 'logevents' => 'ApiQueryLogevents',
55 // 'recentchanges' => 'ApiQueryRecentchanges',
56 // 'usercontribs' => 'ApiQueryUsercontribs',
57 // 'users' => 'ApiQueryUsers',
58 // 'watchlist' => 'ApiQueryWatchlist',
59
60 private $mQueryMetaModules = array (
61 'siteinfo' => 'ApiQuerySiteinfo'
62 );
63 // 'userinfo' => 'ApiQueryUserinfo',
64
65 private $mSlaveDB = null;
66
67 public function __construct($main, $action) {
68 parent :: __construct($main);
69 $this->mPropModuleNames = array_keys($this->mQueryPropModules);
70 $this->mListModuleNames = array_keys($this->mQueryListModules);
71 $this->mMetaModuleNames = array_keys($this->mQueryMetaModules);
72
73 // Allow the entire list of modules at first,
74 // but during module instantiation check if it can be used as a generator.
75 $this->mAllowedGenerators = array_merge($this->mListModuleNames, $this->mPropModuleNames);
76 }
77
78 public function getDB() {
79 if (!isset ($this->mSlaveDB))
80 $this->mSlaveDB = & wfGetDB(DB_SLAVE);
81 return $this->mSlaveDB;
82 }
83
84 public function getPageSet() {
85 return $this->mPageSet;
86 }
87
88 /**
89 * Query execution happens in the following steps:
90 * #1 Create a PageSet object with any pages requested by the user
91 * #2 If using generator, execute it to get a new PageSet object
92 * #3 Instantiate all requested modules.
93 * This way the PageSet object will know what shared data is required,
94 * and minimize DB calls.
95 * #4 Output all normalization and redirect resolution information
96 * #5 Execute all requested modules
97 */
98 public function execute() {
99 $prop = $list = $meta = $generator = null;
100 extract($this->extractRequestParams());
101
102 //
103 // Create PageSet
104 //
105 $this->mPageSet = new ApiPageSet($this);
106
107 //
108 // If generator is provided, get a new dataset to work on
109 //
110 if (isset ($generator))
111 $this->executeGenerator($generator);
112
113 // Instantiate required modules
114 $modules = array ();
115 if (isset ($prop))
116 foreach ($prop as $moduleName)
117 $modules[] = new $this->mQueryPropModules[$moduleName] ($this, $moduleName);
118 if (isset ($list))
119 foreach ($list as $moduleName)
120 $modules[] = new $this->mQueryListModules[$moduleName] ($this, $moduleName);
121 if (isset ($meta))
122 foreach ($meta as $moduleName)
123 $modules[] = new $this->mQueryMetaModules[$moduleName] ($this, $moduleName);
124
125 // Modules may optimize data requests through the $this->getPageSet() object
126 // Execute all requested modules.
127 foreach ($modules as $module) {
128 $module->requestExtraData();
129 }
130
131 //
132 // Get page information for the given pageSet
133 //
134 $this->mPageSet->execute();
135
136 //
137 // Record page information
138 //
139 $this->outputGeneralPageInfo();
140
141 // Execute all requested modules.
142 foreach ($modules as $module) {
143 $module->profileIn();
144 $module->execute();
145 $module->profileOut();
146 }
147 }
148
149 private function outputGeneralPageInfo() {
150
151 $pageSet = $this->getPageSet();
152
153 // Title normalizations
154 $normValues = array ();
155 foreach ($pageSet->getNormalizedTitles() as $rawTitleStr => $titleStr) {
156 $normValues[] = array (
157 'from' => $rawTitleStr,
158 'to' => $titleStr
159 );
160 }
161
162 if (!empty ($normValues)) {
163 ApiResult :: setIndexedTagName($normValues, 'n');
164 $this->getResult()->addValue('query', 'normalized', $normValues);
165 }
166
167 // Show redirect information
168 $redirValues = array ();
169 foreach ($pageSet->getRedirectTitles() as $titleStrFrom => $titleStrTo) {
170 $redirValues[] = array (
171 'from' => $titleStrFrom,
172 'to' => $titleStrTo
173 );
174 }
175
176 if (!empty ($redirValues)) {
177 ApiResult :: setIndexedTagName($redirValues, 'r');
178 $this->getResult()->addValue('query', 'redirects', $redirValues);
179 }
180
181 //
182 // Page elements
183 //
184 $pages = array ();
185
186 // Report any missing titles
187 $fakepageid = -1;
188 foreach ($pageSet->getMissingTitles() as $title) {
189 $pages[$fakepageid--] = array (
190 'ns' => $title->getNamespace(), 'title' => $title->getPrefixedText(), 'missing' => '');
191 }
192
193 // Report any missing page ids
194 foreach ($pageSet->getMissingPageIDs() as $pageid) {
195 $pages[$pageid] = array (
196 'id' => $pageid,
197 'missing' => ''
198 );
199 }
200
201 // Output general page information for found titles
202 foreach ($pageSet->getGoodTitles() as $pageid => $title) {
203 $pages[$pageid] = array (
204 'ns' => $title->getNamespace(), 'title' => $title->getPrefixedText(), 'id' => $pageid);
205 }
206
207 if (!empty ($pages)) {
208 ApiResult :: setIndexedTagName($pages, 'page');
209 $this->getResult()->addValue('query', 'pages', $pages);
210 }
211 }
212
213 protected function executeGenerator($generatorName) {
214
215 // Find class that implements requested generator
216 if (isset ($this->mQueryListModules[$generatorName]))
217 $className = $this->mQueryListModules[$generatorName];
218 elseif (isset ($this->mQueryPropModules[$generatorName])) $className = $this->mQueryPropModules[$generatorName];
219 else
220 ApiBase :: dieDebug(__METHOD__, "Unknown generator=$generatorName");
221
222 $generator = new $className ($this, $generatorName, true);
223 if (!$generator->getCanGenerate())
224 $this->dieUsage("Module $generatorName cannot be used as a generator", "badgenerator");
225
226 $generator->requestExtraData();
227
228 // execute pageSet here to get the data required by the generator module
229 $this->mPageSet->execute();
230
231 $generator->profileIn();
232 $this->mPageSet = $generator->execute();
233 $generator->profileOut();
234 }
235
236 protected function getAllowedParams() {
237 return array (
238 'prop' => array (
239 ApiBase :: PARAM_ISMULTI => true,
240 ApiBase :: PARAM_TYPE => $this->mPropModuleNames
241 ),
242 'list' => array (
243 ApiBase :: PARAM_ISMULTI => true,
244 ApiBase :: PARAM_TYPE => $this->mListModuleNames
245 ),
246 'meta' => array (
247 ApiBase :: PARAM_ISMULTI => true,
248 ApiBase :: PARAM_TYPE => $this->mMetaModuleNames
249 ),
250 'generator' => array (
251 ApiBase::PARAM_TYPE => $this->mAllowedGenerators
252 )
253 );
254 }
255
256 /**
257 * Override the parent to generate help messages for all available query modules.
258 */
259 public function makeHelpMsg() {
260
261 // Use parent to make default message for the query module
262 $msg = parent :: makeHelpMsg();
263
264 // Make sure the internal object is empty
265 // (just in case a sub-module decides to optimize during instantiation)
266 $this->mPageSet = null;
267
268 $astriks = str_repeat('--- ', 8);
269 $msg .= "\n$astriks Query: Prop $astriks\n\n";
270 $msg .= $this->makeHelpMsgHelper($this->mQueryPropModules, 'prop');
271 $msg .= "\n$astriks Query: List $astriks\n\n";
272 $msg .= $this->makeHelpMsgHelper($this->mQueryListModules, 'list');
273 $msg .= "\n$astriks Query: Meta $astriks\n\n";
274 $msg .= $this->makeHelpMsgHelper($this->mQueryMetaModules, 'meta');
275
276 return $msg;
277 }
278
279 private function makeHelpMsgHelper($moduleList, $paramName) {
280
281 $moduleDscriptions = array ();
282
283 foreach ($moduleList as $moduleName => $moduleClass) {
284 $msg = "* $paramName=$moduleName *";
285 $module = new $moduleClass ($this, $moduleName, null);
286 $msg2 = $module->makeHelpMsg();
287 if ($msg2 !== false)
288 $msg .= $msg2;
289 if ($module->getCanGenerate())
290 $msg .= "Generator:\n This module may be used as a generator\n";
291 $moduleDscriptions[] = $msg;
292 }
293
294 return implode("\n", $moduleDscriptions);
295 }
296
297 /**
298 * Override to add extra parameters from PageSet
299 */
300 public function makeHelpMsgParameters() {
301 $psModule = new ApiPageSet($this);
302 return $psModule->makeHelpMsgParameters() . parent :: makeHelpMsgParameters();
303 }
304
305 protected function getParamDescription() {
306 return array (
307 'prop' => 'Which properties to get for the titles/revisions/pageids',
308 'list' => 'Which lists to get',
309 'meta' => 'Which meta data to get about the site',
310 'generator' => 'Use the output of a list as the input for other prop/list/meta items'
311 );
312 }
313
314 protected function getDescription() {
315 return array (
316 'Query API module allows applications to get needed pieces of data from the MediaWiki databases,',
317 'and is loosely based on the Query API interface currently available on all MediaWiki servers.',
318 'All data modifications will first have to use query to acquire a token to prevent abuse from malicious sites.'
319 );
320 }
321
322 protected function getExamples() {
323 return array (
324 'api.php?action=query&prop=revisions&meta=siteinfo&titles=Main%20Page&rvprop=user|comment'
325 );
326 }
327
328 public function getVersion() {
329 $psModule = new ApiPageSet($this);
330 $vers = array();
331 $vers[] = __CLASS__ . ': $Id$';
332 $vers[] = $psModule->getVersion();
333 return $vers;
334 }
335 }
336 ?>