|
1
|
|
|
<?php |
|
2
|
|
|
/** |
|
3
|
|
|
* |
|
4
|
|
|
* |
|
5
|
|
|
* Created on Sep 5, 2006 |
|
6
|
|
|
* |
|
7
|
|
|
* Copyright © 2006, 2010 Yuri Astrakhan "<Firstname><Lastname>@gmail.com" |
|
8
|
|
|
* |
|
9
|
|
|
* This program is free software; you can redistribute it and/or modify |
|
10
|
|
|
* it under the terms of the GNU General Public License as published by |
|
11
|
|
|
* the Free Software Foundation; either version 2 of the License, or |
|
12
|
|
|
* (at your option) any later version. |
|
13
|
|
|
* |
|
14
|
|
|
* This program is distributed in the hope that it will be useful, |
|
15
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
16
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
17
|
|
|
* GNU General Public License for more details. |
|
18
|
|
|
* |
|
19
|
|
|
* You should have received a copy of the GNU General Public License along |
|
20
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc., |
|
21
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
|
22
|
|
|
* http://www.gnu.org/copyleft/gpl.html |
|
23
|
|
|
* |
|
24
|
|
|
* @file |
|
25
|
|
|
*/ |
|
26
|
|
|
|
|
27
|
|
|
/** |
|
28
|
|
|
* This abstract class implements many basic API functions, and is the base of |
|
29
|
|
|
* all API classes. |
|
30
|
|
|
* The class functions are divided into several areas of functionality: |
|
31
|
|
|
* |
|
32
|
|
|
* Module parameters: Derived classes can define getAllowedParams() to specify |
|
33
|
|
|
* which parameters to expect, how to parse and validate them. |
|
34
|
|
|
* |
|
35
|
|
|
* Self-documentation: code to allow the API to document its own state |
|
36
|
|
|
* |
|
37
|
|
|
* @ingroup API |
|
38
|
|
|
*/ |
|
39
|
|
|
abstract class ApiBase extends ContextSource { |
|
40
|
|
|
|
|
41
|
|
|
/** |
|
42
|
|
|
* @name Constants for ::getAllowedParams() arrays |
|
43
|
|
|
* These constants are keys in the arrays returned by ::getAllowedParams() |
|
44
|
|
|
* and accepted by ::getParameterFromSettings() that define how the |
|
45
|
|
|
* parameters coming in from the request are to be interpreted. |
|
46
|
|
|
* @{ |
|
47
|
|
|
*/ |
|
48
|
|
|
|
|
49
|
|
|
/** (null|boolean|integer|string) Default value of the parameter. */ |
|
50
|
|
|
const PARAM_DFLT = 0; |
|
51
|
|
|
|
|
52
|
|
|
/** (boolean) Accept multiple pipe-separated values for this parameter (e.g. titles)? */ |
|
53
|
|
|
const PARAM_ISMULTI = 1; |
|
54
|
|
|
|
|
55
|
|
|
/** |
|
56
|
|
|
* (string|string[]) Either an array of allowed value strings, or a string |
|
57
|
|
|
* type as described below. If not specified, will be determined from the |
|
58
|
|
|
* type of PARAM_DFLT. |
|
59
|
|
|
* |
|
60
|
|
|
* Supported string types are: |
|
61
|
|
|
* - boolean: A boolean parameter, returned as false if the parameter is |
|
62
|
|
|
* omitted and true if present (even with a falsey value, i.e. it works |
|
63
|
|
|
* like HTML checkboxes). PARAM_DFLT must be boolean false, if specified. |
|
64
|
|
|
* Cannot be used with PARAM_ISMULTI. |
|
65
|
|
|
* - integer: An integer value. See also PARAM_MIN, PARAM_MAX, and |
|
66
|
|
|
* PARAM_RANGE_ENFORCE. |
|
67
|
|
|
* - limit: An integer or the string 'max'. Default lower limit is 0 (but |
|
68
|
|
|
* see PARAM_MIN), and requires that PARAM_MAX and PARAM_MAX2 be |
|
69
|
|
|
* specified. Cannot be used with PARAM_ISMULTI. |
|
70
|
|
|
* - namespace: An integer representing a MediaWiki namespace. |
|
71
|
|
|
* - NULL: Any string. |
|
72
|
|
|
* - password: Any non-empty string. Input value is private or sensitive. |
|
73
|
|
|
* <input type="password"> would be an appropriate HTML form field. |
|
74
|
|
|
* - string: Any non-empty string, not expected to be very long or contain newlines. |
|
75
|
|
|
* <input type="text"> would be an appropriate HTML form field. |
|
76
|
|
|
* - submodule: The name of a submodule of this module, see PARAM_SUBMODULE_MAP. |
|
77
|
|
|
* - tags: A string naming an existing, explicitly-defined tag. Should usually be |
|
78
|
|
|
* used with PARAM_ISMULTI. |
|
79
|
|
|
* - text: Any non-empty string, expected to be very long or contain newlines. |
|
80
|
|
|
* <textarea> would be an appropriate HTML form field. |
|
81
|
|
|
* - timestamp: A timestamp in any format recognized by MWTimestamp, or the |
|
82
|
|
|
* string 'now' representing the current timestamp. Will be returned in |
|
83
|
|
|
* TS_MW format. |
|
84
|
|
|
* - user: A MediaWiki username or IP. Will be returned normalized but not canonicalized. |
|
85
|
|
|
* - upload: An uploaded file. Will be returned as a WebRequestUpload object. |
|
86
|
|
|
* Cannot be used with PARAM_ISMULTI. |
|
87
|
|
|
*/ |
|
88
|
|
|
const PARAM_TYPE = 2; |
|
89
|
|
|
|
|
90
|
|
|
/** (integer) Max value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'. */ |
|
91
|
|
|
const PARAM_MAX = 3; |
|
92
|
|
|
|
|
93
|
|
|
/** |
|
94
|
|
|
* (integer) Max value allowed for the parameter for users with the |
|
95
|
|
|
* apihighlimits right, for PARAM_TYPE 'limit'. |
|
96
|
|
|
*/ |
|
97
|
|
|
const PARAM_MAX2 = 4; |
|
98
|
|
|
|
|
99
|
|
|
/** (integer) Lowest value allowed for the parameter, for PARAM_TYPE 'integer' and 'limit'. */ |
|
100
|
|
|
const PARAM_MIN = 5; |
|
101
|
|
|
|
|
102
|
|
|
/** (boolean) Allow the same value to be set more than once when PARAM_ISMULTI is true? */ |
|
103
|
|
|
const PARAM_ALLOW_DUPLICATES = 6; |
|
104
|
|
|
|
|
105
|
|
|
/** (boolean) Is the parameter deprecated (will show a warning)? */ |
|
106
|
|
|
const PARAM_DEPRECATED = 7; |
|
107
|
|
|
|
|
108
|
|
|
/** |
|
109
|
|
|
* (boolean) Is the parameter required? |
|
110
|
|
|
* @since 1.17 |
|
111
|
|
|
*/ |
|
112
|
|
|
const PARAM_REQUIRED = 8; |
|
113
|
|
|
|
|
114
|
|
|
/** |
|
115
|
|
|
* (boolean) For PARAM_TYPE 'integer', enforce PARAM_MIN and PARAM_MAX? |
|
116
|
|
|
* @since 1.17 |
|
117
|
|
|
*/ |
|
118
|
|
|
const PARAM_RANGE_ENFORCE = 9; |
|
119
|
|
|
|
|
120
|
|
|
/** |
|
121
|
|
|
* (string|array|Message) Specify an alternative i18n documentation message |
|
122
|
|
|
* for this parameter. Default is apihelp-{$path}-param-{$param}. |
|
123
|
|
|
* @since 1.25 |
|
124
|
|
|
*/ |
|
125
|
|
|
const PARAM_HELP_MSG = 10; |
|
126
|
|
|
|
|
127
|
|
|
/** |
|
128
|
|
|
* ((string|array|Message)[]) Specify additional i18n messages to append to |
|
129
|
|
|
* the normal message for this parameter. |
|
130
|
|
|
* @since 1.25 |
|
131
|
|
|
*/ |
|
132
|
|
|
const PARAM_HELP_MSG_APPEND = 11; |
|
133
|
|
|
|
|
134
|
|
|
/** |
|
135
|
|
|
* (array) Specify additional information tags for the parameter. Value is |
|
136
|
|
|
* an array of arrays, with the first member being the 'tag' for the info |
|
137
|
|
|
* and the remaining members being the values. In the help, this is |
|
138
|
|
|
* formatted using apihelp-{$path}-paraminfo-{$tag}, which is passed |
|
139
|
|
|
* $1 = count, $2 = comma-joined list of values, $3 = module prefix. |
|
140
|
|
|
* @since 1.25 |
|
141
|
|
|
*/ |
|
142
|
|
|
const PARAM_HELP_MSG_INFO = 12; |
|
143
|
|
|
|
|
144
|
|
|
/** |
|
145
|
|
|
* (string[]) When PARAM_TYPE is an array, this may be an array mapping |
|
146
|
|
|
* those values to page titles which will be linked in the help. |
|
147
|
|
|
* @since 1.25 |
|
148
|
|
|
*/ |
|
149
|
|
|
const PARAM_VALUE_LINKS = 13; |
|
150
|
|
|
|
|
151
|
|
|
/** |
|
152
|
|
|
* ((string|array|Message)[]) When PARAM_TYPE is an array, this is an array |
|
153
|
|
|
* mapping those values to $msg for ApiBase::makeMessage(). Any value not |
|
154
|
|
|
* having a mapping will use apihelp-{$path}-paramvalue-{$param}-{$value}. |
|
155
|
|
|
* @since 1.25 |
|
156
|
|
|
*/ |
|
157
|
|
|
const PARAM_HELP_MSG_PER_VALUE = 14; |
|
158
|
|
|
|
|
159
|
|
|
/** |
|
160
|
|
|
* (string[]) When PARAM_TYPE is 'submodule', map parameter values to |
|
161
|
|
|
* submodule paths. Default is to use all modules in |
|
162
|
|
|
* $this->getModuleManager() in the group matching the parameter name. |
|
163
|
|
|
* @since 1.26 |
|
164
|
|
|
*/ |
|
165
|
|
|
const PARAM_SUBMODULE_MAP = 15; |
|
166
|
|
|
|
|
167
|
|
|
/** |
|
168
|
|
|
* (string) When PARAM_TYPE is 'submodule', used to indicate the 'g' prefix |
|
169
|
|
|
* added by ApiQueryGeneratorBase (and similar if anything else ever does that). |
|
170
|
|
|
* @since 1.26 |
|
171
|
|
|
*/ |
|
172
|
|
|
const PARAM_SUBMODULE_PARAM_PREFIX = 16; |
|
173
|
|
|
|
|
174
|
|
|
/**@}*/ |
|
175
|
|
|
|
|
176
|
|
|
/** Fast query, standard limit. */ |
|
177
|
|
|
const LIMIT_BIG1 = 500; |
|
178
|
|
|
/** Fast query, apihighlimits limit. */ |
|
179
|
|
|
const LIMIT_BIG2 = 5000; |
|
180
|
|
|
/** Slow query, standard limit. */ |
|
181
|
|
|
const LIMIT_SML1 = 50; |
|
182
|
|
|
/** Slow query, apihighlimits limit. */ |
|
183
|
|
|
const LIMIT_SML2 = 500; |
|
184
|
|
|
|
|
185
|
|
|
/** |
|
186
|
|
|
* getAllowedParams() flag: When set, the result could take longer to generate, |
|
187
|
|
|
* but should be more thorough. E.g. get the list of generators for ApiSandBox extension |
|
188
|
|
|
* @since 1.21 |
|
189
|
|
|
*/ |
|
190
|
|
|
const GET_VALUES_FOR_HELP = 1; |
|
191
|
|
|
|
|
192
|
|
|
/** @var array Maps extension paths to info arrays */ |
|
193
|
|
|
private static $extensionInfo = null; |
|
194
|
|
|
|
|
195
|
|
|
/** @var ApiMain */ |
|
196
|
|
|
private $mMainModule; |
|
197
|
|
|
/** @var string */ |
|
198
|
|
|
private $mModuleName, $mModulePrefix; |
|
|
|
|
|
|
199
|
|
|
private $mSlaveDB = null; |
|
200
|
|
|
private $mParamCache = []; |
|
201
|
|
|
/** @var array|null|bool */ |
|
202
|
|
|
private $mModuleSource = false; |
|
203
|
|
|
|
|
204
|
|
|
/** |
|
205
|
|
|
* @param ApiMain $mainModule |
|
206
|
|
|
* @param string $moduleName Name of this module |
|
207
|
|
|
* @param string $modulePrefix Prefix to use for parameter names |
|
208
|
|
|
*/ |
|
209
|
|
|
public function __construct( ApiMain $mainModule, $moduleName, $modulePrefix = '' ) { |
|
210
|
|
|
$this->mMainModule = $mainModule; |
|
211
|
|
|
$this->mModuleName = $moduleName; |
|
212
|
|
|
$this->mModulePrefix = $modulePrefix; |
|
213
|
|
|
|
|
214
|
|
|
if ( !$this->isMain() ) { |
|
215
|
|
|
$this->setContext( $mainModule->getContext() ); |
|
216
|
|
|
} |
|
217
|
|
|
} |
|
218
|
|
|
|
|
219
|
|
|
/************************************************************************//** |
|
220
|
|
|
* @name Methods to implement |
|
221
|
|
|
* @{ |
|
222
|
|
|
*/ |
|
223
|
|
|
|
|
224
|
|
|
/** |
|
225
|
|
|
* Evaluates the parameters, performs the requested query, and sets up |
|
226
|
|
|
* the result. Concrete implementations of ApiBase must override this |
|
227
|
|
|
* method to provide whatever functionality their module offers. |
|
228
|
|
|
* Implementations must not produce any output on their own and are not |
|
229
|
|
|
* expected to handle any errors. |
|
230
|
|
|
* |
|
231
|
|
|
* The execute() method will be invoked directly by ApiMain immediately |
|
232
|
|
|
* before the result of the module is output. Aside from the |
|
233
|
|
|
* constructor, implementations should assume that no other methods |
|
234
|
|
|
* will be called externally on the module before the result is |
|
235
|
|
|
* processed. |
|
236
|
|
|
* |
|
237
|
|
|
* The result data should be stored in the ApiResult object available |
|
238
|
|
|
* through getResult(). |
|
239
|
|
|
*/ |
|
240
|
|
|
abstract public function execute(); |
|
241
|
|
|
|
|
242
|
|
|
/** |
|
243
|
|
|
* Get the module manager, or null if this module has no sub-modules |
|
244
|
|
|
* @since 1.21 |
|
245
|
|
|
* @return ApiModuleManager |
|
246
|
|
|
*/ |
|
247
|
|
|
public function getModuleManager() { |
|
248
|
|
|
return null; |
|
249
|
|
|
} |
|
250
|
|
|
|
|
251
|
|
|
/** |
|
252
|
|
|
* If the module may only be used with a certain format module, |
|
253
|
|
|
* it should override this method to return an instance of that formatter. |
|
254
|
|
|
* A value of null means the default format will be used. |
|
255
|
|
|
* @note Do not use this just because you don't want to support non-json |
|
256
|
|
|
* formats. This should be used only when there is a fundamental |
|
257
|
|
|
* requirement for a specific format. |
|
258
|
|
|
* @return mixed Instance of a derived class of ApiFormatBase, or null |
|
259
|
|
|
*/ |
|
260
|
|
|
public function getCustomPrinter() { |
|
261
|
|
|
return null; |
|
262
|
|
|
} |
|
263
|
|
|
|
|
264
|
|
|
/** |
|
265
|
|
|
* Returns usage examples for this module. |
|
266
|
|
|
* |
|
267
|
|
|
* Return value has query strings as keys, with values being either strings |
|
268
|
|
|
* (message key), arrays (message key + parameter), or Message objects. |
|
269
|
|
|
* |
|
270
|
|
|
* Do not call this base class implementation when overriding this method. |
|
271
|
|
|
* |
|
272
|
|
|
* @since 1.25 |
|
273
|
|
|
* @return array |
|
274
|
|
|
*/ |
|
275
|
|
|
protected function getExamplesMessages() { |
|
276
|
|
|
// Fall back to old non-localised method |
|
277
|
|
|
$ret = []; |
|
278
|
|
|
|
|
279
|
|
|
$examples = $this->getExamples(); |
|
280
|
|
|
if ( $examples ) { |
|
281
|
|
|
if ( !is_array( $examples ) ) { |
|
282
|
|
|
$examples = [ $examples ]; |
|
283
|
|
|
} elseif ( $examples && ( count( $examples ) & 1 ) == 0 && |
|
284
|
|
|
array_keys( $examples ) === range( 0, count( $examples ) - 1 ) && |
|
285
|
|
|
!preg_match( '/^\s*api\.php\?/', $examples[0] ) |
|
286
|
|
|
) { |
|
287
|
|
|
// Fix up the ugly "even numbered elements are description, odd |
|
288
|
|
|
// numbered elemts are the link" format (see doc for self::getExamples) |
|
289
|
|
|
$tmp = []; |
|
290
|
|
|
$examplesCount = count( $examples ); |
|
291
|
|
|
for ( $i = 0; $i < $examplesCount; $i += 2 ) { |
|
292
|
|
|
$tmp[$examples[$i + 1]] = $examples[$i]; |
|
293
|
|
|
} |
|
294
|
|
|
$examples = $tmp; |
|
295
|
|
|
} |
|
296
|
|
|
|
|
297
|
|
|
foreach ( $examples as $k => $v ) { |
|
298
|
|
|
if ( is_numeric( $k ) ) { |
|
299
|
|
|
$qs = $v; |
|
300
|
|
|
$msg = ''; |
|
301
|
|
|
} else { |
|
302
|
|
|
$qs = $k; |
|
303
|
|
|
$msg = self::escapeWikiText( $v ); |
|
304
|
|
|
if ( is_array( $msg ) ) { |
|
305
|
|
|
$msg = implode( ' ', $msg ); |
|
306
|
|
|
} |
|
307
|
|
|
} |
|
308
|
|
|
|
|
309
|
|
|
$qs = preg_replace( '/^\s*api\.php\?/', '', $qs ); |
|
310
|
|
|
$ret[$qs] = $this->msg( 'api-help-fallback-example', [ $msg ] ); |
|
311
|
|
|
} |
|
312
|
|
|
} |
|
313
|
|
|
|
|
314
|
|
|
return $ret; |
|
315
|
|
|
} |
|
316
|
|
|
|
|
317
|
|
|
/** |
|
318
|
|
|
* Return links to more detailed help pages about the module. |
|
319
|
|
|
* @since 1.25, returning boolean false is deprecated |
|
320
|
|
|
* @return string|array |
|
321
|
|
|
*/ |
|
322
|
|
|
public function getHelpUrls() { |
|
323
|
|
|
return []; |
|
324
|
|
|
} |
|
325
|
|
|
|
|
326
|
|
|
/** |
|
327
|
|
|
* Returns an array of allowed parameters (parameter name) => (default |
|
328
|
|
|
* value) or (parameter name) => (array with PARAM_* constants as keys) |
|
329
|
|
|
* Don't call this function directly: use getFinalParams() to allow |
|
330
|
|
|
* hooks to modify parameters as needed. |
|
331
|
|
|
* |
|
332
|
|
|
* Some derived classes may choose to handle an integer $flags parameter |
|
333
|
|
|
* in the overriding methods. Callers of this method can pass zero or |
|
334
|
|
|
* more OR-ed flags like GET_VALUES_FOR_HELP. |
|
335
|
|
|
* |
|
336
|
|
|
* @return array |
|
337
|
|
|
*/ |
|
338
|
|
|
protected function getAllowedParams( /* $flags = 0 */ ) { |
|
339
|
|
|
// int $flags is not declared because it causes "Strict standards" |
|
340
|
|
|
// warning. Most derived classes do not implement it. |
|
341
|
|
|
return []; |
|
342
|
|
|
} |
|
343
|
|
|
|
|
344
|
|
|
/** |
|
345
|
|
|
* Indicates if this module needs maxlag to be checked |
|
346
|
|
|
* @return bool |
|
347
|
|
|
*/ |
|
348
|
|
|
public function shouldCheckMaxlag() { |
|
349
|
|
|
return true; |
|
350
|
|
|
} |
|
351
|
|
|
|
|
352
|
|
|
/** |
|
353
|
|
|
* Indicates whether this module requires read rights |
|
354
|
|
|
* @return bool |
|
355
|
|
|
*/ |
|
356
|
|
|
public function isReadMode() { |
|
357
|
|
|
return true; |
|
358
|
|
|
} |
|
359
|
|
|
|
|
360
|
|
|
/** |
|
361
|
|
|
* Indicates whether this module requires write mode |
|
362
|
|
|
* @return bool |
|
363
|
|
|
*/ |
|
364
|
|
|
public function isWriteMode() { |
|
365
|
|
|
return false; |
|
366
|
|
|
} |
|
367
|
|
|
|
|
368
|
|
|
/** |
|
369
|
|
|
* Indicates whether this module must be called with a POST request |
|
370
|
|
|
* @return bool |
|
371
|
|
|
*/ |
|
372
|
|
|
public function mustBePosted() { |
|
373
|
|
|
return $this->needsToken() !== false; |
|
374
|
|
|
} |
|
375
|
|
|
|
|
376
|
|
|
/** |
|
377
|
|
|
* Indicates whether this module is deprecated |
|
378
|
|
|
* @since 1.25 |
|
379
|
|
|
* @return bool |
|
380
|
|
|
*/ |
|
381
|
|
|
public function isDeprecated() { |
|
382
|
|
|
return false; |
|
383
|
|
|
} |
|
384
|
|
|
|
|
385
|
|
|
/** |
|
386
|
|
|
* Indicates whether this module is "internal" |
|
387
|
|
|
* Internal API modules are not (yet) intended for 3rd party use and may be unstable. |
|
388
|
|
|
* @since 1.25 |
|
389
|
|
|
* @return bool |
|
390
|
|
|
*/ |
|
391
|
|
|
public function isInternal() { |
|
392
|
|
|
return false; |
|
393
|
|
|
} |
|
394
|
|
|
|
|
395
|
|
|
/** |
|
396
|
|
|
* Returns the token type this module requires in order to execute. |
|
397
|
|
|
* |
|
398
|
|
|
* Modules are strongly encouraged to use the core 'csrf' type unless they |
|
399
|
|
|
* have specialized security needs. If the token type is not one of the |
|
400
|
|
|
* core types, you must use the ApiQueryTokensRegisterTypes hook to |
|
401
|
|
|
* register it. |
|
402
|
|
|
* |
|
403
|
|
|
* Returning a non-falsey value here will force the addition of an |
|
404
|
|
|
* appropriate 'token' parameter in self::getFinalParams(). Also, |
|
405
|
|
|
* self::mustBePosted() must return true when tokens are used. |
|
406
|
|
|
* |
|
407
|
|
|
* In previous versions of MediaWiki, true was a valid return value. |
|
408
|
|
|
* Returning true will generate errors indicating that the API module needs |
|
409
|
|
|
* updating. |
|
410
|
|
|
* |
|
411
|
|
|
* @return string|false |
|
412
|
|
|
*/ |
|
413
|
|
|
public function needsToken() { |
|
414
|
|
|
return false; |
|
415
|
|
|
} |
|
416
|
|
|
|
|
417
|
|
|
/** |
|
418
|
|
|
* Fetch the salt used in the Web UI corresponding to this module. |
|
419
|
|
|
* |
|
420
|
|
|
* Only override this if the Web UI uses a token with a non-constant salt. |
|
421
|
|
|
* |
|
422
|
|
|
* @since 1.24 |
|
423
|
|
|
* @param array $params All supplied parameters for the module |
|
424
|
|
|
* @return string|array|null |
|
425
|
|
|
*/ |
|
426
|
|
|
protected function getWebUITokenSalt( array $params ) { |
|
427
|
|
|
return null; |
|
428
|
|
|
} |
|
429
|
|
|
|
|
430
|
|
|
/** |
|
431
|
|
|
* Returns data for HTTP conditional request mechanisms. |
|
432
|
|
|
* |
|
433
|
|
|
* @since 1.26 |
|
434
|
|
|
* @param string $condition Condition being queried: |
|
435
|
|
|
* - last-modified: Return a timestamp representing the maximum of the |
|
436
|
|
|
* last-modified dates for all resources involved in the request. See |
|
437
|
|
|
* RFC 7232 § 2.2 for semantics. |
|
438
|
|
|
* - etag: Return an entity-tag representing the state of all resources involved |
|
439
|
|
|
* in the request. Quotes must be included. See RFC 7232 § 2.3 for semantics. |
|
440
|
|
|
* @return string|bool|null As described above, or null if no value is available. |
|
441
|
|
|
*/ |
|
442
|
|
|
public function getConditionalRequestData( $condition ) { |
|
443
|
|
|
return null; |
|
444
|
|
|
} |
|
445
|
|
|
|
|
446
|
|
|
/**@}*/ |
|
447
|
|
|
|
|
448
|
|
|
/************************************************************************//** |
|
449
|
|
|
* @name Data access methods |
|
450
|
|
|
* @{ |
|
451
|
|
|
*/ |
|
452
|
|
|
|
|
453
|
|
|
/** |
|
454
|
|
|
* Get the name of the module being executed by this instance |
|
455
|
|
|
* @return string |
|
456
|
|
|
*/ |
|
457
|
|
|
public function getModuleName() { |
|
458
|
|
|
return $this->mModuleName; |
|
459
|
|
|
} |
|
460
|
|
|
|
|
461
|
|
|
/** |
|
462
|
|
|
* Get parameter prefix (usually two letters or an empty string). |
|
463
|
|
|
* @return string |
|
464
|
|
|
*/ |
|
465
|
|
|
public function getModulePrefix() { |
|
466
|
|
|
return $this->mModulePrefix; |
|
467
|
|
|
} |
|
468
|
|
|
|
|
469
|
|
|
/** |
|
470
|
|
|
* Get the main module |
|
471
|
|
|
* @return ApiMain |
|
472
|
|
|
*/ |
|
473
|
|
|
public function getMain() { |
|
474
|
|
|
return $this->mMainModule; |
|
475
|
|
|
} |
|
476
|
|
|
|
|
477
|
|
|
/** |
|
478
|
|
|
* Returns true if this module is the main module ($this === $this->mMainModule), |
|
479
|
|
|
* false otherwise. |
|
480
|
|
|
* @return bool |
|
481
|
|
|
*/ |
|
482
|
|
|
public function isMain() { |
|
483
|
|
|
return $this === $this->mMainModule; |
|
484
|
|
|
} |
|
485
|
|
|
|
|
486
|
|
|
/** |
|
487
|
|
|
* Get the parent of this module |
|
488
|
|
|
* @since 1.25 |
|
489
|
|
|
* @return ApiBase|null |
|
490
|
|
|
*/ |
|
491
|
|
|
public function getParent() { |
|
492
|
|
|
return $this->isMain() ? null : $this->getMain(); |
|
493
|
|
|
} |
|
494
|
|
|
|
|
495
|
|
|
/** |
|
496
|
|
|
* Returns true if the current request breaks the same-origin policy. |
|
497
|
|
|
* |
|
498
|
|
|
* For example, json with callbacks. |
|
499
|
|
|
* |
|
500
|
|
|
* https://en.wikipedia.org/wiki/Same-origin_policy |
|
501
|
|
|
* |
|
502
|
|
|
* @since 1.25 |
|
503
|
|
|
* @return bool |
|
504
|
|
|
*/ |
|
505
|
|
|
public function lacksSameOriginSecurity() { |
|
506
|
|
|
// Main module has this method overridden |
|
507
|
|
|
// Safety - avoid infinite loop: |
|
508
|
|
|
if ( $this->isMain() ) { |
|
509
|
|
|
ApiBase::dieDebug( __METHOD__, 'base method was called on main module.' ); |
|
510
|
|
|
} |
|
511
|
|
|
|
|
512
|
|
|
return $this->getMain()->lacksSameOriginSecurity(); |
|
513
|
|
|
} |
|
514
|
|
|
|
|
515
|
|
|
/** |
|
516
|
|
|
* Get the path to this module |
|
517
|
|
|
* |
|
518
|
|
|
* @since 1.25 |
|
519
|
|
|
* @return string |
|
520
|
|
|
*/ |
|
521
|
|
|
public function getModulePath() { |
|
522
|
|
|
if ( $this->isMain() ) { |
|
523
|
|
|
return 'main'; |
|
524
|
|
|
} elseif ( $this->getParent()->isMain() ) { |
|
525
|
|
|
return $this->getModuleName(); |
|
526
|
|
|
} else { |
|
527
|
|
|
return $this->getParent()->getModulePath() . '+' . $this->getModuleName(); |
|
528
|
|
|
} |
|
529
|
|
|
} |
|
530
|
|
|
|
|
531
|
|
|
/** |
|
532
|
|
|
* Get a module from its module path |
|
533
|
|
|
* |
|
534
|
|
|
* @since 1.25 |
|
535
|
|
|
* @param string $path |
|
536
|
|
|
* @return ApiBase|null |
|
537
|
|
|
* @throws UsageException |
|
538
|
|
|
*/ |
|
539
|
|
|
public function getModuleFromPath( $path ) { |
|
540
|
|
|
$module = $this->getMain(); |
|
541
|
|
|
if ( $path === 'main' ) { |
|
542
|
|
|
return $module; |
|
543
|
|
|
} |
|
544
|
|
|
|
|
545
|
|
|
$parts = explode( '+', $path ); |
|
546
|
|
|
if ( count( $parts ) === 1 ) { |
|
547
|
|
|
// In case the '+' was typed into URL, it resolves as a space |
|
548
|
|
|
$parts = explode( ' ', $path ); |
|
549
|
|
|
} |
|
550
|
|
|
|
|
551
|
|
|
$count = count( $parts ); |
|
552
|
|
|
for ( $i = 0; $i < $count; $i++ ) { |
|
553
|
|
|
$parent = $module; |
|
554
|
|
|
$manager = $parent->getModuleManager(); |
|
555
|
|
|
if ( $manager === null ) { |
|
556
|
|
|
$errorPath = implode( '+', array_slice( $parts, 0, $i ) ); |
|
557
|
|
|
$this->dieUsage( "The module \"$errorPath\" has no submodules", 'badmodule' ); |
|
558
|
|
|
} |
|
559
|
|
|
$module = $manager->getModule( $parts[$i] ); |
|
560
|
|
|
|
|
561
|
|
|
if ( $module === null ) { |
|
562
|
|
|
$errorPath = $i ? implode( '+', array_slice( $parts, 0, $i ) ) : $parent->getModuleName(); |
|
563
|
|
|
$this->dieUsage( |
|
564
|
|
|
"The module \"$errorPath\" does not have a submodule \"{$parts[$i]}\"", |
|
565
|
|
|
'badmodule' |
|
566
|
|
|
); |
|
567
|
|
|
} |
|
568
|
|
|
} |
|
569
|
|
|
|
|
570
|
|
|
return $module; |
|
571
|
|
|
} |
|
572
|
|
|
|
|
573
|
|
|
/** |
|
574
|
|
|
* Get the result object |
|
575
|
|
|
* @return ApiResult |
|
576
|
|
|
*/ |
|
577
|
|
|
public function getResult() { |
|
578
|
|
|
// Main module has getResult() method overridden |
|
579
|
|
|
// Safety - avoid infinite loop: |
|
580
|
|
|
if ( $this->isMain() ) { |
|
581
|
|
|
ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' ); |
|
582
|
|
|
} |
|
583
|
|
|
|
|
584
|
|
|
return $this->getMain()->getResult(); |
|
585
|
|
|
} |
|
586
|
|
|
|
|
587
|
|
|
/** |
|
588
|
|
|
* Get the error formatter |
|
589
|
|
|
* @return ApiErrorFormatter |
|
590
|
|
|
*/ |
|
591
|
|
|
public function getErrorFormatter() { |
|
592
|
|
|
// Main module has getErrorFormatter() method overridden |
|
593
|
|
|
// Safety - avoid infinite loop: |
|
594
|
|
|
if ( $this->isMain() ) { |
|
595
|
|
|
ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' ); |
|
596
|
|
|
} |
|
597
|
|
|
|
|
598
|
|
|
return $this->getMain()->getErrorFormatter(); |
|
599
|
|
|
} |
|
600
|
|
|
|
|
601
|
|
|
/** |
|
602
|
|
|
* Gets a default slave database connection object |
|
603
|
|
|
* @return DatabaseBase |
|
604
|
|
|
*/ |
|
605
|
|
|
protected function getDB() { |
|
606
|
|
|
if ( !isset( $this->mSlaveDB ) ) { |
|
607
|
|
|
$this->mSlaveDB = wfGetDB( DB_SLAVE, 'api' ); |
|
608
|
|
|
} |
|
609
|
|
|
|
|
610
|
|
|
return $this->mSlaveDB; |
|
611
|
|
|
} |
|
612
|
|
|
|
|
613
|
|
|
/** |
|
614
|
|
|
* Get the continuation manager |
|
615
|
|
|
* @return ApiContinuationManager|null |
|
616
|
|
|
*/ |
|
617
|
|
|
public function getContinuationManager() { |
|
618
|
|
|
// Main module has getContinuationManager() method overridden |
|
619
|
|
|
// Safety - avoid infinite loop: |
|
620
|
|
|
if ( $this->isMain() ) { |
|
621
|
|
|
ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' ); |
|
622
|
|
|
} |
|
623
|
|
|
|
|
624
|
|
|
return $this->getMain()->getContinuationManager(); |
|
625
|
|
|
} |
|
626
|
|
|
|
|
627
|
|
|
/** |
|
628
|
|
|
* Set the continuation manager |
|
629
|
|
|
* @param ApiContinuationManager|null |
|
630
|
|
|
*/ |
|
631
|
|
|
public function setContinuationManager( $manager ) { |
|
632
|
|
|
// Main module has setContinuationManager() method overridden |
|
633
|
|
|
// Safety - avoid infinite loop: |
|
634
|
|
|
if ( $this->isMain() ) { |
|
635
|
|
|
ApiBase::dieDebug( __METHOD__, 'base method was called on main module. ' ); |
|
636
|
|
|
} |
|
637
|
|
|
|
|
638
|
|
|
$this->getMain()->setContinuationManager( $manager ); |
|
639
|
|
|
} |
|
640
|
|
|
|
|
641
|
|
|
/**@}*/ |
|
642
|
|
|
|
|
643
|
|
|
/************************************************************************//** |
|
644
|
|
|
* @name Parameter handling |
|
645
|
|
|
* @{ |
|
646
|
|
|
*/ |
|
647
|
|
|
|
|
648
|
|
|
/** |
|
649
|
|
|
* Indicate if the module supports dynamically-determined parameters that |
|
650
|
|
|
* cannot be included in self::getAllowedParams(). |
|
651
|
|
|
* @return string|array|Message|null Return null if the module does not |
|
652
|
|
|
* support additional dynamic parameters, otherwise return a message |
|
653
|
|
|
* describing them. |
|
654
|
|
|
*/ |
|
655
|
|
|
public function dynamicParameterDocumentation() { |
|
656
|
|
|
return null; |
|
657
|
|
|
} |
|
658
|
|
|
|
|
659
|
|
|
/** |
|
660
|
|
|
* This method mangles parameter name based on the prefix supplied to the constructor. |
|
661
|
|
|
* Override this method to change parameter name during runtime |
|
662
|
|
|
* @param string $paramName Parameter name |
|
663
|
|
|
* @return string Prefixed parameter name |
|
664
|
|
|
*/ |
|
665
|
|
|
public function encodeParamName( $paramName ) { |
|
666
|
|
|
return $this->mModulePrefix . $paramName; |
|
667
|
|
|
} |
|
668
|
|
|
|
|
669
|
|
|
/** |
|
670
|
|
|
* Using getAllowedParams(), this function makes an array of the values |
|
671
|
|
|
* provided by the user, with key being the name of the variable, and |
|
672
|
|
|
* value - validated value from user or default. limits will not be |
|
673
|
|
|
* parsed if $parseLimit is set to false; use this when the max |
|
674
|
|
|
* limit is not definitive yet, e.g. when getting revisions. |
|
675
|
|
|
* @param bool $parseLimit True by default |
|
676
|
|
|
* @return array |
|
677
|
|
|
*/ |
|
678
|
|
|
public function extractRequestParams( $parseLimit = true ) { |
|
679
|
|
|
// Cache parameters, for performance and to avoid bug 24564. |
|
680
|
|
|
if ( !isset( $this->mParamCache[$parseLimit] ) ) { |
|
681
|
|
|
$params = $this->getFinalParams(); |
|
682
|
|
|
$results = []; |
|
683
|
|
|
|
|
684
|
|
|
if ( $params ) { // getFinalParams() can return false |
|
685
|
|
|
foreach ( $params as $paramName => $paramSettings ) { |
|
|
|
|
|
|
686
|
|
|
$results[$paramName] = $this->getParameterFromSettings( |
|
687
|
|
|
$paramName, $paramSettings, $parseLimit ); |
|
688
|
|
|
} |
|
689
|
|
|
} |
|
690
|
|
|
$this->mParamCache[$parseLimit] = $results; |
|
691
|
|
|
} |
|
692
|
|
|
|
|
693
|
|
|
return $this->mParamCache[$parseLimit]; |
|
694
|
|
|
} |
|
695
|
|
|
|
|
696
|
|
|
/** |
|
697
|
|
|
* Get a value for the given parameter |
|
698
|
|
|
* @param string $paramName Parameter name |
|
699
|
|
|
* @param bool $parseLimit See extractRequestParams() |
|
700
|
|
|
* @return mixed Parameter value |
|
701
|
|
|
*/ |
|
702
|
|
|
protected function getParameter( $paramName, $parseLimit = true ) { |
|
703
|
|
|
$paramSettings = $this->getFinalParams()[$paramName]; |
|
704
|
|
|
|
|
705
|
|
|
return $this->getParameterFromSettings( $paramName, $paramSettings, $parseLimit ); |
|
706
|
|
|
} |
|
707
|
|
|
|
|
708
|
|
|
/** |
|
709
|
|
|
* Die if none or more than one of a certain set of parameters is set and not false. |
|
710
|
|
|
* |
|
711
|
|
|
* @param array $params User provided set of parameters, as from $this->extractRequestParams() |
|
712
|
|
|
* @param string $required,... Names of parameters of which exactly one must be set |
|
|
|
|
|
|
713
|
|
|
*/ |
|
714
|
|
|
public function requireOnlyOneParameter( $params, $required /*...*/ ) { |
|
715
|
|
|
$required = func_get_args(); |
|
716
|
|
|
array_shift( $required ); |
|
717
|
|
|
$p = $this->getModulePrefix(); |
|
718
|
|
|
|
|
719
|
|
|
$intersection = array_intersect( array_keys( array_filter( $params, |
|
720
|
|
|
[ $this, 'parameterNotEmpty' ] ) ), $required ); |
|
721
|
|
|
|
|
722
|
|
|
if ( count( $intersection ) > 1 ) { |
|
723
|
|
|
$this->dieUsage( |
|
724
|
|
|
"The parameters {$p}" . implode( ", {$p}", $intersection ) . ' can not be used together', |
|
725
|
|
|
'invalidparammix' ); |
|
726
|
|
|
} elseif ( count( $intersection ) == 0 ) { |
|
727
|
|
|
$this->dieUsage( |
|
728
|
|
|
"One of the parameters {$p}" . implode( ", {$p}", $required ) . ' is required', |
|
729
|
|
|
'missingparam' |
|
730
|
|
|
); |
|
731
|
|
|
} |
|
732
|
|
|
} |
|
733
|
|
|
|
|
734
|
|
|
/** |
|
735
|
|
|
* Die if more than one of a certain set of parameters is set and not false. |
|
736
|
|
|
* |
|
737
|
|
|
* @param array $params User provided set of parameters, as from $this->extractRequestParams() |
|
738
|
|
|
* @param string $required,... Names of parameters of which at most one must be set |
|
|
|
|
|
|
739
|
|
|
*/ |
|
740
|
|
View Code Duplication |
public function requireMaxOneParameter( $params, $required /*...*/ ) { |
|
741
|
|
|
$required = func_get_args(); |
|
742
|
|
|
array_shift( $required ); |
|
743
|
|
|
$p = $this->getModulePrefix(); |
|
744
|
|
|
|
|
745
|
|
|
$intersection = array_intersect( array_keys( array_filter( $params, |
|
746
|
|
|
[ $this, 'parameterNotEmpty' ] ) ), $required ); |
|
747
|
|
|
|
|
748
|
|
|
if ( count( $intersection ) > 1 ) { |
|
749
|
|
|
$this->dieUsage( |
|
750
|
|
|
"The parameters {$p}" . implode( ", {$p}", $intersection ) . ' can not be used together', |
|
751
|
|
|
'invalidparammix' |
|
752
|
|
|
); |
|
753
|
|
|
} |
|
754
|
|
|
} |
|
755
|
|
|
|
|
756
|
|
|
/** |
|
757
|
|
|
* Die if none of a certain set of parameters is set and not false. |
|
758
|
|
|
* |
|
759
|
|
|
* @since 1.23 |
|
760
|
|
|
* @param array $params User provided set of parameters, as from $this->extractRequestParams() |
|
761
|
|
|
* @param string $required,... Names of parameters of which at least one must be set |
|
|
|
|
|
|
762
|
|
|
*/ |
|
763
|
|
View Code Duplication |
public function requireAtLeastOneParameter( $params, $required /*...*/ ) { |
|
764
|
|
|
$required = func_get_args(); |
|
765
|
|
|
array_shift( $required ); |
|
766
|
|
|
$p = $this->getModulePrefix(); |
|
767
|
|
|
|
|
768
|
|
|
$intersection = array_intersect( |
|
769
|
|
|
array_keys( array_filter( $params, [ $this, 'parameterNotEmpty' ] ) ), |
|
770
|
|
|
$required |
|
771
|
|
|
); |
|
772
|
|
|
|
|
773
|
|
|
if ( count( $intersection ) == 0 ) { |
|
774
|
|
|
$this->dieUsage( "At least one of the parameters {$p}" . |
|
775
|
|
|
implode( ", {$p}", $required ) . ' is required', "{$p}missingparam" ); |
|
776
|
|
|
} |
|
777
|
|
|
} |
|
778
|
|
|
|
|
779
|
|
|
/** |
|
780
|
|
|
* Die if any of the specified parameters were found in the query part of |
|
781
|
|
|
* the URL rather than the post body. |
|
782
|
|
|
* @since 1.28 |
|
783
|
|
|
* @param string[] $params Parameters to check |
|
784
|
|
|
* @param string $prefix Set to 'noprefix' to skip calling $this->encodeParamName() |
|
785
|
|
|
*/ |
|
786
|
|
|
public function requirePostedParameters( $params, $prefix = 'prefix' ) { |
|
787
|
|
|
// Skip if $wgDebugAPI is set or we're in internal mode |
|
788
|
|
|
if ( $this->getConfig()->get( 'DebugAPI' ) || $this->getMain()->isInternalMode() ) { |
|
789
|
|
|
return; |
|
790
|
|
|
} |
|
791
|
|
|
|
|
792
|
|
|
$queryValues = $this->getRequest()->getQueryValues(); |
|
793
|
|
|
$badParams = []; |
|
794
|
|
|
foreach ( $params as $param ) { |
|
795
|
|
|
if ( $prefix !== 'noprefix' ) { |
|
796
|
|
|
$param = $this->encodeParamName( $param ); |
|
797
|
|
|
} |
|
798
|
|
|
if ( array_key_exists( $param, $queryValues ) ) { |
|
799
|
|
|
$badParams[] = $param; |
|
800
|
|
|
} |
|
801
|
|
|
} |
|
802
|
|
|
|
|
803
|
|
|
if ( $badParams ) { |
|
804
|
|
|
$this->dieUsage( |
|
805
|
|
|
'The following parameters were found in the query string, but must be in the POST body: ' |
|
806
|
|
|
. join( ', ', $badParams ), |
|
807
|
|
|
'mustpostparams' |
|
808
|
|
|
); |
|
809
|
|
|
} |
|
810
|
|
|
} |
|
811
|
|
|
|
|
812
|
|
|
/** |
|
813
|
|
|
* Callback function used in requireOnlyOneParameter to check whether required parameters are set |
|
814
|
|
|
* |
|
815
|
|
|
* @param object $x Parameter to check is not null/false |
|
816
|
|
|
* @return bool |
|
817
|
|
|
*/ |
|
818
|
|
|
private function parameterNotEmpty( $x ) { |
|
819
|
|
|
return !is_null( $x ) && $x !== false; |
|
820
|
|
|
} |
|
821
|
|
|
|
|
822
|
|
|
/** |
|
823
|
|
|
* Get a WikiPage object from a title or pageid param, if possible. |
|
824
|
|
|
* Can die, if no param is set or if the title or page id is not valid. |
|
825
|
|
|
* |
|
826
|
|
|
* @param array $params |
|
827
|
|
|
* @param bool|string $load Whether load the object's state from the database: |
|
828
|
|
|
* - false: don't load (if the pageid is given, it will still be loaded) |
|
829
|
|
|
* - 'fromdb': load from a slave database |
|
830
|
|
|
* - 'fromdbmaster': load from the master database |
|
831
|
|
|
* @return WikiPage |
|
832
|
|
|
*/ |
|
833
|
|
|
public function getTitleOrPageId( $params, $load = false ) { |
|
834
|
|
|
$this->requireOnlyOneParameter( $params, 'title', 'pageid' ); |
|
835
|
|
|
|
|
836
|
|
|
$pageObj = null; |
|
837
|
|
|
if ( isset( $params['title'] ) ) { |
|
838
|
|
|
$titleObj = Title::newFromText( $params['title'] ); |
|
839
|
|
|
if ( !$titleObj || $titleObj->isExternal() ) { |
|
840
|
|
|
$this->dieUsageMsg( [ 'invalidtitle', $params['title'] ] ); |
|
841
|
|
|
} |
|
842
|
|
|
if ( !$titleObj->canExist() ) { |
|
843
|
|
|
$this->dieUsage( "Namespace doesn't allow actual pages", 'pagecannotexist' ); |
|
844
|
|
|
} |
|
845
|
|
|
$pageObj = WikiPage::factory( $titleObj ); |
|
|
|
|
|
|
846
|
|
|
if ( $load !== false ) { |
|
847
|
|
|
$pageObj->loadPageData( $load ); |
|
|
|
|
|
|
848
|
|
|
} |
|
849
|
|
|
} elseif ( isset( $params['pageid'] ) ) { |
|
850
|
|
|
if ( $load === false ) { |
|
851
|
|
|
$load = 'fromdb'; |
|
852
|
|
|
} |
|
853
|
|
|
$pageObj = WikiPage::newFromID( $params['pageid'], $load ); |
|
|
|
|
|
|
854
|
|
|
if ( !$pageObj ) { |
|
855
|
|
|
$this->dieUsageMsg( [ 'nosuchpageid', $params['pageid'] ] ); |
|
856
|
|
|
} |
|
857
|
|
|
} |
|
858
|
|
|
|
|
859
|
|
|
return $pageObj; |
|
860
|
|
|
} |
|
861
|
|
|
|
|
862
|
|
|
/** |
|
863
|
|
|
* Return true if we're to watch the page, false if not, null if no change. |
|
864
|
|
|
* @param string $watchlist Valid values: 'watch', 'unwatch', 'preferences', 'nochange' |
|
865
|
|
|
* @param Title $titleObj The page under consideration |
|
866
|
|
|
* @param string $userOption The user option to consider when $watchlist=preferences. |
|
867
|
|
|
* If not set will use watchdefault always and watchcreations if $titleObj doesn't exist. |
|
868
|
|
|
* @return bool |
|
869
|
|
|
*/ |
|
870
|
|
|
protected function getWatchlistValue( $watchlist, $titleObj, $userOption = null ) { |
|
871
|
|
|
|
|
872
|
|
|
$userWatching = $this->getUser()->isWatched( $titleObj, User::IGNORE_USER_RIGHTS ); |
|
873
|
|
|
|
|
874
|
|
|
switch ( $watchlist ) { |
|
875
|
|
|
case 'watch': |
|
876
|
|
|
return true; |
|
877
|
|
|
|
|
878
|
|
|
case 'unwatch': |
|
879
|
|
|
return false; |
|
880
|
|
|
|
|
881
|
|
|
case 'preferences': |
|
882
|
|
|
# If the user is already watching, don't bother checking |
|
883
|
|
|
if ( $userWatching ) { |
|
884
|
|
|
return true; |
|
885
|
|
|
} |
|
886
|
|
|
# If no user option was passed, use watchdefault and watchcreations |
|
887
|
|
|
if ( is_null( $userOption ) ) { |
|
888
|
|
|
return $this->getUser()->getBoolOption( 'watchdefault' ) || |
|
889
|
|
|
$this->getUser()->getBoolOption( 'watchcreations' ) && !$titleObj->exists(); |
|
890
|
|
|
} |
|
891
|
|
|
|
|
892
|
|
|
# Watch the article based on the user preference |
|
893
|
|
|
return $this->getUser()->getBoolOption( $userOption ); |
|
894
|
|
|
|
|
895
|
|
|
case 'nochange': |
|
896
|
|
|
return $userWatching; |
|
897
|
|
|
|
|
898
|
|
|
default: |
|
899
|
|
|
return $userWatching; |
|
900
|
|
|
} |
|
901
|
|
|
} |
|
902
|
|
|
|
|
903
|
|
|
/** |
|
904
|
|
|
* Using the settings determine the value for the given parameter |
|
905
|
|
|
* |
|
906
|
|
|
* @param string $paramName Parameter name |
|
907
|
|
|
* @param array|mixed $paramSettings Default value or an array of settings |
|
908
|
|
|
* using PARAM_* constants. |
|
909
|
|
|
* @param bool $parseLimit Parse limit? |
|
910
|
|
|
* @return mixed Parameter value |
|
911
|
|
|
*/ |
|
912
|
|
|
protected function getParameterFromSettings( $paramName, $paramSettings, $parseLimit ) { |
|
913
|
|
|
// Some classes may decide to change parameter names |
|
914
|
|
|
$encParamName = $this->encodeParamName( $paramName ); |
|
915
|
|
|
|
|
916
|
|
|
if ( !is_array( $paramSettings ) ) { |
|
917
|
|
|
$default = $paramSettings; |
|
918
|
|
|
$multi = false; |
|
919
|
|
|
$type = gettype( $paramSettings ); |
|
920
|
|
|
$dupes = false; |
|
921
|
|
|
$deprecated = false; |
|
922
|
|
|
$required = false; |
|
923
|
|
|
} else { |
|
924
|
|
|
$default = isset( $paramSettings[self::PARAM_DFLT] ) |
|
925
|
|
|
? $paramSettings[self::PARAM_DFLT] |
|
926
|
|
|
: null; |
|
927
|
|
|
$multi = isset( $paramSettings[self::PARAM_ISMULTI] ) |
|
928
|
|
|
? $paramSettings[self::PARAM_ISMULTI] |
|
929
|
|
|
: false; |
|
930
|
|
|
$type = isset( $paramSettings[self::PARAM_TYPE] ) |
|
931
|
|
|
? $paramSettings[self::PARAM_TYPE] |
|
932
|
|
|
: null; |
|
933
|
|
|
$dupes = isset( $paramSettings[self::PARAM_ALLOW_DUPLICATES] ) |
|
934
|
|
|
? $paramSettings[self::PARAM_ALLOW_DUPLICATES] |
|
935
|
|
|
: false; |
|
936
|
|
|
$deprecated = isset( $paramSettings[self::PARAM_DEPRECATED] ) |
|
937
|
|
|
? $paramSettings[self::PARAM_DEPRECATED] |
|
938
|
|
|
: false; |
|
939
|
|
|
$required = isset( $paramSettings[self::PARAM_REQUIRED] ) |
|
940
|
|
|
? $paramSettings[self::PARAM_REQUIRED] |
|
941
|
|
|
: false; |
|
942
|
|
|
|
|
943
|
|
|
// When type is not given, and no choices, the type is the same as $default |
|
944
|
|
|
if ( !isset( $type ) ) { |
|
945
|
|
|
if ( isset( $default ) ) { |
|
946
|
|
|
$type = gettype( $default ); |
|
947
|
|
|
} else { |
|
948
|
|
|
$type = 'NULL'; // allow everything |
|
949
|
|
|
} |
|
950
|
|
|
} |
|
951
|
|
|
} |
|
952
|
|
|
|
|
953
|
|
|
if ( $type == 'boolean' ) { |
|
954
|
|
|
if ( isset( $default ) && $default !== false ) { |
|
955
|
|
|
// Having a default value of anything other than 'false' is not allowed |
|
956
|
|
|
ApiBase::dieDebug( |
|
957
|
|
|
__METHOD__, |
|
958
|
|
|
"Boolean param $encParamName's default is set to '$default'. " . |
|
959
|
|
|
'Boolean parameters must default to false.' |
|
960
|
|
|
); |
|
961
|
|
|
} |
|
962
|
|
|
|
|
963
|
|
|
$value = $this->getMain()->getCheck( $encParamName ); |
|
964
|
|
|
} elseif ( $type == 'upload' ) { |
|
965
|
|
|
if ( isset( $default ) ) { |
|
966
|
|
|
// Having a default value is not allowed |
|
967
|
|
|
ApiBase::dieDebug( |
|
968
|
|
|
__METHOD__, |
|
969
|
|
|
"File upload param $encParamName's default is set to " . |
|
970
|
|
|
"'$default'. File upload parameters may not have a default." ); |
|
971
|
|
|
} |
|
972
|
|
|
if ( $multi ) { |
|
973
|
|
|
ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" ); |
|
974
|
|
|
} |
|
975
|
|
|
$value = $this->getMain()->getUpload( $encParamName ); |
|
976
|
|
|
if ( !$value->exists() ) { |
|
977
|
|
|
// This will get the value without trying to normalize it |
|
978
|
|
|
// (because trying to normalize a large binary file |
|
979
|
|
|
// accidentally uploaded as a field fails spectacularly) |
|
980
|
|
|
$value = $this->getMain()->getRequest()->unsetVal( $encParamName ); |
|
981
|
|
|
if ( $value !== null ) { |
|
982
|
|
|
$this->dieUsage( |
|
983
|
|
|
"File upload param $encParamName is not a file upload; " . |
|
984
|
|
|
'be sure to use multipart/form-data for your POST and include ' . |
|
985
|
|
|
'a filename in the Content-Disposition header.', |
|
986
|
|
|
"badupload_{$encParamName}" |
|
987
|
|
|
); |
|
988
|
|
|
} |
|
989
|
|
|
} |
|
990
|
|
|
} else { |
|
991
|
|
|
$value = $this->getMain()->getVal( $encParamName, $default ); |
|
992
|
|
|
|
|
993
|
|
|
if ( isset( $value ) && $type == 'namespace' ) { |
|
994
|
|
|
$type = MWNamespace::getValidNamespaces(); |
|
995
|
|
|
} |
|
996
|
|
View Code Duplication |
if ( isset( $value ) && $type == 'submodule' ) { |
|
997
|
|
|
if ( isset( $paramSettings[self::PARAM_SUBMODULE_MAP] ) ) { |
|
998
|
|
|
$type = array_keys( $paramSettings[self::PARAM_SUBMODULE_MAP] ); |
|
999
|
|
|
} else { |
|
1000
|
|
|
$type = $this->getModuleManager()->getNames( $paramName ); |
|
1001
|
|
|
} |
|
1002
|
|
|
} |
|
1003
|
|
|
|
|
1004
|
|
|
$request = $this->getMain()->getRequest(); |
|
1005
|
|
|
$rawValue = $request->getRawVal( $encParamName ); |
|
1006
|
|
|
if ( $rawValue === null ) { |
|
1007
|
|
|
$rawValue = $default; |
|
1008
|
|
|
} |
|
1009
|
|
|
|
|
1010
|
|
|
// Preserve U+001F for self::parseMultiValue(), or error out if that won't be called |
|
1011
|
|
|
if ( isset( $value ) && substr( $rawValue, 0, 1 ) === "\x1f" ) { |
|
1012
|
|
|
if ( $multi ) { |
|
1013
|
|
|
// This loses the potential $wgContLang->checkTitleEncoding() transformation |
|
1014
|
|
|
// done by WebRequest for $_GET. Let's call that a feature. |
|
1015
|
|
|
$value = join( "\x1f", $request->normalizeUnicode( explode( "\x1f", $rawValue ) ) ); |
|
1016
|
|
|
} else { |
|
1017
|
|
|
$this->dieUsage( |
|
1018
|
|
|
"U+001F multi-value separation may only be used for multi-valued parameters.", |
|
1019
|
|
|
'badvalue_notmultivalue' |
|
1020
|
|
|
); |
|
1021
|
|
|
} |
|
1022
|
|
|
} |
|
1023
|
|
|
|
|
1024
|
|
|
// Check for NFC normalization, and warn |
|
1025
|
|
|
if ( $rawValue !== $value ) { |
|
1026
|
|
|
$this->handleParamNormalization( $paramName, $value, $rawValue ); |
|
1027
|
|
|
} |
|
1028
|
|
|
} |
|
1029
|
|
|
|
|
1030
|
|
|
if ( isset( $value ) && ( $multi || is_array( $type ) ) ) { |
|
1031
|
|
|
$value = $this->parseMultiValue( |
|
1032
|
|
|
$encParamName, |
|
1033
|
|
|
$value, |
|
1034
|
|
|
$multi, |
|
1035
|
|
|
is_array( $type ) ? $type : null |
|
|
|
|
|
|
1036
|
|
|
); |
|
1037
|
|
|
} |
|
1038
|
|
|
|
|
1039
|
|
|
// More validation only when choices were not given |
|
1040
|
|
|
// choices were validated in parseMultiValue() |
|
1041
|
|
|
if ( isset( $value ) ) { |
|
1042
|
|
|
if ( !is_array( $type ) ) { |
|
1043
|
|
|
switch ( $type ) { |
|
1044
|
|
|
case 'NULL': // nothing to do |
|
1045
|
|
|
break; |
|
1046
|
|
|
case 'string': |
|
1047
|
|
|
case 'text': |
|
1048
|
|
|
case 'password': |
|
1049
|
|
|
if ( $required && $value === '' ) { |
|
1050
|
|
|
$this->dieUsageMsg( [ 'missingparam', $paramName ] ); |
|
1051
|
|
|
} |
|
1052
|
|
|
break; |
|
1053
|
|
|
case 'integer': // Force everything using intval() and optionally validate limits |
|
1054
|
|
|
$min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : null; |
|
1055
|
|
|
$max = isset( $paramSettings[self::PARAM_MAX] ) ? $paramSettings[self::PARAM_MAX] : null; |
|
1056
|
|
|
$enforceLimits = isset( $paramSettings[self::PARAM_RANGE_ENFORCE] ) |
|
1057
|
|
|
? $paramSettings[self::PARAM_RANGE_ENFORCE] : false; |
|
1058
|
|
|
|
|
1059
|
|
|
if ( is_array( $value ) ) { |
|
1060
|
|
|
$value = array_map( 'intval', $value ); |
|
1061
|
|
View Code Duplication |
if ( !is_null( $min ) || !is_null( $max ) ) { |
|
1062
|
|
|
foreach ( $value as &$v ) { |
|
1063
|
|
|
$this->validateLimit( $paramName, $v, $min, $max, null, $enforceLimits ); |
|
1064
|
|
|
} |
|
1065
|
|
|
} |
|
1066
|
|
View Code Duplication |
} else { |
|
1067
|
|
|
$value = intval( $value ); |
|
1068
|
|
|
if ( !is_null( $min ) || !is_null( $max ) ) { |
|
1069
|
|
|
$this->validateLimit( $paramName, $value, $min, $max, null, $enforceLimits ); |
|
1070
|
|
|
} |
|
1071
|
|
|
} |
|
1072
|
|
|
break; |
|
1073
|
|
|
case 'limit': |
|
1074
|
|
|
if ( !$parseLimit ) { |
|
1075
|
|
|
// Don't do any validation whatsoever |
|
1076
|
|
|
break; |
|
1077
|
|
|
} |
|
1078
|
|
|
if ( !isset( $paramSettings[self::PARAM_MAX] ) |
|
1079
|
|
|
|| !isset( $paramSettings[self::PARAM_MAX2] ) |
|
1080
|
|
|
) { |
|
1081
|
|
|
ApiBase::dieDebug( |
|
1082
|
|
|
__METHOD__, |
|
1083
|
|
|
"MAX1 or MAX2 are not defined for the limit $encParamName" |
|
1084
|
|
|
); |
|
1085
|
|
|
} |
|
1086
|
|
|
if ( $multi ) { |
|
1087
|
|
|
ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" ); |
|
1088
|
|
|
} |
|
1089
|
|
|
$min = isset( $paramSettings[self::PARAM_MIN] ) ? $paramSettings[self::PARAM_MIN] : 0; |
|
1090
|
|
|
if ( $value == 'max' ) { |
|
1091
|
|
|
$value = $this->getMain()->canApiHighLimits() |
|
1092
|
|
|
? $paramSettings[self::PARAM_MAX2] |
|
1093
|
|
|
: $paramSettings[self::PARAM_MAX]; |
|
1094
|
|
|
$this->getResult()->addParsedLimit( $this->getModuleName(), $value ); |
|
1095
|
|
|
} else { |
|
1096
|
|
|
$value = intval( $value ); |
|
1097
|
|
|
$this->validateLimit( |
|
1098
|
|
|
$paramName, |
|
1099
|
|
|
$value, |
|
1100
|
|
|
$min, |
|
1101
|
|
|
$paramSettings[self::PARAM_MAX], |
|
1102
|
|
|
$paramSettings[self::PARAM_MAX2] |
|
1103
|
|
|
); |
|
1104
|
|
|
} |
|
1105
|
|
|
break; |
|
1106
|
|
|
case 'boolean': |
|
1107
|
|
|
if ( $multi ) { |
|
1108
|
|
|
ApiBase::dieDebug( __METHOD__, "Multi-values not supported for $encParamName" ); |
|
1109
|
|
|
} |
|
1110
|
|
|
break; |
|
1111
|
|
View Code Duplication |
case 'timestamp': |
|
1112
|
|
|
if ( is_array( $value ) ) { |
|
1113
|
|
|
foreach ( $value as $key => $val ) { |
|
1114
|
|
|
$value[$key] = $this->validateTimestamp( $val, $encParamName ); |
|
1115
|
|
|
} |
|
1116
|
|
|
} else { |
|
1117
|
|
|
$value = $this->validateTimestamp( $value, $encParamName ); |
|
1118
|
|
|
} |
|
1119
|
|
|
break; |
|
1120
|
|
View Code Duplication |
case 'user': |
|
1121
|
|
|
if ( is_array( $value ) ) { |
|
1122
|
|
|
foreach ( $value as $key => $val ) { |
|
1123
|
|
|
$value[$key] = $this->validateUser( $val, $encParamName ); |
|
1124
|
|
|
} |
|
1125
|
|
|
} else { |
|
1126
|
|
|
$value = $this->validateUser( $value, $encParamName ); |
|
1127
|
|
|
} |
|
1128
|
|
|
break; |
|
1129
|
|
|
case 'upload': // nothing to do |
|
1130
|
|
|
break; |
|
1131
|
|
|
case 'tags': |
|
1132
|
|
|
// If change tagging was requested, check that the tags are valid. |
|
1133
|
|
|
if ( !is_array( $value ) && !$multi ) { |
|
1134
|
|
|
$value = [ $value ]; |
|
1135
|
|
|
} |
|
1136
|
|
|
$tagsStatus = ChangeTags::canAddTagsAccompanyingChange( $value ); |
|
1137
|
|
|
if ( !$tagsStatus->isGood() ) { |
|
1138
|
|
|
$this->dieStatus( $tagsStatus ); |
|
1139
|
|
|
} |
|
1140
|
|
|
break; |
|
1141
|
|
|
default: |
|
1142
|
|
|
ApiBase::dieDebug( __METHOD__, "Param $encParamName's type is unknown - $type" ); |
|
1143
|
|
|
} |
|
1144
|
|
|
} |
|
1145
|
|
|
|
|
1146
|
|
|
// Throw out duplicates if requested |
|
1147
|
|
|
if ( !$dupes && is_array( $value ) ) { |
|
1148
|
|
|
$value = array_unique( $value ); |
|
1149
|
|
|
} |
|
1150
|
|
|
|
|
1151
|
|
|
// Set a warning if a deprecated parameter has been passed |
|
1152
|
|
|
if ( $deprecated && $value !== false ) { |
|
1153
|
|
|
$this->setWarning( "The $encParamName parameter has been deprecated." ); |
|
1154
|
|
|
|
|
1155
|
|
|
$feature = $encParamName; |
|
1156
|
|
|
$m = $this; |
|
1157
|
|
|
while ( !$m->isMain() ) { |
|
1158
|
|
|
$p = $m->getParent(); |
|
1159
|
|
|
$name = $m->getModuleName(); |
|
1160
|
|
|
$param = $p->encodeParamName( $p->getModuleManager()->getModuleGroup( $name ) ); |
|
1161
|
|
|
$feature = "{$param}={$name}&{$feature}"; |
|
1162
|
|
|
$m = $p; |
|
1163
|
|
|
} |
|
1164
|
|
|
$this->logFeatureUsage( $feature ); |
|
1165
|
|
|
} |
|
1166
|
|
|
} elseif ( $required ) { |
|
1167
|
|
|
$this->dieUsageMsg( [ 'missingparam', $paramName ] ); |
|
1168
|
|
|
} |
|
1169
|
|
|
|
|
1170
|
|
|
return $value; |
|
1171
|
|
|
} |
|
1172
|
|
|
|
|
1173
|
|
|
/** |
|
1174
|
|
|
* Handle when a parameter was Unicode-normalized |
|
1175
|
|
|
* @since 1.28 |
|
1176
|
|
|
* @param string $paramName Unprefixed parameter name |
|
1177
|
|
|
* @param string $value Input that will be used. |
|
1178
|
|
|
* @param string $rawValue Input before normalization. |
|
1179
|
|
|
*/ |
|
1180
|
|
|
protected function handleParamNormalization( $paramName, $value, $rawValue ) { |
|
1181
|
|
|
$encParamName = $this->encodeParamName( $paramName ); |
|
1182
|
|
|
$this->setWarning( |
|
1183
|
|
|
"The value passed for '$encParamName' contains invalid or non-normalized data. " |
|
1184
|
|
|
. 'Textual data should be valid, NFC-normalized Unicode without ' |
|
1185
|
|
|
. 'C0 control characters other than HT (\\t), LF (\\n), and CR (\\r).' |
|
1186
|
|
|
); |
|
1187
|
|
|
} |
|
1188
|
|
|
|
|
1189
|
|
|
/** |
|
1190
|
|
|
* Split a multi-valued parameter string, like explode() |
|
1191
|
|
|
* @since 1.28 |
|
1192
|
|
|
* @param string $value |
|
1193
|
|
|
* @param int $limit |
|
1194
|
|
|
* @return string[] |
|
1195
|
|
|
*/ |
|
1196
|
|
|
protected function explodeMultiValue( $value, $limit ) { |
|
1197
|
|
|
if ( substr( $value, 0, 1 ) === "\x1f" ) { |
|
1198
|
|
|
$sep = "\x1f"; |
|
1199
|
|
|
$value = substr( $value, 1 ); |
|
1200
|
|
|
} else { |
|
1201
|
|
|
$sep = '|'; |
|
1202
|
|
|
} |
|
1203
|
|
|
|
|
1204
|
|
|
return explode( $sep, $value, $limit ); |
|
1205
|
|
|
} |
|
1206
|
|
|
|
|
1207
|
|
|
/** |
|
1208
|
|
|
* Return an array of values that were given in a 'a|b|c' notation, |
|
1209
|
|
|
* after it optionally validates them against the list allowed values. |
|
1210
|
|
|
* |
|
1211
|
|
|
* @param string $valueName The name of the parameter (for error |
|
1212
|
|
|
* reporting) |
|
1213
|
|
|
* @param mixed $value The value being parsed |
|
1214
|
|
|
* @param bool $allowMultiple Can $value contain more than one value |
|
1215
|
|
|
* separated by '|'? |
|
1216
|
|
|
* @param string[]|null $allowedValues An array of values to check against. If |
|
1217
|
|
|
* null, all values are accepted. |
|
1218
|
|
|
* @return string|string[] (allowMultiple ? an_array_of_values : a_single_value) |
|
1219
|
|
|
*/ |
|
1220
|
|
|
protected function parseMultiValue( $valueName, $value, $allowMultiple, $allowedValues ) { |
|
1221
|
|
|
if ( ( trim( $value ) === '' || trim( $value ) === "\x1f" ) && $allowMultiple ) { |
|
1222
|
|
|
return []; |
|
1223
|
|
|
} |
|
1224
|
|
|
|
|
1225
|
|
|
// This is a bit awkward, but we want to avoid calling canApiHighLimits() |
|
1226
|
|
|
// because it unstubs $wgUser |
|
1227
|
|
|
$valuesList = $this->explodeMultiValue( $value, self::LIMIT_SML2 + 1 ); |
|
1228
|
|
|
$sizeLimit = count( $valuesList ) > self::LIMIT_SML1 && $this->mMainModule->canApiHighLimits() |
|
1229
|
|
|
? self::LIMIT_SML2 |
|
1230
|
|
|
: self::LIMIT_SML1; |
|
1231
|
|
|
|
|
1232
|
|
|
if ( self::truncateArray( $valuesList, $sizeLimit ) ) { |
|
1233
|
|
|
$this->logFeatureUsage( "too-many-$valueName-for-{$this->getModulePath()}" ); |
|
1234
|
|
|
$this->setWarning( "Too many values supplied for parameter '$valueName': " . |
|
1235
|
|
|
"the limit is $sizeLimit" ); |
|
1236
|
|
|
} |
|
1237
|
|
|
|
|
1238
|
|
|
if ( !$allowMultiple && count( $valuesList ) != 1 ) { |
|
1239
|
|
|
// Bug 33482 - Allow entries with | in them for non-multiple values |
|
1240
|
|
|
if ( in_array( $value, $allowedValues, true ) ) { |
|
1241
|
|
|
return $value; |
|
1242
|
|
|
} |
|
1243
|
|
|
|
|
1244
|
|
|
$possibleValues = is_array( $allowedValues ) |
|
1245
|
|
|
? "of '" . implode( "', '", $allowedValues ) . "'" |
|
1246
|
|
|
: ''; |
|
1247
|
|
|
$this->dieUsage( |
|
1248
|
|
|
"Only one $possibleValues is allowed for parameter '$valueName'", |
|
1249
|
|
|
"multival_$valueName" |
|
1250
|
|
|
); |
|
1251
|
|
|
} |
|
1252
|
|
|
|
|
1253
|
|
|
if ( is_array( $allowedValues ) ) { |
|
1254
|
|
|
// Check for unknown values |
|
1255
|
|
|
$unknown = array_diff( $valuesList, $allowedValues ); |
|
1256
|
|
|
if ( count( $unknown ) ) { |
|
1257
|
|
|
if ( $allowMultiple ) { |
|
1258
|
|
|
$s = count( $unknown ) > 1 ? 's' : ''; |
|
1259
|
|
|
$vals = implode( ', ', $unknown ); |
|
1260
|
|
|
$this->setWarning( "Unrecognized value$s for parameter '$valueName': $vals" ); |
|
1261
|
|
|
} else { |
|
1262
|
|
|
$this->dieUsage( |
|
1263
|
|
|
"Unrecognized value for parameter '$valueName': {$valuesList[0]}", |
|
1264
|
|
|
"unknown_$valueName" |
|
1265
|
|
|
); |
|
1266
|
|
|
} |
|
1267
|
|
|
} |
|
1268
|
|
|
// Now throw them out |
|
1269
|
|
|
$valuesList = array_intersect( $valuesList, $allowedValues ); |
|
1270
|
|
|
} |
|
1271
|
|
|
|
|
1272
|
|
|
return $allowMultiple ? $valuesList : $valuesList[0]; |
|
1273
|
|
|
} |
|
1274
|
|
|
|
|
1275
|
|
|
/** |
|
1276
|
|
|
* Validate the value against the minimum and user/bot maximum limits. |
|
1277
|
|
|
* Prints usage info on failure. |
|
1278
|
|
|
* @param string $paramName Parameter name |
|
1279
|
|
|
* @param int $value Parameter value |
|
1280
|
|
|
* @param int|null $min Minimum value |
|
1281
|
|
|
* @param int|null $max Maximum value for users |
|
1282
|
|
|
* @param int $botMax Maximum value for sysops/bots |
|
1283
|
|
|
* @param bool $enforceLimits Whether to enforce (die) if value is outside limits |
|
1284
|
|
|
*/ |
|
1285
|
|
|
protected function validateLimit( $paramName, &$value, $min, $max, $botMax = null, |
|
1286
|
|
|
$enforceLimits = false |
|
1287
|
|
|
) { |
|
1288
|
|
|
if ( !is_null( $min ) && $value < $min ) { |
|
1289
|
|
|
$msg = $this->encodeParamName( $paramName ) . " may not be less than $min (set to $value)"; |
|
1290
|
|
|
$this->warnOrDie( $msg, $enforceLimits ); |
|
1291
|
|
|
$value = $min; |
|
1292
|
|
|
} |
|
1293
|
|
|
|
|
1294
|
|
|
// Minimum is always validated, whereas maximum is checked only if not |
|
1295
|
|
|
// running in internal call mode |
|
1296
|
|
|
if ( $this->getMain()->isInternalMode() ) { |
|
1297
|
|
|
return; |
|
1298
|
|
|
} |
|
1299
|
|
|
|
|
1300
|
|
|
// Optimization: do not check user's bot status unless really needed -- skips db query |
|
1301
|
|
|
// assumes $botMax >= $max |
|
1302
|
|
|
if ( !is_null( $max ) && $value > $max ) { |
|
1303
|
|
|
if ( !is_null( $botMax ) && $this->getMain()->canApiHighLimits() ) { |
|
1304
|
|
View Code Duplication |
if ( $value > $botMax ) { |
|
1305
|
|
|
$msg = $this->encodeParamName( $paramName ) . |
|
1306
|
|
|
" may not be over $botMax (set to $value) for bots or sysops"; |
|
1307
|
|
|
$this->warnOrDie( $msg, $enforceLimits ); |
|
1308
|
|
|
$value = $botMax; |
|
1309
|
|
|
} |
|
1310
|
|
View Code Duplication |
} else { |
|
1311
|
|
|
$msg = $this->encodeParamName( $paramName ) . " may not be over $max (set to $value) for users"; |
|
1312
|
|
|
$this->warnOrDie( $msg, $enforceLimits ); |
|
1313
|
|
|
$value = $max; |
|
1314
|
|
|
} |
|
1315
|
|
|
} |
|
1316
|
|
|
} |
|
1317
|
|
|
|
|
1318
|
|
|
/** |
|
1319
|
|
|
* Validate and normalize of parameters of type 'timestamp' |
|
1320
|
|
|
* @param string $value Parameter value |
|
1321
|
|
|
* @param string $encParamName Parameter name |
|
1322
|
|
|
* @return string Validated and normalized parameter |
|
1323
|
|
|
*/ |
|
1324
|
|
|
protected function validateTimestamp( $value, $encParamName ) { |
|
1325
|
|
|
// Confusing synonyms for the current time accepted by wfTimestamp() |
|
1326
|
|
|
// (wfTimestamp() also accepts various non-strings and the string of 14 |
|
1327
|
|
|
// ASCII NUL bytes, but those can't get here) |
|
1328
|
|
|
if ( !$value ) { |
|
1329
|
|
|
$this->logFeatureUsage( 'unclear-"now"-timestamp' ); |
|
1330
|
|
|
$this->setWarning( |
|
1331
|
|
|
"Passing '$value' for timestamp parameter $encParamName has been deprecated." . |
|
1332
|
|
|
' If for some reason you need to explicitly specify the current time without' . |
|
1333
|
|
|
' calculating it client-side, use "now".' |
|
1334
|
|
|
); |
|
1335
|
|
|
return wfTimestamp( TS_MW ); |
|
1336
|
|
|
} |
|
1337
|
|
|
|
|
1338
|
|
|
// Explicit synonym for the current time |
|
1339
|
|
|
if ( $value === 'now' ) { |
|
1340
|
|
|
return wfTimestamp( TS_MW ); |
|
1341
|
|
|
} |
|
1342
|
|
|
|
|
1343
|
|
|
$unixTimestamp = wfTimestamp( TS_UNIX, $value ); |
|
1344
|
|
|
if ( $unixTimestamp === false ) { |
|
1345
|
|
|
$this->dieUsage( |
|
1346
|
|
|
"Invalid value '$value' for timestamp parameter $encParamName", |
|
1347
|
|
|
"badtimestamp_{$encParamName}" |
|
1348
|
|
|
); |
|
1349
|
|
|
} |
|
1350
|
|
|
|
|
1351
|
|
|
return wfTimestamp( TS_MW, $unixTimestamp ); |
|
1352
|
|
|
} |
|
1353
|
|
|
|
|
1354
|
|
|
/** |
|
1355
|
|
|
* Validate the supplied token. |
|
1356
|
|
|
* |
|
1357
|
|
|
* @since 1.24 |
|
1358
|
|
|
* @param string $token Supplied token |
|
1359
|
|
|
* @param array $params All supplied parameters for the module |
|
1360
|
|
|
* @return bool |
|
1361
|
|
|
* @throws MWException |
|
1362
|
|
|
*/ |
|
1363
|
|
|
final public function validateToken( $token, array $params ) { |
|
1364
|
|
|
$tokenType = $this->needsToken(); |
|
1365
|
|
|
$salts = ApiQueryTokens::getTokenTypeSalts(); |
|
1366
|
|
|
if ( !isset( $salts[$tokenType] ) ) { |
|
1367
|
|
|
throw new MWException( |
|
1368
|
|
|
"Module '{$this->getModuleName()}' tried to use token type '$tokenType' " . |
|
1369
|
|
|
'without registering it' |
|
1370
|
|
|
); |
|
1371
|
|
|
} |
|
1372
|
|
|
|
|
1373
|
|
|
$tokenObj = ApiQueryTokens::getToken( |
|
1374
|
|
|
$this->getUser(), $this->getRequest()->getSession(), $salts[$tokenType] |
|
1375
|
|
|
); |
|
1376
|
|
|
if ( $tokenObj->match( $token ) ) { |
|
1377
|
|
|
return true; |
|
1378
|
|
|
} |
|
1379
|
|
|
|
|
1380
|
|
|
$webUiSalt = $this->getWebUITokenSalt( $params ); |
|
1381
|
|
|
if ( $webUiSalt !== null && $this->getUser()->matchEditToken( |
|
1382
|
|
|
$token, |
|
1383
|
|
|
$webUiSalt, |
|
|
|
|
|
|
1384
|
|
|
$this->getRequest() |
|
1385
|
|
|
) ) { |
|
1386
|
|
|
return true; |
|
1387
|
|
|
} |
|
1388
|
|
|
|
|
1389
|
|
|
return false; |
|
1390
|
|
|
} |
|
1391
|
|
|
|
|
1392
|
|
|
/** |
|
1393
|
|
|
* Validate and normalize of parameters of type 'user' |
|
1394
|
|
|
* @param string $value Parameter value |
|
1395
|
|
|
* @param string $encParamName Parameter name |
|
1396
|
|
|
* @return string Validated and normalized parameter |
|
1397
|
|
|
*/ |
|
1398
|
|
|
private function validateUser( $value, $encParamName ) { |
|
1399
|
|
|
$title = Title::makeTitleSafe( NS_USER, $value ); |
|
1400
|
|
|
if ( $title === null || $title->hasFragment() ) { |
|
1401
|
|
|
$this->dieUsage( |
|
1402
|
|
|
"Invalid value '$value' for user parameter $encParamName", |
|
1403
|
|
|
"baduser_{$encParamName}" |
|
1404
|
|
|
); |
|
1405
|
|
|
} |
|
1406
|
|
|
|
|
1407
|
|
|
return $title->getText(); |
|
1408
|
|
|
} |
|
1409
|
|
|
|
|
1410
|
|
|
/**@}*/ |
|
1411
|
|
|
|
|
1412
|
|
|
/************************************************************************//** |
|
1413
|
|
|
* @name Utility methods |
|
1414
|
|
|
* @{ |
|
1415
|
|
|
*/ |
|
1416
|
|
|
|
|
1417
|
|
|
/** |
|
1418
|
|
|
* Set a watch (or unwatch) based the based on a watchlist parameter. |
|
1419
|
|
|
* @param string $watch Valid values: 'watch', 'unwatch', 'preferences', 'nochange' |
|
1420
|
|
|
* @param Title $titleObj The article's title to change |
|
1421
|
|
|
* @param string $userOption The user option to consider when $watch=preferences |
|
1422
|
|
|
*/ |
|
1423
|
|
|
protected function setWatch( $watch, $titleObj, $userOption = null ) { |
|
1424
|
|
|
$value = $this->getWatchlistValue( $watch, $titleObj, $userOption ); |
|
1425
|
|
|
if ( $value === null ) { |
|
1426
|
|
|
return; |
|
1427
|
|
|
} |
|
1428
|
|
|
|
|
1429
|
|
|
WatchAction::doWatchOrUnwatch( $value, $titleObj, $this->getUser() ); |
|
1430
|
|
|
} |
|
1431
|
|
|
|
|
1432
|
|
|
/** |
|
1433
|
|
|
* Truncate an array to a certain length. |
|
1434
|
|
|
* @param array $arr Array to truncate |
|
1435
|
|
|
* @param int $limit Maximum length |
|
1436
|
|
|
* @return bool True if the array was truncated, false otherwise |
|
1437
|
|
|
*/ |
|
1438
|
|
|
public static function truncateArray( &$arr, $limit ) { |
|
1439
|
|
|
$modified = false; |
|
1440
|
|
|
while ( count( $arr ) > $limit ) { |
|
1441
|
|
|
array_pop( $arr ); |
|
1442
|
|
|
$modified = true; |
|
1443
|
|
|
} |
|
1444
|
|
|
|
|
1445
|
|
|
return $modified; |
|
1446
|
|
|
} |
|
1447
|
|
|
|
|
1448
|
|
|
/** |
|
1449
|
|
|
* Gets the user for whom to get the watchlist |
|
1450
|
|
|
* |
|
1451
|
|
|
* @param array $params |
|
1452
|
|
|
* @return User |
|
1453
|
|
|
*/ |
|
1454
|
|
|
public function getWatchlistUser( $params ) { |
|
1455
|
|
|
if ( !is_null( $params['owner'] ) && !is_null( $params['token'] ) ) { |
|
1456
|
|
|
$user = User::newFromName( $params['owner'], false ); |
|
1457
|
|
|
if ( !( $user && $user->getId() ) ) { |
|
1458
|
|
|
$this->dieUsage( 'Specified user does not exist', 'bad_wlowner' ); |
|
1459
|
|
|
} |
|
1460
|
|
|
$token = $user->getOption( 'watchlisttoken' ); |
|
1461
|
|
|
if ( $token == '' || !hash_equals( $token, $params['token'] ) ) { |
|
1462
|
|
|
$this->dieUsage( |
|
1463
|
|
|
'Incorrect watchlist token provided -- please set a correct token in Special:Preferences', |
|
1464
|
|
|
'bad_wltoken' |
|
1465
|
|
|
); |
|
1466
|
|
|
} |
|
1467
|
|
|
} else { |
|
1468
|
|
|
if ( !$this->getUser()->isLoggedIn() ) { |
|
1469
|
|
|
$this->dieUsage( 'You must be logged-in to have a watchlist', 'notloggedin' ); |
|
1470
|
|
|
} |
|
1471
|
|
|
if ( !$this->getUser()->isAllowed( 'viewmywatchlist' ) ) { |
|
1472
|
|
|
$this->dieUsage( 'You don\'t have permission to view your watchlist', 'permissiondenied' ); |
|
1473
|
|
|
} |
|
1474
|
|
|
$user = $this->getUser(); |
|
1475
|
|
|
} |
|
1476
|
|
|
|
|
1477
|
|
|
return $user; |
|
1478
|
|
|
} |
|
1479
|
|
|
|
|
1480
|
|
|
/** |
|
1481
|
|
|
* A subset of wfEscapeWikiText for BC texts |
|
1482
|
|
|
* |
|
1483
|
|
|
* @since 1.25 |
|
1484
|
|
|
* @param string|array $v |
|
1485
|
|
|
* @return string|array |
|
1486
|
|
|
*/ |
|
1487
|
|
View Code Duplication |
private static function escapeWikiText( $v ) { |
|
1488
|
|
|
if ( is_array( $v ) ) { |
|
1489
|
|
|
return array_map( 'self::escapeWikiText', $v ); |
|
1490
|
|
|
} else { |
|
1491
|
|
|
return strtr( $v, [ |
|
1492
|
|
|
'__' => '__', '{' => '{', '}' => '}', |
|
1493
|
|
|
'[[Category:' => '[[:Category:', |
|
1494
|
|
|
'[[File:' => '[[:File:', '[[Image:' => '[[:Image:', |
|
1495
|
|
|
] ); |
|
1496
|
|
|
} |
|
1497
|
|
|
} |
|
1498
|
|
|
|
|
1499
|
|
|
/** |
|
1500
|
|
|
* Create a Message from a string or array |
|
1501
|
|
|
* |
|
1502
|
|
|
* A string is used as a message key. An array has the message key as the |
|
1503
|
|
|
* first value and message parameters as subsequent values. |
|
1504
|
|
|
* |
|
1505
|
|
|
* @since 1.25 |
|
1506
|
|
|
* @param string|array|Message $msg |
|
1507
|
|
|
* @param IContextSource $context |
|
1508
|
|
|
* @param array $params |
|
1509
|
|
|
* @return Message|null |
|
1510
|
|
|
*/ |
|
1511
|
|
|
public static function makeMessage( $msg, IContextSource $context, array $params = null ) { |
|
1512
|
|
|
if ( is_string( $msg ) ) { |
|
1513
|
|
|
$msg = wfMessage( $msg ); |
|
1514
|
|
|
} elseif ( is_array( $msg ) ) { |
|
1515
|
|
|
$msg = call_user_func_array( 'wfMessage', $msg ); |
|
1516
|
|
|
} |
|
1517
|
|
|
if ( !$msg instanceof Message ) { |
|
1518
|
|
|
return null; |
|
1519
|
|
|
} |
|
1520
|
|
|
|
|
1521
|
|
|
$msg->setContext( $context ); |
|
1522
|
|
|
if ( $params ) { |
|
1523
|
|
|
$msg->params( $params ); |
|
1524
|
|
|
} |
|
1525
|
|
|
|
|
1526
|
|
|
return $msg; |
|
1527
|
|
|
} |
|
1528
|
|
|
|
|
1529
|
|
|
/**@}*/ |
|
1530
|
|
|
|
|
1531
|
|
|
/************************************************************************//** |
|
1532
|
|
|
* @name Warning and error reporting |
|
1533
|
|
|
* @{ |
|
1534
|
|
|
*/ |
|
1535
|
|
|
|
|
1536
|
|
|
/** |
|
1537
|
|
|
* Set warning section for this module. Users should monitor this |
|
1538
|
|
|
* section to notice any changes in API. Multiple calls to this |
|
1539
|
|
|
* function will result in the warning messages being separated by |
|
1540
|
|
|
* newlines |
|
1541
|
|
|
* @param string $warning Warning message |
|
1542
|
|
|
*/ |
|
1543
|
|
|
public function setWarning( $warning ) { |
|
1544
|
|
|
$msg = new ApiRawMessage( $warning, 'warning' ); |
|
1545
|
|
|
$this->getErrorFormatter()->addWarning( $this->getModuleName(), $msg ); |
|
1546
|
|
|
} |
|
1547
|
|
|
|
|
1548
|
|
|
/** |
|
1549
|
|
|
* Adds a warning to the output, else dies |
|
1550
|
|
|
* |
|
1551
|
|
|
* @param string $msg Message to show as a warning, or error message if dying |
|
1552
|
|
|
* @param bool $enforceLimits Whether this is an enforce (die) |
|
1553
|
|
|
*/ |
|
1554
|
|
|
private function warnOrDie( $msg, $enforceLimits = false ) { |
|
1555
|
|
|
if ( $enforceLimits ) { |
|
1556
|
|
|
$this->dieUsage( $msg, 'integeroutofrange' ); |
|
1557
|
|
|
} |
|
1558
|
|
|
|
|
1559
|
|
|
$this->setWarning( $msg ); |
|
1560
|
|
|
} |
|
1561
|
|
|
|
|
1562
|
|
|
/** |
|
1563
|
|
|
* Throw a UsageException, which will (if uncaught) call the main module's |
|
1564
|
|
|
* error handler and die with an error message. |
|
1565
|
|
|
* |
|
1566
|
|
|
* @param string $description One-line human-readable description of the |
|
1567
|
|
|
* error condition, e.g., "The API requires a valid action parameter" |
|
1568
|
|
|
* @param string $errorCode Brief, arbitrary, stable string to allow easy |
|
1569
|
|
|
* automated identification of the error, e.g., 'unknown_action' |
|
1570
|
|
|
* @param int $httpRespCode HTTP response code |
|
1571
|
|
|
* @param array|null $extradata Data to add to the "<error>" element; array in ApiResult format |
|
1572
|
|
|
* @throws UsageException always |
|
1573
|
|
|
*/ |
|
1574
|
|
|
public function dieUsage( $description, $errorCode, $httpRespCode = 0, $extradata = null ) { |
|
1575
|
|
|
throw new UsageException( |
|
1576
|
|
|
$description, |
|
1577
|
|
|
$this->encodeParamName( $errorCode ), |
|
1578
|
|
|
$httpRespCode, |
|
1579
|
|
|
$extradata |
|
1580
|
|
|
); |
|
1581
|
|
|
} |
|
1582
|
|
|
|
|
1583
|
|
|
/** |
|
1584
|
|
|
* Throw a UsageException, which will (if uncaught) call the main module's |
|
1585
|
|
|
* error handler and die with an error message including block info. |
|
1586
|
|
|
* |
|
1587
|
|
|
* @since 1.27 |
|
1588
|
|
|
* @param Block $block The block used to generate the UsageException |
|
1589
|
|
|
* @throws UsageException always |
|
1590
|
|
|
*/ |
|
1591
|
|
|
public function dieBlocked( Block $block ) { |
|
1592
|
|
|
// Die using the appropriate message depending on block type |
|
1593
|
|
|
if ( $block->getType() == Block::TYPE_AUTO ) { |
|
1594
|
|
|
$this->dieUsage( |
|
1595
|
|
|
'Your IP address has been blocked automatically, because it was used by a blocked user', |
|
1596
|
|
|
'autoblocked', |
|
1597
|
|
|
0, |
|
1598
|
|
|
[ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ] |
|
1599
|
|
|
); |
|
1600
|
|
|
} else { |
|
1601
|
|
|
$this->dieUsage( |
|
1602
|
|
|
'You have been blocked from editing', |
|
1603
|
|
|
'blocked', |
|
1604
|
|
|
0, |
|
1605
|
|
|
[ 'blockinfo' => ApiQueryUserInfo::getBlockInfo( $block ) ] |
|
1606
|
|
|
); |
|
1607
|
|
|
} |
|
1608
|
|
|
} |
|
1609
|
|
|
|
|
1610
|
|
|
/** |
|
1611
|
|
|
* Get error (as code, string) from a Status object. |
|
1612
|
|
|
* |
|
1613
|
|
|
* @since 1.23 |
|
1614
|
|
|
* @param Status $status |
|
1615
|
|
|
* @param array|null &$extraData Set if extra data from IApiMessage is available (since 1.27) |
|
1616
|
|
|
* @return array Array of code and error string |
|
1617
|
|
|
* @throws MWException |
|
1618
|
|
|
*/ |
|
1619
|
|
|
public function getErrorFromStatus( $status, &$extraData = null ) { |
|
1620
|
|
|
if ( $status->isGood() ) { |
|
1621
|
|
|
throw new MWException( 'Successful status passed to ApiBase::dieStatus' ); |
|
1622
|
|
|
} |
|
1623
|
|
|
|
|
1624
|
|
|
$errors = $status->getErrorsByType( 'error' ); |
|
1625
|
|
|
if ( !$errors ) { |
|
1626
|
|
|
// No errors? Assume the warnings should be treated as errors |
|
1627
|
|
|
$errors = $status->getErrorsByType( 'warning' ); |
|
1628
|
|
|
} |
|
1629
|
|
|
if ( !$errors ) { |
|
1630
|
|
|
// Still no errors? Punt |
|
1631
|
|
|
$errors = [ [ 'message' => 'unknownerror-nocode', 'params' => [] ] ]; |
|
1632
|
|
|
} |
|
1633
|
|
|
|
|
1634
|
|
|
// Cannot use dieUsageMsg() because extensions might return custom |
|
1635
|
|
|
// error messages. |
|
1636
|
|
|
if ( $errors[0]['message'] instanceof Message ) { |
|
1637
|
|
|
$msg = $errors[0]['message']; |
|
1638
|
|
|
if ( $msg instanceof IApiMessage ) { |
|
1639
|
|
|
$extraData = $msg->getApiData(); |
|
1640
|
|
|
$code = $msg->getApiCode(); |
|
1641
|
|
|
} else { |
|
1642
|
|
|
$code = $msg->getKey(); |
|
1643
|
|
|
} |
|
1644
|
|
|
} else { |
|
1645
|
|
|
$code = $errors[0]['message']; |
|
1646
|
|
|
$msg = wfMessage( $code, $errors[0]['params'] ); |
|
1647
|
|
|
} |
|
1648
|
|
|
if ( isset( ApiBase::$messageMap[$code] ) ) { |
|
1649
|
|
|
// Translate message to code, for backwards compatibility |
|
1650
|
|
|
$code = ApiBase::$messageMap[$code]['code']; |
|
1651
|
|
|
} |
|
1652
|
|
|
|
|
1653
|
|
|
return [ $code, $msg->inLanguage( 'en' )->useDatabase( false )->plain() ]; |
|
1654
|
|
|
} |
|
1655
|
|
|
|
|
1656
|
|
|
/** |
|
1657
|
|
|
* Throw a UsageException based on the errors in the Status object. |
|
1658
|
|
|
* |
|
1659
|
|
|
* @since 1.22 |
|
1660
|
|
|
* @param Status $status |
|
1661
|
|
|
* @throws UsageException always |
|
1662
|
|
|
*/ |
|
1663
|
|
|
public function dieStatus( $status ) { |
|
1664
|
|
|
$extraData = null; |
|
1665
|
|
|
list( $code, $msg ) = $this->getErrorFromStatus( $status, $extraData ); |
|
1666
|
|
|
$this->dieUsage( $msg, $code, 0, $extraData ); |
|
1667
|
|
|
} |
|
1668
|
|
|
|
|
1669
|
|
|
// @codingStandardsIgnoreStart Allow long lines. Cannot split these. |
|
1670
|
|
|
/** |
|
1671
|
|
|
* Array that maps message keys to error messages. $1 and friends are replaced. |
|
1672
|
|
|
*/ |
|
1673
|
|
|
public static $messageMap = [ |
|
1674
|
|
|
// This one MUST be present, or dieUsageMsg() will recurse infinitely |
|
1675
|
|
|
'unknownerror' => [ 'code' => 'unknownerror', 'info' => "Unknown error: \"\$1\"" ], |
|
1676
|
|
|
'unknownerror-nocode' => [ 'code' => 'unknownerror', 'info' => 'Unknown error' ], |
|
1677
|
|
|
|
|
1678
|
|
|
// Messages from Title::getUserPermissionsErrors() |
|
1679
|
|
|
'ns-specialprotected' => [ |
|
1680
|
|
|
'code' => 'unsupportednamespace', |
|
1681
|
|
|
'info' => "Pages in the Special namespace can't be edited" |
|
1682
|
|
|
], |
|
1683
|
|
|
'protectedinterface' => [ |
|
1684
|
|
|
'code' => 'protectednamespace-interface', |
|
1685
|
|
|
'info' => "You're not allowed to edit interface messages" |
|
1686
|
|
|
], |
|
1687
|
|
|
'namespaceprotected' => [ |
|
1688
|
|
|
'code' => 'protectednamespace', |
|
1689
|
|
|
'info' => "You're not allowed to edit pages in the \"\$1\" namespace" |
|
1690
|
|
|
], |
|
1691
|
|
|
'customcssprotected' => [ |
|
1692
|
|
|
'code' => 'customcssprotected', |
|
1693
|
|
|
'info' => "You're not allowed to edit custom CSS pages" |
|
1694
|
|
|
], |
|
1695
|
|
|
'customjsprotected' => [ |
|
1696
|
|
|
'code' => 'customjsprotected', |
|
1697
|
|
|
'info' => "You're not allowed to edit custom JavaScript pages" |
|
1698
|
|
|
], |
|
1699
|
|
|
'cascadeprotected' => [ |
|
1700
|
|
|
'code' => 'cascadeprotected', |
|
1701
|
|
|
'info' => "The page you're trying to edit is protected because it's included in a cascade-protected page" |
|
1702
|
|
|
], |
|
1703
|
|
|
'protectedpagetext' => [ |
|
1704
|
|
|
'code' => 'protectedpage', |
|
1705
|
|
|
'info' => "The \"\$1\" right is required to edit this page" |
|
1706
|
|
|
], |
|
1707
|
|
|
'protect-cantedit' => [ |
|
1708
|
|
|
'code' => 'cantedit', |
|
1709
|
|
|
'info' => "You can't protect this page because you can't edit it" |
|
1710
|
|
|
], |
|
1711
|
|
|
'deleteprotected' => [ |
|
1712
|
|
|
'code' => 'cantedit', |
|
1713
|
|
|
'info' => "You can't delete this page because it has been protected" |
|
1714
|
|
|
], |
|
1715
|
|
|
'badaccess-group0' => [ |
|
1716
|
|
|
'code' => 'permissiondenied', |
|
1717
|
|
|
'info' => 'Permission denied' |
|
1718
|
|
|
], // Generic permission denied message |
|
1719
|
|
|
'badaccess-groups' => [ |
|
1720
|
|
|
'code' => 'permissiondenied', |
|
1721
|
|
|
'info' => 'Permission denied' |
|
1722
|
|
|
], |
|
1723
|
|
|
'titleprotected' => [ |
|
1724
|
|
|
'code' => 'protectedtitle', |
|
1725
|
|
|
'info' => 'This title has been protected from creation' |
|
1726
|
|
|
], |
|
1727
|
|
|
'nocreate-loggedin' => [ |
|
1728
|
|
|
'code' => 'cantcreate', |
|
1729
|
|
|
'info' => "You don't have permission to create new pages" |
|
1730
|
|
|
], |
|
1731
|
|
|
'nocreatetext' => [ |
|
1732
|
|
|
'code' => 'cantcreate-anon', |
|
1733
|
|
|
'info' => "Anonymous users can't create new pages" |
|
1734
|
|
|
], |
|
1735
|
|
|
'movenologintext' => [ |
|
1736
|
|
|
'code' => 'cantmove-anon', |
|
1737
|
|
|
'info' => "Anonymous users can't move pages" |
|
1738
|
|
|
], |
|
1739
|
|
|
'movenotallowed' => [ |
|
1740
|
|
|
'code' => 'cantmove', |
|
1741
|
|
|
'info' => "You don't have permission to move pages" |
|
1742
|
|
|
], |
|
1743
|
|
|
'confirmedittext' => [ |
|
1744
|
|
|
'code' => 'confirmemail', |
|
1745
|
|
|
'info' => 'You must confirm your email address before you can edit' |
|
1746
|
|
|
], |
|
1747
|
|
|
'blockedtext' => [ |
|
1748
|
|
|
'code' => 'blocked', |
|
1749
|
|
|
'info' => 'You have been blocked from editing' |
|
1750
|
|
|
], |
|
1751
|
|
|
'autoblockedtext' => [ |
|
1752
|
|
|
'code' => 'autoblocked', |
|
1753
|
|
|
'info' => 'Your IP address has been blocked automatically, because it was used by a blocked user' |
|
1754
|
|
|
], |
|
1755
|
|
|
|
|
1756
|
|
|
// Miscellaneous interface messages |
|
1757
|
|
|
'actionthrottledtext' => [ |
|
1758
|
|
|
'code' => 'ratelimited', |
|
1759
|
|
|
'info' => "You've exceeded your rate limit. Please wait some time and try again" |
|
1760
|
|
|
], |
|
1761
|
|
|
'alreadyrolled' => [ |
|
1762
|
|
|
'code' => 'alreadyrolled', |
|
1763
|
|
|
'info' => 'The page you tried to rollback was already rolled back' |
|
1764
|
|
|
], |
|
1765
|
|
|
'cantrollback' => [ |
|
1766
|
|
|
'code' => 'onlyauthor', |
|
1767
|
|
|
'info' => 'The page you tried to rollback only has one author' |
|
1768
|
|
|
], |
|
1769
|
|
|
'readonlytext' => [ |
|
1770
|
|
|
'code' => 'readonly', |
|
1771
|
|
|
'info' => 'The wiki is currently in read-only mode' |
|
1772
|
|
|
], |
|
1773
|
|
|
'sessionfailure' => [ |
|
1774
|
|
|
'code' => 'badtoken', |
|
1775
|
|
|
'info' => 'Invalid token' ], |
|
1776
|
|
|
'cannotdelete' => [ |
|
1777
|
|
|
'code' => 'cantdelete', |
|
1778
|
|
|
'info' => "Couldn't delete \"\$1\". Maybe it was deleted already by someone else" |
|
1779
|
|
|
], |
|
1780
|
|
|
'notanarticle' => [ |
|
1781
|
|
|
'code' => 'missingtitle', |
|
1782
|
|
|
'info' => "The page you requested doesn't exist" |
|
1783
|
|
|
], |
|
1784
|
|
|
'selfmove' => [ 'code' => 'selfmove', 'info' => "Can't move a page to itself" |
|
1785
|
|
|
], |
|
1786
|
|
|
'immobile_namespace' => [ |
|
1787
|
|
|
'code' => 'immobilenamespace', |
|
1788
|
|
|
'info' => 'You tried to move pages from or to a namespace that is protected from moving' |
|
1789
|
|
|
], |
|
1790
|
|
|
'articleexists' => [ |
|
1791
|
|
|
'code' => 'articleexists', |
|
1792
|
|
|
'info' => 'The destination article already exists and is not a redirect to the source article' |
|
1793
|
|
|
], |
|
1794
|
|
|
'protectedpage' => [ |
|
1795
|
|
|
'code' => 'protectedpage', |
|
1796
|
|
|
'info' => "You don't have permission to perform this move" |
|
1797
|
|
|
], |
|
1798
|
|
|
'hookaborted' => [ |
|
1799
|
|
|
'code' => 'hookaborted', |
|
1800
|
|
|
'info' => 'The modification you tried to make was aborted by an extension hook' |
|
1801
|
|
|
], |
|
1802
|
|
|
'cantmove-titleprotected' => [ |
|
1803
|
|
|
'code' => 'protectedtitle', |
|
1804
|
|
|
'info' => 'The destination article has been protected from creation' |
|
1805
|
|
|
], |
|
1806
|
|
|
'imagenocrossnamespace' => [ |
|
1807
|
|
|
'code' => 'nonfilenamespace', |
|
1808
|
|
|
'info' => "Can't move a file to a non-file namespace" |
|
1809
|
|
|
], |
|
1810
|
|
|
'imagetypemismatch' => [ |
|
1811
|
|
|
'code' => 'filetypemismatch', |
|
1812
|
|
|
'info' => "The new file extension doesn't match its type" |
|
1813
|
|
|
], |
|
1814
|
|
|
// 'badarticleerror' => shouldn't happen |
|
1815
|
|
|
// 'badtitletext' => shouldn't happen |
|
1816
|
|
|
'ip_range_invalid' => [ 'code' => 'invalidrange', 'info' => 'Invalid IP range' ], |
|
1817
|
|
|
'range_block_disabled' => [ |
|
1818
|
|
|
'code' => 'rangedisabled', |
|
1819
|
|
|
'info' => 'Blocking IP ranges has been disabled' |
|
1820
|
|
|
], |
|
1821
|
|
|
'nosuchusershort' => [ |
|
1822
|
|
|
'code' => 'nosuchuser', |
|
1823
|
|
|
'info' => "The user you specified doesn't exist" |
|
1824
|
|
|
], |
|
1825
|
|
|
'badipaddress' => [ 'code' => 'invalidip', 'info' => 'Invalid IP address specified' ], |
|
1826
|
|
|
'ipb_expiry_invalid' => [ 'code' => 'invalidexpiry', 'info' => 'Invalid expiry time' ], |
|
1827
|
|
|
'ipb_already_blocked' => [ |
|
1828
|
|
|
'code' => 'alreadyblocked', |
|
1829
|
|
|
'info' => 'The user you tried to block was already blocked' |
|
1830
|
|
|
], |
|
1831
|
|
|
'ipb_blocked_as_range' => [ |
|
1832
|
|
|
'code' => 'blockedasrange', |
|
1833
|
|
|
'info' => "IP address \"\$1\" was blocked as part of range \"\$2\". You can't unblock the IP individually, but you can unblock the range as a whole." |
|
1834
|
|
|
], |
|
1835
|
|
|
'ipb_cant_unblock' => [ |
|
1836
|
|
|
'code' => 'cantunblock', |
|
1837
|
|
|
'info' => 'The block you specified was not found. It may have been unblocked already' |
|
1838
|
|
|
], |
|
1839
|
|
|
'mailnologin' => [ |
|
1840
|
|
|
'code' => 'cantsend', |
|
1841
|
|
|
'info' => 'You are not logged in, you do not have a confirmed email address, or you are not allowed to send email to other users, so you cannot send email' |
|
1842
|
|
|
], |
|
1843
|
|
|
'ipbblocked' => [ |
|
1844
|
|
|
'code' => 'ipbblocked', |
|
1845
|
|
|
'info' => 'You cannot block or unblock users while you are yourself blocked' |
|
1846
|
|
|
], |
|
1847
|
|
|
'ipbnounblockself' => [ |
|
1848
|
|
|
'code' => 'ipbnounblockself', |
|
1849
|
|
|
'info' => 'You are not allowed to unblock yourself' |
|
1850
|
|
|
], |
|
1851
|
|
|
'usermaildisabled' => [ |
|
1852
|
|
|
'code' => 'usermaildisabled', |
|
1853
|
|
|
'info' => 'User email has been disabled' |
|
1854
|
|
|
], |
|
1855
|
|
|
'blockedemailuser' => [ |
|
1856
|
|
|
'code' => 'blockedfrommail', |
|
1857
|
|
|
'info' => 'You have been blocked from sending email' |
|
1858
|
|
|
], |
|
1859
|
|
|
'notarget' => [ |
|
1860
|
|
|
'code' => 'notarget', |
|
1861
|
|
|
'info' => 'You have not specified a valid target for this action' |
|
1862
|
|
|
], |
|
1863
|
|
|
'noemail' => [ |
|
1864
|
|
|
'code' => 'noemail', |
|
1865
|
|
|
'info' => 'The user has not specified a valid email address, or has chosen not to receive email from other users' |
|
1866
|
|
|
], |
|
1867
|
|
|
'rcpatroldisabled' => [ |
|
1868
|
|
|
'code' => 'patroldisabled', |
|
1869
|
|
|
'info' => 'Patrolling is disabled on this wiki' |
|
1870
|
|
|
], |
|
1871
|
|
|
'markedaspatrollederror-noautopatrol' => [ |
|
1872
|
|
|
'code' => 'noautopatrol', |
|
1873
|
|
|
'info' => "You don't have permission to patrol your own changes" |
|
1874
|
|
|
], |
|
1875
|
|
|
'delete-toobig' => [ |
|
1876
|
|
|
'code' => 'bigdelete', |
|
1877
|
|
|
'info' => "You can't delete this page because it has more than \$1 revisions" |
|
1878
|
|
|
], |
|
1879
|
|
|
'movenotallowedfile' => [ |
|
1880
|
|
|
'code' => 'cantmovefile', |
|
1881
|
|
|
'info' => "You don't have permission to move files" |
|
1882
|
|
|
], |
|
1883
|
|
|
'userrights-no-interwiki' => [ |
|
1884
|
|
|
'code' => 'nointerwikiuserrights', |
|
1885
|
|
|
'info' => "You don't have permission to change user rights on other wikis" |
|
1886
|
|
|
], |
|
1887
|
|
|
'userrights-nodatabase' => [ |
|
1888
|
|
|
'code' => 'nosuchdatabase', |
|
1889
|
|
|
'info' => "Database \"\$1\" does not exist or is not local" |
|
1890
|
|
|
], |
|
1891
|
|
|
'nouserspecified' => [ 'code' => 'invaliduser', 'info' => "Invalid username \"\$1\"" ], |
|
1892
|
|
|
'noname' => [ 'code' => 'invaliduser', 'info' => "Invalid username \"\$1\"" ], |
|
1893
|
|
|
'summaryrequired' => [ 'code' => 'summaryrequired', 'info' => 'Summary required' ], |
|
1894
|
|
|
'import-rootpage-invalid' => [ |
|
1895
|
|
|
'code' => 'import-rootpage-invalid', |
|
1896
|
|
|
'info' => 'Root page is an invalid title' |
|
1897
|
|
|
], |
|
1898
|
|
|
'import-rootpage-nosubpage' => [ |
|
1899
|
|
|
'code' => 'import-rootpage-nosubpage', |
|
1900
|
|
|
'info' => 'Namespace "$1" of the root page does not allow subpages' |
|
1901
|
|
|
], |
|
1902
|
|
|
|
|
1903
|
|
|
// API-specific messages |
|
1904
|
|
|
'readrequired' => [ |
|
1905
|
|
|
'code' => 'readapidenied', |
|
1906
|
|
|
'info' => 'You need read permission to use this module' |
|
1907
|
|
|
], |
|
1908
|
|
|
'writedisabled' => [ |
|
1909
|
|
|
'code' => 'noapiwrite', |
|
1910
|
|
|
'info' => "Editing of this wiki through the API is disabled. Make sure the \$wgEnableWriteAPI=true; statement is included in the wiki's LocalSettings.php file" |
|
1911
|
|
|
], |
|
1912
|
|
|
'writerequired' => [ |
|
1913
|
|
|
'code' => 'writeapidenied', |
|
1914
|
|
|
'info' => "You're not allowed to edit this wiki through the API" |
|
1915
|
|
|
], |
|
1916
|
|
|
'missingparam' => [ 'code' => 'no$1', 'info' => "The \$1 parameter must be set" ], |
|
1917
|
|
|
'invalidtitle' => [ 'code' => 'invalidtitle', 'info' => "Bad title \"\$1\"" ], |
|
1918
|
|
|
'nosuchpageid' => [ 'code' => 'nosuchpageid', 'info' => "There is no page with ID \$1" ], |
|
1919
|
|
|
'nosuchrevid' => [ 'code' => 'nosuchrevid', 'info' => "There is no revision with ID \$1" ], |
|
1920
|
|
|
'nosuchuser' => [ 'code' => 'nosuchuser', 'info' => "User \"\$1\" doesn't exist" ], |
|
1921
|
|
|
'invaliduser' => [ 'code' => 'invaliduser', 'info' => "Invalid username \"\$1\"" ], |
|
1922
|
|
|
'invalidexpiry' => [ 'code' => 'invalidexpiry', 'info' => "Invalid expiry time \"\$1\"" ], |
|
1923
|
|
|
'pastexpiry' => [ 'code' => 'pastexpiry', 'info' => "Expiry time \"\$1\" is in the past" ], |
|
1924
|
|
|
'create-titleexists' => [ |
|
1925
|
|
|
'code' => 'create-titleexists', |
|
1926
|
|
|
'info' => "Existing titles can't be protected with 'create'" |
|
1927
|
|
|
], |
|
1928
|
|
|
'missingtitle-createonly' => [ |
|
1929
|
|
|
'code' => 'missingtitle-createonly', |
|
1930
|
|
|
'info' => "Missing titles can only be protected with 'create'" |
|
1931
|
|
|
], |
|
1932
|
|
|
'cantblock' => [ 'code' => 'cantblock', |
|
1933
|
|
|
'info' => "You don't have permission to block users" |
|
1934
|
|
|
], |
|
1935
|
|
|
'canthide' => [ |
|
1936
|
|
|
'code' => 'canthide', |
|
1937
|
|
|
'info' => "You don't have permission to hide user names from the block log" |
|
1938
|
|
|
], |
|
1939
|
|
|
'cantblock-email' => [ |
|
1940
|
|
|
'code' => 'cantblock-email', |
|
1941
|
|
|
'info' => "You don't have permission to block users from sending email through the wiki" |
|
1942
|
|
|
], |
|
1943
|
|
|
'unblock-notarget' => [ |
|
1944
|
|
|
'code' => 'notarget', |
|
1945
|
|
|
'info' => 'Either the id or the user parameter must be set' |
|
1946
|
|
|
], |
|
1947
|
|
|
'unblock-idanduser' => [ |
|
1948
|
|
|
'code' => 'idanduser', |
|
1949
|
|
|
'info' => "The id and user parameters can't be used together" |
|
1950
|
|
|
], |
|
1951
|
|
|
'cantunblock' => [ |
|
1952
|
|
|
'code' => 'permissiondenied', |
|
1953
|
|
|
'info' => "You don't have permission to unblock users" |
|
1954
|
|
|
], |
|
1955
|
|
|
'cannotundelete' => [ |
|
1956
|
|
|
'code' => 'cantundelete', |
|
1957
|
|
|
'info' => "Couldn't undelete: the requested revisions may not exist, or may have been undeleted already" |
|
1958
|
|
|
], |
|
1959
|
|
|
'permdenied-undelete' => [ |
|
1960
|
|
|
'code' => 'permissiondenied', |
|
1961
|
|
|
'info' => "You don't have permission to restore deleted revisions" |
|
1962
|
|
|
], |
|
1963
|
|
|
'createonly-exists' => [ |
|
1964
|
|
|
'code' => 'articleexists', |
|
1965
|
|
|
'info' => 'The article you tried to create has been created already' |
|
1966
|
|
|
], |
|
1967
|
|
|
'nocreate-missing' => [ |
|
1968
|
|
|
'code' => 'missingtitle', |
|
1969
|
|
|
'info' => "The article you tried to edit doesn't exist" |
|
1970
|
|
|
], |
|
1971
|
|
|
'cantchangecontentmodel' => [ |
|
1972
|
|
|
'code' => 'cantchangecontentmodel', |
|
1973
|
|
|
'info' => "You don't have permission to change the content model of a page" |
|
1974
|
|
|
], |
|
1975
|
|
|
'nosuchrcid' => [ |
|
1976
|
|
|
'code' => 'nosuchrcid', |
|
1977
|
|
|
'info' => "There is no change with rcid \"\$1\"" |
|
1978
|
|
|
], |
|
1979
|
|
|
'nosuchlogid' => [ |
|
1980
|
|
|
'code' => 'nosuchlogid', |
|
1981
|
|
|
'info' => "There is no log entry with ID \"\$1\"" |
|
1982
|
|
|
], |
|
1983
|
|
|
'protect-invalidaction' => [ |
|
1984
|
|
|
'code' => 'protect-invalidaction', |
|
1985
|
|
|
'info' => "Invalid protection type \"\$1\"" |
|
1986
|
|
|
], |
|
1987
|
|
|
'protect-invalidlevel' => [ |
|
1988
|
|
|
'code' => 'protect-invalidlevel', |
|
1989
|
|
|
'info' => "Invalid protection level \"\$1\"" |
|
1990
|
|
|
], |
|
1991
|
|
|
'toofewexpiries' => [ |
|
1992
|
|
|
'code' => 'toofewexpiries', |
|
1993
|
|
|
'info' => "\$1 expiry timestamps were provided where \$2 were needed" |
|
1994
|
|
|
], |
|
1995
|
|
|
'cantimport' => [ |
|
1996
|
|
|
'code' => 'cantimport', |
|
1997
|
|
|
'info' => "You don't have permission to import pages" |
|
1998
|
|
|
], |
|
1999
|
|
|
'cantimport-upload' => [ |
|
2000
|
|
|
'code' => 'cantimport-upload', |
|
2001
|
|
|
'info' => "You don't have permission to import uploaded pages" |
|
2002
|
|
|
], |
|
2003
|
|
|
'importnofile' => [ 'code' => 'nofile', 'info' => "You didn't upload a file" ], |
|
2004
|
|
|
'importuploaderrorsize' => [ |
|
2005
|
|
|
'code' => 'filetoobig', |
|
2006
|
|
|
'info' => 'The file you uploaded is bigger than the maximum upload size' |
|
2007
|
|
|
], |
|
2008
|
|
|
'importuploaderrorpartial' => [ |
|
2009
|
|
|
'code' => 'partialupload', |
|
2010
|
|
|
'info' => 'The file was only partially uploaded' |
|
2011
|
|
|
], |
|
2012
|
|
|
'importuploaderrortemp' => [ |
|
2013
|
|
|
'code' => 'notempdir', |
|
2014
|
|
|
'info' => 'The temporary upload directory is missing' |
|
2015
|
|
|
], |
|
2016
|
|
|
'importcantopen' => [ |
|
2017
|
|
|
'code' => 'cantopenfile', |
|
2018
|
|
|
'info' => "Couldn't open the uploaded file" |
|
2019
|
|
|
], |
|
2020
|
|
|
'import-noarticle' => [ |
|
2021
|
|
|
'code' => 'badinterwiki', |
|
2022
|
|
|
'info' => 'Invalid interwiki title specified' |
|
2023
|
|
|
], |
|
2024
|
|
|
'importbadinterwiki' => [ |
|
2025
|
|
|
'code' => 'badinterwiki', |
|
2026
|
|
|
'info' => 'Invalid interwiki title specified' |
|
2027
|
|
|
], |
|
2028
|
|
|
'import-unknownerror' => [ |
|
2029
|
|
|
'code' => 'import-unknownerror', |
|
2030
|
|
|
'info' => "Unknown error on import: \"\$1\"" |
|
2031
|
|
|
], |
|
2032
|
|
|
'cantoverwrite-sharedfile' => [ |
|
2033
|
|
|
'code' => 'cantoverwrite-sharedfile', |
|
2034
|
|
|
'info' => 'The target file exists on a shared repository and you do not have permission to override it' |
|
2035
|
|
|
], |
|
2036
|
|
|
'sharedfile-exists' => [ |
|
2037
|
|
|
'code' => 'fileexists-sharedrepo-perm', |
|
2038
|
|
|
'info' => 'The target file exists on a shared repository. Use the ignorewarnings parameter to override it.' |
|
2039
|
|
|
], |
|
2040
|
|
|
'mustbeposted' => [ |
|
2041
|
|
|
'code' => 'mustbeposted', |
|
2042
|
|
|
'info' => "The \$1 module requires a POST request" |
|
2043
|
|
|
], |
|
2044
|
|
|
'show' => [ |
|
2045
|
|
|
'code' => 'show', |
|
2046
|
|
|
'info' => 'Incorrect parameter - mutually exclusive values may not be supplied' |
|
2047
|
|
|
], |
|
2048
|
|
|
'specialpage-cantexecute' => [ |
|
2049
|
|
|
'code' => 'specialpage-cantexecute', |
|
2050
|
|
|
'info' => "You don't have permission to view the results of this special page" |
|
2051
|
|
|
], |
|
2052
|
|
|
'invalidoldimage' => [ |
|
2053
|
|
|
'code' => 'invalidoldimage', |
|
2054
|
|
|
'info' => 'The oldimage parameter has invalid format' |
|
2055
|
|
|
], |
|
2056
|
|
|
'nodeleteablefile' => [ |
|
2057
|
|
|
'code' => 'nodeleteablefile', |
|
2058
|
|
|
'info' => 'No such old version of the file' |
|
2059
|
|
|
], |
|
2060
|
|
|
'fileexists-forbidden' => [ |
|
2061
|
|
|
'code' => 'fileexists-forbidden', |
|
2062
|
|
|
'info' => 'A file with name "$1" already exists, and cannot be overwritten.' |
|
2063
|
|
|
], |
|
2064
|
|
|
'fileexists-shared-forbidden' => [ |
|
2065
|
|
|
'code' => 'fileexists-shared-forbidden', |
|
2066
|
|
|
'info' => 'A file with name "$1" already exists in the shared file repository, and cannot be overwritten.' |
|
2067
|
|
|
], |
|
2068
|
|
|
'filerevert-badversion' => [ |
|
2069
|
|
|
'code' => 'filerevert-badversion', |
|
2070
|
|
|
'info' => 'There is no previous local version of this file with the provided timestamp.' |
|
2071
|
|
|
], |
|
2072
|
|
|
|
|
2073
|
|
|
// ApiEditPage messages |
|
2074
|
|
|
'noimageredirect-anon' => [ |
|
2075
|
|
|
'code' => 'noimageredirect-anon', |
|
2076
|
|
|
'info' => "Anonymous users can't create image redirects" |
|
2077
|
|
|
], |
|
2078
|
|
|
'noimageredirect-logged' => [ |
|
2079
|
|
|
'code' => 'noimageredirect', |
|
2080
|
|
|
'info' => "You don't have permission to create image redirects" |
|
2081
|
|
|
], |
|
2082
|
|
|
'spamdetected' => [ |
|
2083
|
|
|
'code' => 'spamdetected', |
|
2084
|
|
|
'info' => "Your edit was refused because it contained a spam fragment: \"\$1\"" |
|
2085
|
|
|
], |
|
2086
|
|
|
'contenttoobig' => [ |
|
2087
|
|
|
'code' => 'contenttoobig', |
|
2088
|
|
|
'info' => "The content you supplied exceeds the article size limit of \$1 kilobytes" |
|
2089
|
|
|
], |
|
2090
|
|
|
'noedit-anon' => [ 'code' => 'noedit-anon', 'info' => "Anonymous users can't edit pages" ], |
|
2091
|
|
|
'noedit' => [ 'code' => 'noedit', 'info' => "You don't have permission to edit pages" ], |
|
2092
|
|
|
'wasdeleted' => [ |
|
2093
|
|
|
'code' => 'pagedeleted', |
|
2094
|
|
|
'info' => 'The page has been deleted since you fetched its timestamp' |
|
2095
|
|
|
], |
|
2096
|
|
|
'blankpage' => [ |
|
2097
|
|
|
'code' => 'emptypage', |
|
2098
|
|
|
'info' => 'Creating new, empty pages is not allowed' |
|
2099
|
|
|
], |
|
2100
|
|
|
'editconflict' => [ 'code' => 'editconflict', 'info' => 'Edit conflict detected' ], |
|
2101
|
|
|
'hashcheckfailed' => [ 'code' => 'badmd5', 'info' => 'The supplied MD5 hash was incorrect' ], |
|
2102
|
|
|
'missingtext' => [ |
|
2103
|
|
|
'code' => 'notext', |
|
2104
|
|
|
'info' => 'One of the text, appendtext, prependtext and undo parameters must be set' |
|
2105
|
|
|
], |
|
2106
|
|
|
'emptynewsection' => [ |
|
2107
|
|
|
'code' => 'emptynewsection', |
|
2108
|
|
|
'info' => 'Creating empty new sections is not possible.' |
|
2109
|
|
|
], |
|
2110
|
|
|
'revwrongpage' => [ |
|
2111
|
|
|
'code' => 'revwrongpage', |
|
2112
|
|
|
'info' => "r\$1 is not a revision of \"\$2\"" |
|
2113
|
|
|
], |
|
2114
|
|
|
'undo-failure' => [ |
|
2115
|
|
|
'code' => 'undofailure', |
|
2116
|
|
|
'info' => 'Undo failed due to conflicting intermediate edits' |
|
2117
|
|
|
], |
|
2118
|
|
|
'content-not-allowed-here' => [ |
|
2119
|
|
|
'code' => 'contentnotallowedhere', |
|
2120
|
|
|
'info' => 'Content model "$1" is not allowed at title "$2"' |
|
2121
|
|
|
], |
|
2122
|
|
|
|
|
2123
|
|
|
// Messages from WikiPage::doEit(] |
|
2124
|
|
|
'edit-hook-aborted' => [ |
|
2125
|
|
|
'code' => 'edit-hook-aborted', |
|
2126
|
|
|
'info' => 'Your edit was aborted by an ArticleSave hook' |
|
2127
|
|
|
], |
|
2128
|
|
|
'edit-gone-missing' => [ |
|
2129
|
|
|
'code' => 'edit-gone-missing', |
|
2130
|
|
|
'info' => "The page you tried to edit doesn't seem to exist anymore" |
|
2131
|
|
|
], |
|
2132
|
|
|
'edit-conflict' => [ 'code' => 'editconflict', 'info' => 'Edit conflict detected' ], |
|
2133
|
|
|
'edit-already-exists' => [ |
|
2134
|
|
|
'code' => 'edit-already-exists', |
|
2135
|
|
|
'info' => 'It seems the page you tried to create already exist' |
|
2136
|
|
|
], |
|
2137
|
|
|
|
|
2138
|
|
|
// uploadMsgs |
|
2139
|
|
|
'invalid-file-key' => [ 'code' => 'invalid-file-key', 'info' => 'Not a valid file key' ], |
|
2140
|
|
|
'nouploadmodule' => [ 'code' => 'nouploadmodule', 'info' => 'No upload module set' ], |
|
2141
|
|
|
'uploaddisabled' => [ |
|
2142
|
|
|
'code' => 'uploaddisabled', |
|
2143
|
|
|
'info' => 'Uploads are not enabled. Make sure $wgEnableUploads is set to true in LocalSettings.php and the PHP ini setting file_uploads is true' |
|
2144
|
|
|
], |
|
2145
|
|
|
'copyuploaddisabled' => [ |
|
2146
|
|
|
'code' => 'copyuploaddisabled', |
|
2147
|
|
|
'info' => 'Uploads by URL is not enabled. Make sure $wgAllowCopyUploads is set to true in LocalSettings.php.' |
|
2148
|
|
|
], |
|
2149
|
|
|
'copyuploadbaddomain' => [ |
|
2150
|
|
|
'code' => 'copyuploadbaddomain', |
|
2151
|
|
|
'info' => 'Uploads by URL are not allowed from this domain.' |
|
2152
|
|
|
], |
|
2153
|
|
|
'copyuploadbadurl' => [ |
|
2154
|
|
|
'code' => 'copyuploadbadurl', |
|
2155
|
|
|
'info' => 'Upload not allowed from this URL.' |
|
2156
|
|
|
], |
|
2157
|
|
|
|
|
2158
|
|
|
'filename-tooshort' => [ |
|
2159
|
|
|
'code' => 'filename-tooshort', |
|
2160
|
|
|
'info' => 'The filename is too short' |
|
2161
|
|
|
], |
|
2162
|
|
|
'filename-toolong' => [ 'code' => 'filename-toolong', 'info' => 'The filename is too long' ], |
|
2163
|
|
|
'illegal-filename' => [ |
|
2164
|
|
|
'code' => 'illegal-filename', |
|
2165
|
|
|
'info' => 'The filename is not allowed' |
|
2166
|
|
|
], |
|
2167
|
|
|
'filetype-missing' => [ |
|
2168
|
|
|
'code' => 'filetype-missing', |
|
2169
|
|
|
'info' => 'The file is missing an extension' |
|
2170
|
|
|
], |
|
2171
|
|
|
|
|
2172
|
|
|
'mustbeloggedin' => [ 'code' => 'mustbeloggedin', 'info' => 'You must be logged in to $1.' ] |
|
2173
|
|
|
]; |
|
2174
|
|
|
// @codingStandardsIgnoreEnd |
|
2175
|
|
|
|
|
2176
|
|
|
/** |
|
2177
|
|
|
* Helper function for readonly errors |
|
2178
|
|
|
* |
|
2179
|
|
|
* @throws UsageException always |
|
2180
|
|
|
*/ |
|
2181
|
|
|
public function dieReadOnly() { |
|
2182
|
|
|
$parsed = $this->parseMsg( [ 'readonlytext' ] ); |
|
2183
|
|
|
$this->dieUsage( $parsed['info'], $parsed['code'], /* http error */ 0, |
|
2184
|
|
|
[ 'readonlyreason' => wfReadOnlyReason() ] ); |
|
2185
|
|
|
} |
|
2186
|
|
|
|
|
2187
|
|
|
/** |
|
2188
|
|
|
* Output the error message related to a certain array |
|
2189
|
|
|
* @param array|string|MessageSpecifier $error Element of a getUserPermissionsErrors()-style array |
|
2190
|
|
|
* @throws UsageException always |
|
2191
|
|
|
*/ |
|
2192
|
|
|
public function dieUsageMsg( $error ) { |
|
2193
|
|
|
# most of the time we send a 1 element, so we might as well send it as |
|
2194
|
|
|
# a string and make this an array here. |
|
2195
|
|
|
if ( is_string( $error ) ) { |
|
2196
|
|
|
$error = [ $error ]; |
|
2197
|
|
|
} |
|
2198
|
|
|
$parsed = $this->parseMsg( $error ); |
|
2199
|
|
|
$extraData = isset( $parsed['data'] ) ? $parsed['data'] : null; |
|
2200
|
|
|
$this->dieUsage( $parsed['info'], $parsed['code'], 0, $extraData ); |
|
2201
|
|
|
} |
|
2202
|
|
|
|
|
2203
|
|
|
/** |
|
2204
|
|
|
* Will only set a warning instead of failing if the global $wgDebugAPI |
|
2205
|
|
|
* is set to true. Otherwise behaves exactly as dieUsageMsg(). |
|
2206
|
|
|
* @param array|string|MessageSpecifier $error Element of a getUserPermissionsErrors()-style array |
|
2207
|
|
|
* @throws UsageException |
|
2208
|
|
|
* @since 1.21 |
|
2209
|
|
|
*/ |
|
2210
|
|
|
public function dieUsageMsgOrDebug( $error ) { |
|
2211
|
|
|
if ( $this->getConfig()->get( 'DebugAPI' ) !== true ) { |
|
2212
|
|
|
$this->dieUsageMsg( $error ); |
|
2213
|
|
|
} |
|
2214
|
|
|
|
|
2215
|
|
|
if ( is_string( $error ) ) { |
|
2216
|
|
|
$error = [ $error ]; |
|
2217
|
|
|
} |
|
2218
|
|
|
$parsed = $this->parseMsg( $error ); |
|
2219
|
|
|
$this->setWarning( '$wgDebugAPI: ' . $parsed['code'] . ' - ' . $parsed['info'] ); |
|
2220
|
|
|
} |
|
2221
|
|
|
|
|
2222
|
|
|
/** |
|
2223
|
|
|
* Die with the $prefix.'badcontinue' error. This call is common enough to |
|
2224
|
|
|
* make it into the base method. |
|
2225
|
|
|
* @param bool $condition Will only die if this value is true |
|
2226
|
|
|
* @throws UsageException |
|
2227
|
|
|
* @since 1.21 |
|
2228
|
|
|
*/ |
|
2229
|
|
|
protected function dieContinueUsageIf( $condition ) { |
|
2230
|
|
|
if ( $condition ) { |
|
2231
|
|
|
$this->dieUsage( |
|
2232
|
|
|
'Invalid continue param. You should pass the original value returned by the previous query', |
|
2233
|
|
|
'badcontinue' ); |
|
2234
|
|
|
} |
|
2235
|
|
|
} |
|
2236
|
|
|
|
|
2237
|
|
|
/** |
|
2238
|
|
|
* Return the error message related to a certain array |
|
2239
|
|
|
* @param array|string|MessageSpecifier $error Element of a getUserPermissionsErrors()-style array |
|
2240
|
|
|
* @return [ 'code' => code, 'info' => info ] |
|
|
|
|
|
|
2241
|
|
|
*/ |
|
2242
|
|
|
public function parseMsg( $error ) { |
|
2243
|
|
|
// Check whether someone passed the whole array, instead of one element as |
|
2244
|
|
|
// documented. This breaks if it's actually an array of fallback keys, but |
|
2245
|
|
|
// that's long-standing misbehavior introduced in r87627 to incorrectly |
|
2246
|
|
|
// fix T30797. |
|
2247
|
|
|
if ( is_array( $error ) ) { |
|
2248
|
|
|
$first = reset( $error ); |
|
2249
|
|
|
if ( is_array( $first ) ) { |
|
2250
|
|
|
wfDebug( __METHOD__ . ' was passed an array of arrays. ' . wfGetAllCallers( 5 ) ); |
|
2251
|
|
|
$error = $first; |
|
2252
|
|
|
} |
|
2253
|
|
|
} |
|
2254
|
|
|
|
|
2255
|
|
|
$msg = Message::newFromSpecifier( $error ); |
|
2256
|
|
|
|
|
2257
|
|
|
if ( $msg instanceof IApiMessage ) { |
|
2258
|
|
|
return [ |
|
2259
|
|
|
'code' => $msg->getApiCode(), |
|
2260
|
|
|
'info' => $msg->inLanguage( 'en' )->useDatabase( false )->text(), |
|
2261
|
|
|
'data' => $msg->getApiData() |
|
2262
|
|
|
]; |
|
2263
|
|
|
} |
|
2264
|
|
|
|
|
2265
|
|
|
$key = $msg->getKey(); |
|
2266
|
|
|
if ( isset( self::$messageMap[$key] ) ) { |
|
2267
|
|
|
$params = $msg->getParams(); |
|
2268
|
|
|
return [ |
|
2269
|
|
|
'code' => wfMsgReplaceArgs( self::$messageMap[$key]['code'], $params ), |
|
2270
|
|
|
'info' => wfMsgReplaceArgs( self::$messageMap[$key]['info'], $params ) |
|
2271
|
|
|
]; |
|
2272
|
|
|
} |
|
2273
|
|
|
|
|
2274
|
|
|
// If the key isn't present, throw an "unknown error" |
|
2275
|
|
|
return $this->parseMsg( [ 'unknownerror', $key ] ); |
|
2276
|
|
|
} |
|
2277
|
|
|
|
|
2278
|
|
|
/** |
|
2279
|
|
|
* Internal code errors should be reported with this method |
|
2280
|
|
|
* @param string $method Method or function name |
|
2281
|
|
|
* @param string $message Error message |
|
2282
|
|
|
* @throws MWException always |
|
2283
|
|
|
*/ |
|
2284
|
|
|
protected static function dieDebug( $method, $message ) { |
|
2285
|
|
|
throw new MWException( "Internal error in $method: $message" ); |
|
2286
|
|
|
} |
|
2287
|
|
|
|
|
2288
|
|
|
/** |
|
2289
|
|
|
* Write logging information for API features to a debug log, for usage |
|
2290
|
|
|
* analysis. |
|
2291
|
|
|
* @param string $feature Feature being used. |
|
2292
|
|
|
*/ |
|
2293
|
|
|
public function logFeatureUsage( $feature ) { |
|
2294
|
|
|
$request = $this->getRequest(); |
|
2295
|
|
|
$s = '"' . addslashes( $feature ) . '"' . |
|
2296
|
|
|
' "' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) . '"' . |
|
2297
|
|
|
' "' . $request->getIP() . '"' . |
|
2298
|
|
|
' "' . addslashes( $request->getHeader( 'Referer' ) ) . '"' . |
|
2299
|
|
|
' "' . addslashes( $this->getMain()->getUserAgent() ) . '"'; |
|
2300
|
|
|
wfDebugLog( 'api-feature-usage', $s, 'private' ); |
|
2301
|
|
|
} |
|
2302
|
|
|
|
|
2303
|
|
|
/**@}*/ |
|
2304
|
|
|
|
|
2305
|
|
|
/************************************************************************//** |
|
2306
|
|
|
* @name Help message generation |
|
2307
|
|
|
* @{ |
|
2308
|
|
|
*/ |
|
2309
|
|
|
|
|
2310
|
|
|
/** |
|
2311
|
|
|
* Return the description message. |
|
2312
|
|
|
* |
|
2313
|
|
|
* @return string|array|Message |
|
2314
|
|
|
*/ |
|
2315
|
|
|
protected function getDescriptionMessage() { |
|
2316
|
|
|
return "apihelp-{$this->getModulePath()}-description"; |
|
2317
|
|
|
} |
|
2318
|
|
|
|
|
2319
|
|
|
/** |
|
2320
|
|
|
* Get final module description, after hooks have had a chance to tweak it as |
|
2321
|
|
|
* needed. |
|
2322
|
|
|
* |
|
2323
|
|
|
* @since 1.25, returns Message[] rather than string[] |
|
2324
|
|
|
* @return Message[] |
|
2325
|
|
|
*/ |
|
2326
|
|
|
public function getFinalDescription() { |
|
2327
|
|
|
$desc = $this->getDescription(); |
|
2328
|
|
|
Hooks::run( 'APIGetDescription', [ &$this, &$desc ] ); |
|
2329
|
|
|
$desc = self::escapeWikiText( $desc ); |
|
2330
|
|
|
if ( is_array( $desc ) ) { |
|
2331
|
|
|
$desc = implode( "\n", $desc ); |
|
2332
|
|
|
} else { |
|
2333
|
|
|
$desc = (string)$desc; |
|
2334
|
|
|
} |
|
2335
|
|
|
|
|
2336
|
|
|
$msg = ApiBase::makeMessage( $this->getDescriptionMessage(), $this->getContext(), [ |
|
2337
|
|
|
$this->getModulePrefix(), |
|
2338
|
|
|
$this->getModuleName(), |
|
2339
|
|
|
$this->getModulePath(), |
|
2340
|
|
|
] ); |
|
2341
|
|
|
if ( !$msg->exists() ) { |
|
2342
|
|
|
$msg = $this->msg( 'api-help-fallback-description', $desc ); |
|
2343
|
|
|
} |
|
2344
|
|
|
$msgs = [ $msg ]; |
|
2345
|
|
|
|
|
2346
|
|
|
Hooks::run( 'APIGetDescriptionMessages', [ $this, &$msgs ] ); |
|
2347
|
|
|
|
|
2348
|
|
|
return $msgs; |
|
2349
|
|
|
} |
|
2350
|
|
|
|
|
2351
|
|
|
/** |
|
2352
|
|
|
* Get final list of parameters, after hooks have had a chance to |
|
2353
|
|
|
* tweak it as needed. |
|
2354
|
|
|
* |
|
2355
|
|
|
* @param int $flags Zero or more flags like GET_VALUES_FOR_HELP |
|
2356
|
|
|
* @return array|bool False on no parameters |
|
2357
|
|
|
* @since 1.21 $flags param added |
|
2358
|
|
|
*/ |
|
2359
|
|
|
public function getFinalParams( $flags = 0 ) { |
|
2360
|
|
|
$params = $this->getAllowedParams( $flags ); |
|
2361
|
|
|
if ( !$params ) { |
|
2362
|
|
|
$params = []; |
|
2363
|
|
|
} |
|
2364
|
|
|
|
|
2365
|
|
|
if ( $this->needsToken() ) { |
|
|
|
|
|
|
2366
|
|
|
$params['token'] = [ |
|
2367
|
|
|
ApiBase::PARAM_TYPE => 'string', |
|
2368
|
|
|
ApiBase::PARAM_REQUIRED => true, |
|
2369
|
|
|
ApiBase::PARAM_HELP_MSG => [ |
|
2370
|
|
|
'api-help-param-token', |
|
2371
|
|
|
$this->needsToken(), |
|
2372
|
|
|
], |
|
2373
|
|
|
] + ( isset( $params['token'] ) ? $params['token'] : [] ); |
|
2374
|
|
|
} |
|
2375
|
|
|
|
|
2376
|
|
|
Hooks::run( 'APIGetAllowedParams', [ &$this, &$params, $flags ] ); |
|
2377
|
|
|
|
|
2378
|
|
|
return $params; |
|
2379
|
|
|
} |
|
2380
|
|
|
|
|
2381
|
|
|
/** |
|
2382
|
|
|
* Get final parameter descriptions, after hooks have had a chance to tweak it as |
|
2383
|
|
|
* needed. |
|
2384
|
|
|
* |
|
2385
|
|
|
* @since 1.25, returns array of Message[] rather than array of string[] |
|
2386
|
|
|
* @return array Keys are parameter names, values are arrays of Message objects |
|
2387
|
|
|
*/ |
|
2388
|
|
|
public function getFinalParamDescription() { |
|
2389
|
|
|
$prefix = $this->getModulePrefix(); |
|
2390
|
|
|
$name = $this->getModuleName(); |
|
2391
|
|
|
$path = $this->getModulePath(); |
|
2392
|
|
|
|
|
2393
|
|
|
$desc = $this->getParamDescription(); |
|
2394
|
|
|
Hooks::run( 'APIGetParamDescription', [ &$this, &$desc ] ); |
|
2395
|
|
|
|
|
2396
|
|
|
if ( !$desc ) { |
|
2397
|
|
|
$desc = []; |
|
2398
|
|
|
} |
|
2399
|
|
|
$desc = self::escapeWikiText( $desc ); |
|
|
|
|
|
|
2400
|
|
|
|
|
2401
|
|
|
$params = $this->getFinalParams( ApiBase::GET_VALUES_FOR_HELP ); |
|
2402
|
|
|
$msgs = []; |
|
2403
|
|
|
foreach ( $params as $param => $settings ) { |
|
|
|
|
|
|
2404
|
|
|
if ( !is_array( $settings ) ) { |
|
2405
|
|
|
$settings = []; |
|
2406
|
|
|
} |
|
2407
|
|
|
|
|
2408
|
|
|
$d = isset( $desc[$param] ) ? $desc[$param] : ''; |
|
2409
|
|
|
if ( is_array( $d ) ) { |
|
2410
|
|
|
// Special handling for prop parameters |
|
2411
|
|
|
$d = array_map( function ( $line ) { |
|
2412
|
|
|
if ( preg_match( '/^\s+(\S+)\s+-\s+(.+)$/', $line, $m ) ) { |
|
2413
|
|
|
$line = "\n;{$m[1]}:{$m[2]}"; |
|
2414
|
|
|
} |
|
2415
|
|
|
return $line; |
|
2416
|
|
|
}, $d ); |
|
2417
|
|
|
$d = implode( ' ', $d ); |
|
2418
|
|
|
} |
|
2419
|
|
|
|
|
2420
|
|
|
if ( isset( $settings[ApiBase::PARAM_HELP_MSG] ) ) { |
|
2421
|
|
|
$msg = $settings[ApiBase::PARAM_HELP_MSG]; |
|
2422
|
|
|
} else { |
|
2423
|
|
|
$msg = $this->msg( "apihelp-{$path}-param-{$param}" ); |
|
2424
|
|
|
if ( !$msg->exists() ) { |
|
2425
|
|
|
$msg = $this->msg( 'api-help-fallback-parameter', $d ); |
|
2426
|
|
|
} |
|
2427
|
|
|
} |
|
2428
|
|
|
$msg = ApiBase::makeMessage( $msg, $this->getContext(), |
|
2429
|
|
|
[ $prefix, $param, $name, $path ] ); |
|
2430
|
|
|
if ( !$msg ) { |
|
2431
|
|
|
self::dieDebug( __METHOD__, |
|
2432
|
|
|
'Value in ApiBase::PARAM_HELP_MSG is not valid' ); |
|
2433
|
|
|
} |
|
2434
|
|
|
$msgs[$param] = [ $msg ]; |
|
2435
|
|
|
|
|
2436
|
|
|
if ( isset( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) { |
|
2437
|
|
|
if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE] ) ) { |
|
2438
|
|
|
self::dieDebug( __METHOD__, |
|
2439
|
|
|
'ApiBase::PARAM_HELP_MSG_PER_VALUE is not valid' ); |
|
2440
|
|
|
} |
|
2441
|
|
|
if ( !is_array( $settings[ApiBase::PARAM_TYPE] ) ) { |
|
2442
|
|
|
self::dieDebug( __METHOD__, |
|
2443
|
|
|
'ApiBase::PARAM_HELP_MSG_PER_VALUE may only be used when ' . |
|
2444
|
|
|
'ApiBase::PARAM_TYPE is an array' ); |
|
2445
|
|
|
} |
|
2446
|
|
|
|
|
2447
|
|
|
$valueMsgs = $settings[ApiBase::PARAM_HELP_MSG_PER_VALUE]; |
|
2448
|
|
|
foreach ( $settings[ApiBase::PARAM_TYPE] as $value ) { |
|
2449
|
|
|
if ( isset( $valueMsgs[$value] ) ) { |
|
2450
|
|
|
$msg = $valueMsgs[$value]; |
|
2451
|
|
|
} else { |
|
2452
|
|
|
$msg = "apihelp-{$path}-paramvalue-{$param}-{$value}"; |
|
2453
|
|
|
} |
|
2454
|
|
|
$m = ApiBase::makeMessage( $msg, $this->getContext(), |
|
2455
|
|
|
[ $prefix, $param, $name, $path, $value ] ); |
|
2456
|
|
|
if ( $m ) { |
|
2457
|
|
|
$m = new ApiHelpParamValueMessage( |
|
2458
|
|
|
$value, |
|
2459
|
|
|
[ $m->getKey(), 'api-help-param-no-description' ], |
|
2460
|
|
|
$m->getParams() |
|
2461
|
|
|
); |
|
2462
|
|
|
$msgs[$param][] = $m->setContext( $this->getContext() ); |
|
2463
|
|
|
} else { |
|
2464
|
|
|
self::dieDebug( __METHOD__, |
|
2465
|
|
|
"Value in ApiBase::PARAM_HELP_MSG_PER_VALUE for $value is not valid" ); |
|
2466
|
|
|
} |
|
2467
|
|
|
} |
|
2468
|
|
|
} |
|
2469
|
|
|
|
|
2470
|
|
|
if ( isset( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) { |
|
2471
|
|
|
if ( !is_array( $settings[ApiBase::PARAM_HELP_MSG_APPEND] ) ) { |
|
2472
|
|
|
self::dieDebug( __METHOD__, |
|
2473
|
|
|
'Value for ApiBase::PARAM_HELP_MSG_APPEND is not an array' ); |
|
2474
|
|
|
} |
|
2475
|
|
|
foreach ( $settings[ApiBase::PARAM_HELP_MSG_APPEND] as $m ) { |
|
2476
|
|
|
$m = ApiBase::makeMessage( $m, $this->getContext(), |
|
2477
|
|
|
[ $prefix, $param, $name, $path ] ); |
|
2478
|
|
|
if ( $m ) { |
|
2479
|
|
|
$msgs[$param][] = $m; |
|
2480
|
|
|
} else { |
|
2481
|
|
|
self::dieDebug( __METHOD__, |
|
2482
|
|
|
'Value in ApiBase::PARAM_HELP_MSG_APPEND is not valid' ); |
|
2483
|
|
|
} |
|
2484
|
|
|
} |
|
2485
|
|
|
} |
|
2486
|
|
|
} |
|
2487
|
|
|
|
|
2488
|
|
|
Hooks::run( 'APIGetParamDescriptionMessages', [ $this, &$msgs ] ); |
|
2489
|
|
|
|
|
2490
|
|
|
return $msgs; |
|
2491
|
|
|
} |
|
2492
|
|
|
|
|
2493
|
|
|
/** |
|
2494
|
|
|
* Generates the list of flags for the help screen and for action=paraminfo |
|
2495
|
|
|
* |
|
2496
|
|
|
* Corresponding messages: api-help-flag-deprecated, |
|
2497
|
|
|
* api-help-flag-internal, api-help-flag-readrights, |
|
2498
|
|
|
* api-help-flag-writerights, api-help-flag-mustbeposted |
|
2499
|
|
|
* |
|
2500
|
|
|
* @return string[] |
|
2501
|
|
|
*/ |
|
2502
|
|
|
protected function getHelpFlags() { |
|
2503
|
|
|
$flags = []; |
|
2504
|
|
|
|
|
2505
|
|
|
if ( $this->isDeprecated() ) { |
|
2506
|
|
|
$flags[] = 'deprecated'; |
|
2507
|
|
|
} |
|
2508
|
|
|
if ( $this->isInternal() ) { |
|
2509
|
|
|
$flags[] = 'internal'; |
|
2510
|
|
|
} |
|
2511
|
|
|
if ( $this->isReadMode() ) { |
|
2512
|
|
|
$flags[] = 'readrights'; |
|
2513
|
|
|
} |
|
2514
|
|
|
if ( $this->isWriteMode() ) { |
|
2515
|
|
|
$flags[] = 'writerights'; |
|
2516
|
|
|
} |
|
2517
|
|
|
if ( $this->mustBePosted() ) { |
|
2518
|
|
|
$flags[] = 'mustbeposted'; |
|
2519
|
|
|
} |
|
2520
|
|
|
|
|
2521
|
|
|
return $flags; |
|
2522
|
|
|
} |
|
2523
|
|
|
|
|
2524
|
|
|
/** |
|
2525
|
|
|
* Returns information about the source of this module, if known |
|
2526
|
|
|
* |
|
2527
|
|
|
* Returned array is an array with the following keys: |
|
2528
|
|
|
* - path: Install path |
|
2529
|
|
|
* - name: Extension name, or "MediaWiki" for core |
|
2530
|
|
|
* - namemsg: (optional) i18n message key for a display name |
|
2531
|
|
|
* - license-name: (optional) Name of license |
|
2532
|
|
|
* |
|
2533
|
|
|
* @return array|null |
|
2534
|
|
|
*/ |
|
2535
|
|
|
protected function getModuleSourceInfo() { |
|
2536
|
|
|
global $IP; |
|
2537
|
|
|
|
|
2538
|
|
|
if ( $this->mModuleSource !== false ) { |
|
2539
|
|
|
return $this->mModuleSource; |
|
2540
|
|
|
} |
|
2541
|
|
|
|
|
2542
|
|
|
// First, try to find where the module comes from... |
|
2543
|
|
|
$rClass = new ReflectionClass( $this ); |
|
2544
|
|
|
$path = $rClass->getFileName(); |
|
2545
|
|
|
if ( !$path ) { |
|
2546
|
|
|
// No path known? |
|
2547
|
|
|
$this->mModuleSource = null; |
|
2548
|
|
|
return null; |
|
2549
|
|
|
} |
|
2550
|
|
|
$path = realpath( $path ) ?: $path; |
|
2551
|
|
|
|
|
2552
|
|
|
// Build map of extension directories to extension info |
|
2553
|
|
|
if ( self::$extensionInfo === null ) { |
|
2554
|
|
|
$extDir = $this->getConfig()->get( 'ExtensionDirectory' ); |
|
2555
|
|
|
self::$extensionInfo = [ |
|
2556
|
|
|
realpath( __DIR__ ) ?: __DIR__ => [ |
|
2557
|
|
|
'path' => $IP, |
|
2558
|
|
|
'name' => 'MediaWiki', |
|
2559
|
|
|
'license-name' => 'GPL-2.0+', |
|
2560
|
|
|
], |
|
2561
|
|
|
realpath( "$IP/extensions" ) ?: "$IP/extensions" => null, |
|
2562
|
|
|
realpath( $extDir ) ?: $extDir => null, |
|
2563
|
|
|
]; |
|
2564
|
|
|
$keep = [ |
|
2565
|
|
|
'path' => null, |
|
2566
|
|
|
'name' => null, |
|
2567
|
|
|
'namemsg' => null, |
|
2568
|
|
|
'license-name' => null, |
|
2569
|
|
|
]; |
|
2570
|
|
|
foreach ( $this->getConfig()->get( 'ExtensionCredits' ) as $group ) { |
|
2571
|
|
|
foreach ( $group as $ext ) { |
|
2572
|
|
|
if ( !isset( $ext['path'] ) || !isset( $ext['name'] ) ) { |
|
2573
|
|
|
// This shouldn't happen, but does anyway. |
|
2574
|
|
|
continue; |
|
2575
|
|
|
} |
|
2576
|
|
|
|
|
2577
|
|
|
$extpath = $ext['path']; |
|
2578
|
|
|
if ( !is_dir( $extpath ) ) { |
|
2579
|
|
|
$extpath = dirname( $extpath ); |
|
2580
|
|
|
} |
|
2581
|
|
|
self::$extensionInfo[realpath( $extpath ) ?: $extpath] = |
|
2582
|
|
|
array_intersect_key( $ext, $keep ); |
|
2583
|
|
|
} |
|
2584
|
|
|
} |
|
2585
|
|
|
foreach ( ExtensionRegistry::getInstance()->getAllThings() as $ext ) { |
|
2586
|
|
|
$extpath = $ext['path']; |
|
2587
|
|
|
if ( !is_dir( $extpath ) ) { |
|
2588
|
|
|
$extpath = dirname( $extpath ); |
|
2589
|
|
|
} |
|
2590
|
|
|
self::$extensionInfo[realpath( $extpath ) ?: $extpath] = |
|
2591
|
|
|
array_intersect_key( $ext, $keep ); |
|
2592
|
|
|
} |
|
2593
|
|
|
} |
|
2594
|
|
|
|
|
2595
|
|
|
// Now traverse parent directories until we find a match or run out of |
|
2596
|
|
|
// parents. |
|
2597
|
|
|
do { |
|
2598
|
|
|
if ( array_key_exists( $path, self::$extensionInfo ) ) { |
|
2599
|
|
|
// Found it! |
|
2600
|
|
|
$this->mModuleSource = self::$extensionInfo[$path]; |
|
2601
|
|
|
return $this->mModuleSource; |
|
2602
|
|
|
} |
|
2603
|
|
|
|
|
2604
|
|
|
$oldpath = $path; |
|
2605
|
|
|
$path = dirname( $path ); |
|
2606
|
|
|
} while ( $path !== $oldpath ); |
|
2607
|
|
|
|
|
2608
|
|
|
// No idea what extension this might be. |
|
2609
|
|
|
$this->mModuleSource = null; |
|
2610
|
|
|
return null; |
|
2611
|
|
|
} |
|
2612
|
|
|
|
|
2613
|
|
|
/** |
|
2614
|
|
|
* Called from ApiHelp before the pieces are joined together and returned. |
|
2615
|
|
|
* |
|
2616
|
|
|
* This exists mainly for ApiMain to add the Permissions and Credits |
|
2617
|
|
|
* sections. Other modules probably don't need it. |
|
2618
|
|
|
* |
|
2619
|
|
|
* @param string[] &$help Array of help data |
|
2620
|
|
|
* @param array $options Options passed to ApiHelp::getHelp |
|
2621
|
|
|
* @param array &$tocData If a TOC is being generated, this array has keys |
|
2622
|
|
|
* as anchors in the page and values as for Linker::generateTOC(). |
|
2623
|
|
|
*/ |
|
2624
|
|
|
public function modifyHelp( array &$help, array $options, array &$tocData ) { |
|
2625
|
|
|
} |
|
2626
|
|
|
|
|
2627
|
|
|
/**@}*/ |
|
2628
|
|
|
|
|
2629
|
|
|
/************************************************************************//** |
|
2630
|
|
|
* @name Deprecated |
|
2631
|
|
|
* @{ |
|
2632
|
|
|
*/ |
|
2633
|
|
|
|
|
2634
|
|
|
/** |
|
2635
|
|
|
* Returns the description string for this module |
|
2636
|
|
|
* |
|
2637
|
|
|
* Ignored if an i18n message exists for |
|
2638
|
|
|
* "apihelp-{$this->getModulePath()}-description". |
|
2639
|
|
|
* |
|
2640
|
|
|
* @deprecated since 1.25 |
|
2641
|
|
|
* @return Message|string|array |
|
2642
|
|
|
*/ |
|
2643
|
|
|
protected function getDescription() { |
|
2644
|
|
|
return false; |
|
2645
|
|
|
} |
|
2646
|
|
|
|
|
2647
|
|
|
/** |
|
2648
|
|
|
* Returns an array of parameter descriptions. |
|
2649
|
|
|
* |
|
2650
|
|
|
* For each parameter, ignored if an i18n message exists for the parameter. |
|
2651
|
|
|
* By default that message is |
|
2652
|
|
|
* "apihelp-{$this->getModulePath()}-param-{$param}", but it may be |
|
2653
|
|
|
* overridden using ApiBase::PARAM_HELP_MSG in the data returned by |
|
2654
|
|
|
* self::getFinalParams(). |
|
2655
|
|
|
* |
|
2656
|
|
|
* @deprecated since 1.25 |
|
2657
|
|
|
* @return array|bool False on no parameter descriptions |
|
2658
|
|
|
*/ |
|
2659
|
|
|
protected function getParamDescription() { |
|
2660
|
|
|
return []; |
|
2661
|
|
|
} |
|
2662
|
|
|
|
|
2663
|
|
|
/** |
|
2664
|
|
|
* Returns usage examples for this module. |
|
2665
|
|
|
* |
|
2666
|
|
|
* Return value as an array is either: |
|
2667
|
|
|
* - numeric keys with partial URLs ("api.php?" plus a query string) as |
|
2668
|
|
|
* values |
|
2669
|
|
|
* - sequential numeric keys with even-numbered keys being display-text |
|
2670
|
|
|
* and odd-numbered keys being partial urls |
|
2671
|
|
|
* - partial URLs as keys with display-text (string or array-to-be-joined) |
|
2672
|
|
|
* as values |
|
2673
|
|
|
* Return value as a string is the same as an array with a numeric key and |
|
2674
|
|
|
* that value, and boolean false means "no examples". |
|
2675
|
|
|
* |
|
2676
|
|
|
* @deprecated since 1.25, use getExamplesMessages() instead |
|
2677
|
|
|
* @return bool|string|array |
|
2678
|
|
|
*/ |
|
2679
|
|
|
protected function getExamples() { |
|
2680
|
|
|
return false; |
|
2681
|
|
|
} |
|
2682
|
|
|
|
|
2683
|
|
|
/** |
|
2684
|
|
|
* Generates help message for this module, or false if there is no description |
|
2685
|
|
|
* @deprecated since 1.25 |
|
2686
|
|
|
* @return string|bool |
|
2687
|
|
|
*/ |
|
2688
|
|
|
public function makeHelpMsg() { |
|
2689
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
2690
|
|
|
static $lnPrfx = "\n "; |
|
2691
|
|
|
|
|
2692
|
|
|
$msg = $this->getFinalDescription(); |
|
2693
|
|
|
|
|
2694
|
|
|
if ( $msg !== false ) { |
|
2695
|
|
|
|
|
2696
|
|
|
if ( !is_array( $msg ) ) { |
|
2697
|
|
|
$msg = [ |
|
2698
|
|
|
$msg |
|
2699
|
|
|
]; |
|
2700
|
|
|
} |
|
2701
|
|
|
$msg = $lnPrfx . implode( $lnPrfx, $msg ) . "\n"; |
|
2702
|
|
|
|
|
2703
|
|
|
$msg .= $this->makeHelpArrayToString( $lnPrfx, false, $this->getHelpUrls() ); |
|
2704
|
|
|
|
|
2705
|
|
|
if ( $this->isReadMode() ) { |
|
2706
|
|
|
$msg .= "\nThis module requires read rights"; |
|
2707
|
|
|
} |
|
2708
|
|
|
if ( $this->isWriteMode() ) { |
|
2709
|
|
|
$msg .= "\nThis module requires write rights"; |
|
2710
|
|
|
} |
|
2711
|
|
|
if ( $this->mustBePosted() ) { |
|
2712
|
|
|
$msg .= "\nThis module only accepts POST requests"; |
|
2713
|
|
|
} |
|
2714
|
|
|
if ( $this->isReadMode() || $this->isWriteMode() || |
|
2715
|
|
|
$this->mustBePosted() |
|
2716
|
|
|
) { |
|
2717
|
|
|
$msg .= "\n"; |
|
2718
|
|
|
} |
|
2719
|
|
|
|
|
2720
|
|
|
// Parameters |
|
2721
|
|
|
$paramsMsg = $this->makeHelpMsgParameters(); |
|
2722
|
|
|
if ( $paramsMsg !== false ) { |
|
2723
|
|
|
$msg .= "Parameters:\n$paramsMsg"; |
|
2724
|
|
|
} |
|
2725
|
|
|
|
|
2726
|
|
|
$examples = $this->getExamples(); |
|
2727
|
|
|
if ( $examples ) { |
|
2728
|
|
|
if ( !is_array( $examples ) ) { |
|
2729
|
|
|
$examples = [ |
|
2730
|
|
|
$examples |
|
2731
|
|
|
]; |
|
2732
|
|
|
} |
|
2733
|
|
|
$msg .= 'Example' . ( count( $examples ) > 1 ? 's' : '' ) . ":\n"; |
|
2734
|
|
|
foreach ( $examples as $k => $v ) { |
|
2735
|
|
|
if ( is_numeric( $k ) ) { |
|
2736
|
|
|
$msg .= " $v\n"; |
|
2737
|
|
|
} else { |
|
2738
|
|
View Code Duplication |
if ( is_array( $v ) ) { |
|
2739
|
|
|
$msgExample = implode( "\n", array_map( [ $this, 'indentExampleText' ], $v ) ); |
|
2740
|
|
|
} else { |
|
2741
|
|
|
$msgExample = " $v"; |
|
2742
|
|
|
} |
|
2743
|
|
|
$msgExample .= ':'; |
|
2744
|
|
|
$msg .= wordwrap( $msgExample, 100, "\n" ) . "\n $k\n"; |
|
2745
|
|
|
} |
|
2746
|
|
|
} |
|
2747
|
|
|
} |
|
2748
|
|
|
} |
|
2749
|
|
|
|
|
2750
|
|
|
return $msg; |
|
2751
|
|
|
} |
|
2752
|
|
|
|
|
2753
|
|
|
/** |
|
2754
|
|
|
* @deprecated since 1.25 |
|
2755
|
|
|
* @param string $item |
|
2756
|
|
|
* @return string |
|
2757
|
|
|
*/ |
|
2758
|
|
|
private function indentExampleText( $item ) { |
|
2759
|
|
|
return ' ' . $item; |
|
2760
|
|
|
} |
|
2761
|
|
|
|
|
2762
|
|
|
/** |
|
2763
|
|
|
* @deprecated since 1.25 |
|
2764
|
|
|
* @param string $prefix Text to split output items |
|
2765
|
|
|
* @param string $title What is being output |
|
2766
|
|
|
* @param string|array $input |
|
2767
|
|
|
* @return string |
|
2768
|
|
|
*/ |
|
2769
|
|
|
protected function makeHelpArrayToString( $prefix, $title, $input ) { |
|
2770
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
2771
|
|
|
if ( $input === false ) { |
|
2772
|
|
|
return ''; |
|
2773
|
|
|
} |
|
2774
|
|
|
if ( !is_array( $input ) ) { |
|
2775
|
|
|
$input = [ $input ]; |
|
2776
|
|
|
} |
|
2777
|
|
|
|
|
2778
|
|
|
if ( count( $input ) > 0 ) { |
|
2779
|
|
|
if ( $title ) { |
|
2780
|
|
|
$msg = $title . ( count( $input ) > 1 ? 's' : '' ) . ":\n "; |
|
2781
|
|
|
} else { |
|
2782
|
|
|
$msg = ' '; |
|
2783
|
|
|
} |
|
2784
|
|
|
$msg .= implode( $prefix, $input ) . "\n"; |
|
2785
|
|
|
|
|
2786
|
|
|
return $msg; |
|
2787
|
|
|
} |
|
2788
|
|
|
|
|
2789
|
|
|
return ''; |
|
2790
|
|
|
} |
|
2791
|
|
|
|
|
2792
|
|
|
/** |
|
2793
|
|
|
* Generates the parameter descriptions for this module, to be displayed in the |
|
2794
|
|
|
* module's help. |
|
2795
|
|
|
* @deprecated since 1.25 |
|
2796
|
|
|
* @return string|bool |
|
2797
|
|
|
*/ |
|
2798
|
|
|
public function makeHelpMsgParameters() { |
|
2799
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
2800
|
|
|
$params = $this->getFinalParams( ApiBase::GET_VALUES_FOR_HELP ); |
|
2801
|
|
|
if ( $params ) { |
|
2802
|
|
|
$paramsDescription = $this->getFinalParamDescription(); |
|
2803
|
|
|
$msg = ''; |
|
2804
|
|
|
$paramPrefix = "\n" . str_repeat( ' ', 24 ); |
|
2805
|
|
|
$descWordwrap = "\n" . str_repeat( ' ', 28 ); |
|
2806
|
|
|
foreach ( $params as $paramName => $paramSettings ) { |
|
|
|
|
|
|
2807
|
|
|
$desc = isset( $paramsDescription[$paramName] ) ? $paramsDescription[$paramName] : ''; |
|
2808
|
|
|
if ( is_array( $desc ) ) { |
|
2809
|
|
|
$desc = implode( $paramPrefix, $desc ); |
|
2810
|
|
|
} |
|
2811
|
|
|
|
|
2812
|
|
|
// handle shorthand |
|
2813
|
|
|
if ( !is_array( $paramSettings ) ) { |
|
2814
|
|
|
$paramSettings = [ |
|
2815
|
|
|
self::PARAM_DFLT => $paramSettings, |
|
2816
|
|
|
]; |
|
2817
|
|
|
} |
|
2818
|
|
|
|
|
2819
|
|
|
// handle missing type |
|
2820
|
|
View Code Duplication |
if ( !isset( $paramSettings[ApiBase::PARAM_TYPE] ) ) { |
|
2821
|
|
|
$dflt = isset( $paramSettings[ApiBase::PARAM_DFLT] ) |
|
2822
|
|
|
? $paramSettings[ApiBase::PARAM_DFLT] |
|
2823
|
|
|
: null; |
|
2824
|
|
|
if ( is_bool( $dflt ) ) { |
|
2825
|
|
|
$paramSettings[ApiBase::PARAM_TYPE] = 'boolean'; |
|
2826
|
|
|
} elseif ( is_string( $dflt ) || is_null( $dflt ) ) { |
|
2827
|
|
|
$paramSettings[ApiBase::PARAM_TYPE] = 'string'; |
|
2828
|
|
|
} elseif ( is_int( $dflt ) ) { |
|
2829
|
|
|
$paramSettings[ApiBase::PARAM_TYPE] = 'integer'; |
|
2830
|
|
|
} |
|
2831
|
|
|
} |
|
2832
|
|
|
|
|
2833
|
|
|
if ( isset( $paramSettings[self::PARAM_DEPRECATED] ) |
|
2834
|
|
|
&& $paramSettings[self::PARAM_DEPRECATED] |
|
2835
|
|
|
) { |
|
2836
|
|
|
$desc = "DEPRECATED! $desc"; |
|
2837
|
|
|
} |
|
2838
|
|
|
|
|
2839
|
|
|
if ( isset( $paramSettings[self::PARAM_REQUIRED] ) |
|
2840
|
|
|
&& $paramSettings[self::PARAM_REQUIRED] |
|
2841
|
|
|
) { |
|
2842
|
|
|
$desc .= $paramPrefix . 'This parameter is required'; |
|
2843
|
|
|
} |
|
2844
|
|
|
|
|
2845
|
|
|
$type = isset( $paramSettings[self::PARAM_TYPE] ) |
|
2846
|
|
|
? $paramSettings[self::PARAM_TYPE] |
|
2847
|
|
|
: null; |
|
2848
|
|
|
if ( isset( $type ) ) { |
|
2849
|
|
|
$hintPipeSeparated = true; |
|
2850
|
|
|
$multi = isset( $paramSettings[self::PARAM_ISMULTI] ) |
|
2851
|
|
|
? $paramSettings[self::PARAM_ISMULTI] |
|
2852
|
|
|
: false; |
|
2853
|
|
|
if ( $multi ) { |
|
2854
|
|
|
$prompt = 'Values (separate with \'|\'): '; |
|
2855
|
|
|
} else { |
|
2856
|
|
|
$prompt = 'One value: '; |
|
2857
|
|
|
} |
|
2858
|
|
|
|
|
2859
|
|
View Code Duplication |
if ( $type === 'submodule' ) { |
|
2860
|
|
|
if ( isset( $paramSettings[self::PARAM_SUBMODULE_MAP] ) ) { |
|
2861
|
|
|
$type = array_keys( $paramSettings[self::PARAM_SUBMODULE_MAP] ); |
|
2862
|
|
|
} else { |
|
2863
|
|
|
$type = $this->getModuleManager()->getNames( $paramName ); |
|
2864
|
|
|
} |
|
2865
|
|
|
sort( $type ); |
|
2866
|
|
|
} |
|
2867
|
|
|
if ( is_array( $type ) ) { |
|
2868
|
|
|
$choices = []; |
|
2869
|
|
|
$nothingPrompt = ''; |
|
2870
|
|
|
foreach ( $type as $t ) { |
|
2871
|
|
|
if ( $t === '' ) { |
|
2872
|
|
|
$nothingPrompt = 'Can be empty, or '; |
|
2873
|
|
|
} else { |
|
2874
|
|
|
$choices[] = $t; |
|
2875
|
|
|
} |
|
2876
|
|
|
} |
|
2877
|
|
|
$desc .= $paramPrefix . $nothingPrompt . $prompt; |
|
2878
|
|
|
$choicesstring = implode( ', ', $choices ); |
|
2879
|
|
|
$desc .= wordwrap( $choicesstring, 100, $descWordwrap ); |
|
2880
|
|
|
$hintPipeSeparated = false; |
|
2881
|
|
|
} else { |
|
2882
|
|
|
switch ( $type ) { |
|
2883
|
|
|
case 'namespace': |
|
2884
|
|
|
// Special handling because namespaces are |
|
2885
|
|
|
// type-limited, yet they are not given |
|
2886
|
|
|
$desc .= $paramPrefix . $prompt; |
|
2887
|
|
|
$desc .= wordwrap( implode( ', ', MWNamespace::getValidNamespaces() ), |
|
2888
|
|
|
100, $descWordwrap ); |
|
2889
|
|
|
$hintPipeSeparated = false; |
|
2890
|
|
|
break; |
|
2891
|
|
|
case 'limit': |
|
2892
|
|
|
$desc .= $paramPrefix . "No more than {$paramSettings[self::PARAM_MAX]}"; |
|
2893
|
|
|
if ( isset( $paramSettings[self::PARAM_MAX2] ) ) { |
|
2894
|
|
|
$desc .= " ({$paramSettings[self::PARAM_MAX2]} for bots)"; |
|
2895
|
|
|
} |
|
2896
|
|
|
$desc .= ' allowed'; |
|
2897
|
|
|
break; |
|
2898
|
|
|
case 'integer': |
|
2899
|
|
|
$s = $multi ? 's' : ''; |
|
2900
|
|
|
$hasMin = isset( $paramSettings[self::PARAM_MIN] ); |
|
2901
|
|
|
$hasMax = isset( $paramSettings[self::PARAM_MAX] ); |
|
2902
|
|
|
if ( $hasMin || $hasMax ) { |
|
2903
|
|
|
if ( !$hasMax ) { |
|
2904
|
|
|
$intRangeStr = "The value$s must be no less than " . |
|
2905
|
|
|
"{$paramSettings[self::PARAM_MIN]}"; |
|
2906
|
|
|
} elseif ( !$hasMin ) { |
|
2907
|
|
|
$intRangeStr = "The value$s must be no more than " . |
|
2908
|
|
|
"{$paramSettings[self::PARAM_MAX]}"; |
|
2909
|
|
|
} else { |
|
2910
|
|
|
$intRangeStr = "The value$s must be between " . |
|
2911
|
|
|
"{$paramSettings[self::PARAM_MIN]} and {$paramSettings[self::PARAM_MAX]}"; |
|
2912
|
|
|
} |
|
2913
|
|
|
|
|
2914
|
|
|
$desc .= $paramPrefix . $intRangeStr; |
|
2915
|
|
|
} |
|
2916
|
|
|
break; |
|
2917
|
|
|
case 'upload': |
|
2918
|
|
|
$desc .= $paramPrefix . 'Must be posted as a file upload using multipart/form-data'; |
|
2919
|
|
|
break; |
|
2920
|
|
|
} |
|
2921
|
|
|
} |
|
2922
|
|
|
|
|
2923
|
|
|
if ( $multi ) { |
|
2924
|
|
|
if ( $hintPipeSeparated ) { |
|
2925
|
|
|
$desc .= $paramPrefix . "Separate values with '|'"; |
|
2926
|
|
|
} |
|
2927
|
|
|
|
|
2928
|
|
|
$isArray = is_array( $type ); |
|
2929
|
|
|
if ( !$isArray |
|
2930
|
|
|
|| $isArray && count( $type ) > self::LIMIT_SML1 |
|
2931
|
|
|
) { |
|
2932
|
|
|
$desc .= $paramPrefix . 'Maximum number of values ' . |
|
2933
|
|
|
self::LIMIT_SML1 . ' (' . self::LIMIT_SML2 . ' for bots)'; |
|
2934
|
|
|
} |
|
2935
|
|
|
} |
|
2936
|
|
|
} |
|
2937
|
|
|
|
|
2938
|
|
|
$default = isset( $paramSettings[self::PARAM_DFLT] ) ? $paramSettings[self::PARAM_DFLT] : null; |
|
2939
|
|
|
if ( !is_null( $default ) && $default !== false ) { |
|
2940
|
|
|
$desc .= $paramPrefix . "Default: $default"; |
|
2941
|
|
|
} |
|
2942
|
|
|
|
|
2943
|
|
|
$msg .= sprintf( " %-19s - %s\n", $this->encodeParamName( $paramName ), $desc ); |
|
2944
|
|
|
} |
|
2945
|
|
|
|
|
2946
|
|
|
return $msg; |
|
2947
|
|
|
} |
|
2948
|
|
|
|
|
2949
|
|
|
return false; |
|
2950
|
|
|
} |
|
2951
|
|
|
|
|
2952
|
|
|
/** |
|
2953
|
|
|
* @deprecated since 1.25, always returns empty string |
|
2954
|
|
|
* @param IDatabase|bool $db |
|
2955
|
|
|
* @return string |
|
2956
|
|
|
*/ |
|
2957
|
|
|
public function getModuleProfileName( $db = false ) { |
|
2958
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
2959
|
|
|
return ''; |
|
2960
|
|
|
} |
|
2961
|
|
|
|
|
2962
|
|
|
/** |
|
2963
|
|
|
* @deprecated since 1.25 |
|
2964
|
|
|
*/ |
|
2965
|
|
|
public function profileIn() { |
|
2966
|
|
|
// No wfDeprecated() yet because extensions call this and might need to |
|
2967
|
|
|
// keep doing so for BC. |
|
2968
|
|
|
} |
|
2969
|
|
|
|
|
2970
|
|
|
/** |
|
2971
|
|
|
* @deprecated since 1.25 |
|
2972
|
|
|
*/ |
|
2973
|
|
|
public function profileOut() { |
|
2974
|
|
|
// No wfDeprecated() yet because extensions call this and might need to |
|
2975
|
|
|
// keep doing so for BC. |
|
2976
|
|
|
} |
|
2977
|
|
|
|
|
2978
|
|
|
/** |
|
2979
|
|
|
* @deprecated since 1.25 |
|
2980
|
|
|
*/ |
|
2981
|
|
|
public function safeProfileOut() { |
|
2982
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
2983
|
|
|
} |
|
2984
|
|
|
|
|
2985
|
|
|
/** |
|
2986
|
|
|
* @deprecated since 1.25, always returns 0 |
|
2987
|
|
|
* @return float |
|
2988
|
|
|
*/ |
|
2989
|
|
|
public function getProfileTime() { |
|
2990
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
2991
|
|
|
return 0; |
|
2992
|
|
|
} |
|
2993
|
|
|
|
|
2994
|
|
|
/** |
|
2995
|
|
|
* @deprecated since 1.25 |
|
2996
|
|
|
*/ |
|
2997
|
|
|
public function profileDBIn() { |
|
2998
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
2999
|
|
|
} |
|
3000
|
|
|
|
|
3001
|
|
|
/** |
|
3002
|
|
|
* @deprecated since 1.25 |
|
3003
|
|
|
*/ |
|
3004
|
|
|
public function profileDBOut() { |
|
3005
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
3006
|
|
|
} |
|
3007
|
|
|
|
|
3008
|
|
|
/** |
|
3009
|
|
|
* @deprecated since 1.25, always returns 0 |
|
3010
|
|
|
* @return float |
|
3011
|
|
|
*/ |
|
3012
|
|
|
public function getProfileDBTime() { |
|
3013
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
3014
|
|
|
return 0; |
|
3015
|
|
|
} |
|
3016
|
|
|
|
|
3017
|
|
|
/** |
|
3018
|
|
|
* Get the result data array (read-only) |
|
3019
|
|
|
* @deprecated since 1.25, use $this->getResult() methods instead |
|
3020
|
|
|
* @return array |
|
3021
|
|
|
*/ |
|
3022
|
|
|
public function getResultData() { |
|
3023
|
|
|
wfDeprecated( __METHOD__, '1.25' ); |
|
3024
|
|
|
return $this->getResult()->getData(); |
|
3025
|
|
|
} |
|
3026
|
|
|
|
|
3027
|
|
|
/** |
|
3028
|
|
|
* Call wfTransactionalTimeLimit() if this request was POSTed |
|
3029
|
|
|
* @since 1.26 |
|
3030
|
|
|
*/ |
|
3031
|
|
|
protected function useTransactionalTimeLimit() { |
|
3032
|
|
|
if ( $this->getRequest()->wasPosted() ) { |
|
3033
|
|
|
wfTransactionalTimeLimit(); |
|
3034
|
|
|
} |
|
3035
|
|
|
} |
|
3036
|
|
|
|
|
3037
|
|
|
/**@}*/ |
|
3038
|
|
|
} |
|
3039
|
|
|
|
|
3040
|
|
|
/** |
|
3041
|
|
|
* For really cool vim folding this needs to be at the end: |
|
3042
|
|
|
* vim: foldmarker=@{,@} foldmethod=marker |
|
3043
|
|
|
*/ |
|
3044
|
|
|
|
Only declaring a single property per statement allows you to later on add doc comments more easily.
It is also recommended by PSR2, so it is a common style that many people expect.