00001 <?php
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00041 abstract class ApiBase {
00042
00043
00044
00045 const PARAM_DFLT = 0;
00046 const PARAM_ISMULTI = 1;
00047 const PARAM_TYPE = 2;
00048 const PARAM_MAX = 3;
00049 const PARAM_MAX2 = 4;
00050 const PARAM_MIN = 5;
00051 const PARAM_ALLOW_DUPLICATES = 6;
00052
00053 const LIMIT_BIG1 = 500;
00054 const LIMIT_BIG2 = 5000;
00055 const LIMIT_SML1 = 50;
00056 const LIMIT_SML2 = 500;
00057
00058 private $mMainModule, $mModuleName, $mModulePrefix;
00059
00066 public function __construct($mainModule, $moduleName, $modulePrefix = '') {
00067 $this->mMainModule = $mainModule;
00068 $this->mModuleName = $moduleName;
00069 $this->mModulePrefix = $modulePrefix;
00070 }
00071
00072
00073
00074
00075
00092 public abstract function execute();
00093
00100 public abstract function getVersion();
00101
00106 public function getModuleName() {
00107 return $this->mModuleName;
00108 }
00109
00114 public function getModulePrefix() {
00115 return $this->mModulePrefix;
00116 }
00117
00122 public function getModuleProfileName($db = false) {
00123 if ($db)
00124 return 'API:' . $this->mModuleName . '-DB';
00125 else
00126 return 'API:' . $this->mModuleName;
00127 }
00128
00133 public function getMain() {
00134 return $this->mMainModule;
00135 }
00136
00142 public function isMain() {
00143 return $this === $this->mMainModule;
00144 }
00145
00150 public function getResult() {
00151
00152
00153 if ($this->isMain())
00154 ApiBase :: dieDebug(__METHOD__, 'base method was called on main module. ');
00155 return $this->getMain()->getResult();
00156 }
00157
00162 public function getResultData() {
00163 return $this->getResult()->getData();
00164 }
00165
00173 public function setWarning($warning) {
00174 $data = $this->getResult()->getData();
00175 if(isset($data['warnings'][$this->getModuleName()]))
00176 {
00177 # Don't add duplicate warnings
00178 $warn_regex = preg_quote($warning, '/');
00179 if(preg_match("/{$warn_regex}(\\n|$)/", $data['warnings'][$this->getModuleName()]['*']))
00180 return;
00181 $oldwarning = $data['warnings'][$this->getModuleName()]['*'];
00182 # If there is a warning already, append it to the existing one
00183 $warning = "$oldwarning\n$warning";
00184 $this->getResult()->unsetValue('warnings', $this->getModuleName());
00185 }
00186 $msg = array();
00187 ApiResult :: setContent($msg, $warning);
00188 $this->getResult()->disableSizeCheck();
00189 $this->getResult()->addValue('warnings', $this->getModuleName(), $msg);
00190 $this->getResult()->enableSizeCheck();
00191 }
00192
00199 public function getCustomPrinter() {
00200 return null;
00201 }
00202
00207 public function makeHelpMsg() {
00208
00209 static $lnPrfx = "\n ";
00210
00211 $msg = $this->getDescription();
00212
00213 if ($msg !== false) {
00214
00215 if (!is_array($msg))
00216 $msg = array (
00217 $msg
00218 );
00219 $msg = $lnPrfx . implode($lnPrfx, $msg) . "\n";
00220
00221 if ($this->isReadMode())
00222 $msg .= "\nThis module requires read rights.";
00223 if ($this->isWriteMode())
00224 $msg .= "\nThis module requires write rights.";
00225 if ($this->mustBePosted())
00226 $msg .= "\nThis module only accepts POST requests.";
00227 if ($this->isReadMode() || $this->isWriteMode() ||
00228 $this->mustBePosted())
00229 $msg .= "\n";
00230
00231
00232 $paramsMsg = $this->makeHelpMsgParameters();
00233 if ($paramsMsg !== false) {
00234 $msg .= "Parameters:\n$paramsMsg";
00235 }
00236
00237
00238 $examples = $this->getExamples();
00239 if ($examples !== false) {
00240 if (!is_array($examples))
00241 $examples = array (
00242 $examples
00243 );
00244 $msg .= 'Example' . (count($examples) > 1 ? 's' : '') . ":\n ";
00245 $msg .= implode($lnPrfx, $examples) . "\n";
00246 }
00247
00248 if ($this->getMain()->getShowVersions()) {
00249 $versions = $this->getVersion();
00250 $pattern = '/(\$.*) ([0-9a-z_]+\.php) (.*\$)/i';
00251 $replacement = '\\0' . "\n " . 'http://svn.wikimedia.org/viewvc/mediawiki/trunk/phase3/includes/api/\\2';
00252
00253 if (is_array($versions)) {
00254 foreach ($versions as &$v)
00255 $v = preg_replace($pattern, $replacement, $v);
00256 $versions = implode("\n ", $versions);
00257 }
00258 else
00259 $versions = preg_replace($pattern, $replacement, $versions);
00260
00261 $msg .= "Version:\n $versions\n";
00262 }
00263 }
00264
00265 return $msg;
00266 }
00267
00273 public function makeHelpMsgParameters() {
00274 $params = $this->getFinalParams();
00275 if ($params !== false) {
00276
00277 $paramsDescription = $this->getFinalParamDescription();
00278 $msg = '';
00279 $paramPrefix = "\n" . str_repeat(' ', 19);
00280 foreach ($params as $paramName => $paramSettings) {
00281 $desc = isset ($paramsDescription[$paramName]) ? $paramsDescription[$paramName] : '';
00282 if (is_array($desc))
00283 $desc = implode($paramPrefix, $desc);
00284
00285 $type = isset($paramSettings[self :: PARAM_TYPE])? $paramSettings[self :: PARAM_TYPE] : null;
00286 if (isset ($type)) {
00287 if (isset ($paramSettings[self :: PARAM_ISMULTI]))
00288 $prompt = 'Values (separate with \'|\'): ';
00289 else
00290 $prompt = 'One value: ';
00291
00292 if (is_array($type)) {
00293 $choices = array();
00294 $nothingPrompt = false;
00295 foreach ($type as $t)
00296 if ($t === '')
00297 $nothingPrompt = 'Can be empty, or ';
00298 else
00299 $choices[] = $t;
00300 $desc .= $paramPrefix . $nothingPrompt . $prompt . implode(', ', $choices);
00301 } else {
00302 switch ($type) {
00303 case 'namespace':
00304
00305 $desc .= $paramPrefix . $prompt . implode(', ', ApiBase :: getValidNamespaces());
00306 break;
00307 case 'limit':
00308 $desc .= $paramPrefix . "No more than {$paramSettings[self :: PARAM_MAX]} ({$paramSettings[self :: PARAM_MAX2]} for bots) allowed.";
00309 break;
00310 case 'integer':
00311 $hasMin = isset($paramSettings[self :: PARAM_MIN]);
00312 $hasMax = isset($paramSettings[self :: PARAM_MAX]);
00313 if ($hasMin || $hasMax) {
00314 if (!$hasMax)
00315 $intRangeStr = "The value must be no less than {$paramSettings[self :: PARAM_MIN]}";
00316 elseif (!$hasMin)
00317 $intRangeStr = "The value must be no more than {$paramSettings[self :: PARAM_MAX]}";
00318 else
00319 $intRangeStr = "The value must be between {$paramSettings[self :: PARAM_MIN]} and {$paramSettings[self :: PARAM_MAX]}";
00320
00321 $desc .= $paramPrefix . $intRangeStr;
00322 }
00323 break;
00324 }
00325 }
00326 }
00327
00328 $default = is_array($paramSettings) ? (isset ($paramSettings[self :: PARAM_DFLT]) ? $paramSettings[self :: PARAM_DFLT] : null) : $paramSettings;
00329 if (!is_null($default) && $default !== false)
00330 $desc .= $paramPrefix . "Default: $default";
00331
00332 $msg .= sprintf(" %-14s - %s\n", $this->encodeParamName($paramName), $desc);
00333 }
00334 return $msg;
00335
00336 } else
00337 return false;
00338 }
00339
00344 protected function getDescription() {
00345 return false;
00346 }
00347
00352 protected function getExamples() {
00353 return false;
00354 }
00355
00363 protected function getAllowedParams() {
00364 return false;
00365 }
00366
00373 protected function getParamDescription() {
00374 return false;
00375 }
00376
00382 public function getFinalParams() {
00383 $params = $this->getAllowedParams();
00384 wfRunHooks('APIGetAllowedParams', array(&$this, &$params));
00385 return $params;
00386 }
00387
00393 public function getFinalParamDescription() {
00394 $desc = $this->getParamDescription();
00395 wfRunHooks('APIGetParamDescription', array(&$this, &$desc));
00396 return $desc;
00397 }
00398
00405 public function encodeParamName($paramName) {
00406 return $this->mModulePrefix . $paramName;
00407 }
00408
00418 public function extractRequestParams($parseMaxLimit = true) {
00419 $params = $this->getFinalParams();
00420 $results = array ();
00421
00422 foreach ($params as $paramName => $paramSettings)
00423 $results[$paramName] = $this->getParameterFromSettings($paramName, $paramSettings, $parseMaxLimit);
00424
00425 return $results;
00426 }
00427
00434 protected function getParameter($paramName, $parseMaxLimit = true) {
00435 $params = $this->getFinalParams();
00436 $paramSettings = $params[$paramName];
00437 return $this->getParameterFromSettings($paramName, $paramSettings, $parseMaxLimit);
00438 }
00439
00444 public function requireOnlyOneParameter($params) {
00445 $required = func_get_args();
00446 array_shift($required);
00447
00448 $intersection = array_intersect(array_keys(array_filter($params,
00449 create_function('$x', 'return !is_null($x);')
00450 )), $required);
00451 if (count($intersection) > 1) {
00452 $this->dieUsage('The parameters '.implode(', ', $intersection).' can not be used together', 'invalidparammix');
00453 } elseif (count($intersection) == 0) {
00454 $this->dieUsage('One of the parameters '.implode(', ', $required).' is required', 'missingparam');
00455 }
00456 }
00457
00463 public static function getValidNamespaces() {
00464 static $mValidNamespaces = null;
00465 if (is_null($mValidNamespaces)) {
00466
00467 global $wgContLang;
00468 $mValidNamespaces = array ();
00469 foreach (array_keys($wgContLang->getNamespaces()) as $ns) {
00470 if ($ns >= 0)
00471 $mValidNamespaces[] = $ns;
00472 }
00473 }
00474 return $mValidNamespaces;
00475 }
00476
00486 protected function getParameterFromSettings($paramName, $paramSettings, $parseMaxLimit) {
00487
00488
00489 $encParamName = $this->encodeParamName($paramName);
00490
00491 if (!is_array($paramSettings)) {
00492 $default = $paramSettings;
00493 $multi = false;
00494 $type = gettype($paramSettings);
00495 $dupes = false;
00496 } else {
00497 $default = isset ($paramSettings[self :: PARAM_DFLT]) ? $paramSettings[self :: PARAM_DFLT] : null;
00498 $multi = isset ($paramSettings[self :: PARAM_ISMULTI]) ? $paramSettings[self :: PARAM_ISMULTI] : false;
00499 $type = isset ($paramSettings[self :: PARAM_TYPE]) ? $paramSettings[self :: PARAM_TYPE] : null;
00500 $dupes = isset ($paramSettings[self:: PARAM_ALLOW_DUPLICATES]) ? $paramSettings[self :: PARAM_ALLOW_DUPLICATES] : false;
00501
00502
00503 if (!isset ($type)) {
00504 if (isset ($default))
00505 $type = gettype($default);
00506 else
00507 $type = 'NULL';
00508 }
00509 }
00510
00511 if ($type == 'boolean') {
00512 if (isset ($default) && $default !== false) {
00513
00514 ApiBase :: dieDebug(__METHOD__, "Boolean param $encParamName's default is set to '$default'");
00515 }
00516
00517 $value = $this->getMain()->getRequest()->getCheck($encParamName);
00518 } else {
00519 $value = $this->getMain()->getRequest()->getVal($encParamName, $default);
00520
00521 if (isset ($value) && $type == 'namespace')
00522 $type = ApiBase :: getValidNamespaces();
00523 }
00524
00525 if (isset ($value) && ($multi || is_array($type)))
00526 $value = $this->parseMultiValue($encParamName, $value, $multi, is_array($type) ? $type : null);
00527
00528
00529
00530 if (isset ($value)) {
00531 if (!is_array($type)) {
00532 switch ($type) {
00533 case 'NULL' :
00534 break;
00535 case 'string' :
00536 break;
00537 case 'integer' :
00538
00539 $value = is_array($value) ? array_map('intval', $value) : intval($value);
00540 $min = isset ($paramSettings[self :: PARAM_MIN]) ? $paramSettings[self :: PARAM_MIN] : null;
00541 $max = isset ($paramSettings[self :: PARAM_MAX]) ? $paramSettings[self :: PARAM_MAX] : null;
00542
00543 if (!is_null($min) || !is_null($max)) {
00544 $values = is_array($value) ? $value : array($value);
00545 foreach ($values as $v) {
00546 $this->validateLimit($paramName, $v, $min, $max);
00547 }
00548 }
00549 break;
00550 case 'limit' :
00551 if (!isset ($paramSettings[self :: PARAM_MAX]) || !isset ($paramSettings[self :: PARAM_MAX2]))
00552 ApiBase :: dieDebug(__METHOD__, "MAX1 or MAX2 are not defined for the limit $encParamName");
00553 if ($multi)
00554 ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName");
00555 $min = isset ($paramSettings[self :: PARAM_MIN]) ? $paramSettings[self :: PARAM_MIN] : 0;
00556 if( $value == 'max' ) {
00557 if( $parseMaxLimit ) {
00558 $value = $this->getMain()->canApiHighLimits() ? $paramSettings[self :: PARAM_MAX2] : $paramSettings[self :: PARAM_MAX];
00559 $this->getResult()->addValue( 'limits', $this->getModuleName(), $value );
00560 $this->validateLimit($paramName, $value, $min, $paramSettings[self :: PARAM_MAX], $paramSettings[self :: PARAM_MAX2]);
00561 }
00562 }
00563 else {
00564 $value = intval($value);
00565 $this->validateLimit($paramName, $value, $min, $paramSettings[self :: PARAM_MAX], $paramSettings[self :: PARAM_MAX2]);
00566 }
00567 break;
00568 case 'boolean' :
00569 if ($multi)
00570 ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName");
00571 break;
00572 case 'timestamp' :
00573 if ($multi)
00574 ApiBase :: dieDebug(__METHOD__, "Multi-values not supported for $encParamName");
00575 $value = wfTimestamp(TS_UNIX, $value);
00576 if ($value === 0)
00577 $this->dieUsage("Invalid value '$value' for timestamp parameter $encParamName", "badtimestamp_{$encParamName}");
00578 $value = wfTimestamp(TS_MW, $value);
00579 break;
00580 case 'user' :
00581 $title = Title::makeTitleSafe( NS_USER, $value );
00582 if ( is_null( $title ) )
00583 $this->dieUsage("Invalid value for user parameter $encParamName", "baduser_{$encParamName}");
00584 $value = $title->getText();
00585 break;
00586 default :
00587 ApiBase :: dieDebug(__METHOD__, "Param $encParamName's type is unknown - $type");
00588 }
00589 }
00590
00591
00592 if (is_array($value) && !$dupes)
00593 $value = array_unique($value);
00594 }
00595
00596 return $value;
00597 }
00598
00612 protected function parseMultiValue($valueName, $value, $allowMultiple, $allowedValues) {
00613 if( trim($value) === "" && $allowMultiple)
00614 return array();
00615 $sizeLimit = $this->mMainModule->canApiHighLimits() ? self::LIMIT_SML2 : self::LIMIT_SML1;
00616 $valuesList = explode('|', $value, $sizeLimit + 1);
00617 if( self::truncateArray($valuesList, $sizeLimit) ) {
00618 $this->setWarning("Too many values supplied for parameter '$valueName': the limit is $sizeLimit");
00619 }
00620 if (!$allowMultiple && count($valuesList) != 1) {
00621 $possibleValues = is_array($allowedValues) ? "of '" . implode("', '", $allowedValues) . "'" : '';
00622 $this->dieUsage("Only one $possibleValues is allowed for parameter '$valueName'", "multival_$valueName");
00623 }
00624 if (is_array($allowedValues)) {
00625 # Check for unknown values
00626 $unknown = array_diff($valuesList, $allowedValues);
00627 if(count($unknown))
00628 {
00629 if($allowMultiple)
00630 {
00631 $s = count($unknown) > 1 ? "s" : "";
00632 $vals = implode(", ", $unknown);
00633 $this->setWarning("Unrecognized value$s for parameter '$valueName': $vals");
00634 }
00635 else
00636 $this->dieUsage("Unrecognized value for parameter '$valueName': {$valuesList[0]}", "unknown_$valueName");
00637 }
00638 # Now throw them out
00639 $valuesList = array_intersect($valuesList, $allowedValues);
00640 }
00641
00642 return $allowMultiple ? $valuesList : $valuesList[0];
00643 }
00644
00654 function validateLimit($paramName, $value, $min, $max, $botMax = null) {
00655 if (!is_null($min) && $value < $min) {
00656 $this->dieUsage($this->encodeParamName($paramName) . " may not be less than $min (set to $value)", $paramName);
00657 }
00658
00659
00660 if ($this->getMain()->isInternalMode())
00661 return;
00662
00663
00664
00665 if (!is_null($max) && $value > $max) {
00666 if (!is_null($botMax) && $this->getMain()->canApiHighLimits()) {
00667 if ($value > $botMax) {
00668 $this->dieUsage($this->encodeParamName($paramName) . " may not be over $botMax (set to $value) for bots or sysops", $paramName);
00669 }
00670 } else {
00671 $this->dieUsage($this->encodeParamName($paramName) . " may not be over $max (set to $value) for users", $paramName);
00672 }
00673 }
00674 }
00675
00682 public static function truncateArray(&$arr, $limit)
00683 {
00684 $modified = false;
00685 while(count($arr) > $limit)
00686 {
00687 $junk = array_pop($arr);
00688 $modified = true;
00689 }
00690 return $modified;
00691 }
00692
00699 public function dieUsage($description, $errorCode, $httpRespCode = 0) {
00700 wfProfileClose();
00701 throw new UsageException($description, $this->encodeParamName($errorCode), $httpRespCode);
00702 }
00703
00707 public static $messageMap = array(
00708
00709 'unknownerror' => array('code' => 'unknownerror', 'info' => "Unknown error: ``\$1''"),
00710 'unknownerror-nocode' => array('code' => 'unknownerror', 'info' => 'Unknown error'),
00711
00712
00713 'ns-specialprotected' => array('code' => 'unsupportednamespace', 'info' => "Pages in the Special namespace can't be edited"),
00714 'protectedinterface' => array('code' => 'protectednamespace-interface', 'info' => "You're not allowed to edit interface messages"),
00715 'namespaceprotected' => array('code' => 'protectednamespace', 'info' => "You're not allowed to edit pages in the ``\$1'' namespace"),
00716 'customcssjsprotected' => array('code' => 'customcssjsprotected', 'info' => "You're not allowed to edit custom CSS and JavaScript pages"),
00717 'cascadeprotected' => array('code' => 'cascadeprotected', 'info' =>"The page you're trying to edit is protected because it's included in a cascade-protected page"),
00718 'protectedpagetext' => array('code' => 'protectedpage', 'info' => "The ``\$1'' right is required to edit this page"),
00719 'protect-cantedit' => array('code' => 'cantedit', 'info' => "You can't protect this page because you can't edit it"),
00720 'badaccess-group0' => array('code' => 'permissiondenied', 'info' => "Permission denied"),
00721 'badaccess-groups' => array('code' => 'permissiondenied', 'info' => "Permission denied"),
00722 'titleprotected' => array('code' => 'protectedtitle', 'info' => "This title has been protected from creation"),
00723 'nocreate-loggedin' => array('code' => 'cantcreate', 'info' => "You don't have permission to create new pages"),
00724 'nocreatetext' => array('code' => 'cantcreate-anon', 'info' => "Anonymous users can't create new pages"),
00725 'movenologintext' => array('code' => 'cantmove-anon', 'info' => "Anonymous users can't move pages"),
00726 'movenotallowed' => array('code' => 'cantmove', 'info' => "You don't have permission to move pages"),
00727 'confirmedittext' => array('code' => 'confirmemail', 'info' => "You must confirm your e-mail address before you can edit"),
00728 'blockedtext' => array('code' => 'blocked', 'info' => "You have been blocked from editing"),
00729 'autoblockedtext' => array('code' => 'autoblocked', 'info' => "Your IP address has been blocked automatically, because it was used by a blocked user"),
00730
00731
00732 'actionthrottledtext' => array('code' => 'ratelimited', 'info' => "You've exceeded your rate limit. Please wait some time and try again"),
00733 'alreadyrolled' => array('code' => 'alreadyrolled', 'info' => "The page you tried to rollback was already rolled back"),
00734 'cantrollback' => array('code' => 'onlyauthor', 'info' => "The page you tried to rollback only has one author"),
00735 'readonlytext' => array('code' => 'readonly', 'info' => "The wiki is currently in read-only mode"),
00736 'sessionfailure' => array('code' => 'badtoken', 'info' => "Invalid token"),
00737 'cannotdelete' => array('code' => 'cantdelete', 'info' => "Couldn't delete ``\$1''. Maybe it was deleted already by someone else"),
00738 'notanarticle' => array('code' => 'missingtitle', 'info' => "The page you requested doesn't exist"),
00739 'selfmove' => array('code' => 'selfmove', 'info' => "Can't move a page to itself"),
00740 'immobile_namespace' => array('code' => 'immobilenamespace', 'info' => "You tried to move pages from or to a namespace that is protected from moving"),
00741 'articleexists' => array('code' => 'articleexists', 'info' => "The destination article already exists and is not a redirect to the source article"),
00742 'protectedpage' => array('code' => 'protectedpage', 'info' => "You don't have permission to perform this move"),
00743 'hookaborted' => array('code' => 'hookaborted', 'info' => "The modification you tried to make was aborted by an extension hook"),
00744 'cantmove-titleprotected' => array('code' => 'protectedtitle', 'info' => "The destination article has been protected from creation"),
00745 'imagenocrossnamespace' => array('code' => 'nonfilenamespace', 'info' => "Can't move a file to a non-file namespace"),
00746 'imagetypemismatch' => array('code' => 'filetypemismatch', 'info' => "The new file extension doesn't match its type"),
00747
00748
00749 'ip_range_invalid' => array('code' => 'invalidrange', 'info' => "Invalid IP range"),
00750 'range_block_disabled' => array('code' => 'rangedisabled', 'info' => "Blocking IP ranges has been disabled"),
00751 'nosuchusershort' => array('code' => 'nosuchuser', 'info' => "The user you specified doesn't exist"),
00752 'badipaddress' => array('code' => 'invalidip', 'info' => "Invalid IP address specified"),
00753 'ipb_expiry_invalid' => array('code' => 'invalidexpiry', 'info' => "Invalid expiry time"),
00754 'ipb_already_blocked' => array('code' => 'alreadyblocked', 'info' => "The user you tried to block was already blocked"),
00755 'ipb_blocked_as_range' => array('code' => 'blockedasrange', 'info' => "IP address ``\$1'' was blocked as part of range ``\$2''. You can't unblock the IP invidually, but you can unblock the range as a whole."),
00756 'ipb_cant_unblock' => array('code' => 'cantunblock', 'info' => "The block you specified was not found. It may have been unblocked already"),
00757 'mailnologin' => array('code' => 'cantsend', 'info' => "You're not logged in or you don't have a confirmed e-mail address, so you can't send e-mail"),
00758 'usermaildisabled' => array('code' => 'usermaildisabled', 'info' => "User email has been disabled"),
00759 'blockedemailuser' => array('code' => 'blockedfrommail', 'info' => "You have been blocked from sending e-mail"),
00760 'notarget' => array('code' => 'notarget', 'info' => "You have not specified a valid target for this action"),
00761 'noemail' => array('code' => 'noemail', 'info' => "The user has not specified a valid e-mail address, or has chosen not to receive e-mail from other users"),
00762 'rcpatroldisabled' => array('code' => 'patroldisabled', 'info' => "Patrolling is disabled on this wiki"),
00763 'markedaspatrollederror-noautopatrol' => array('code' => 'noautopatrol', 'info' => "You don't have permission to patrol your own changes"),
00764 'delete-toobig' => array('code' => 'bigdelete', 'info' => "You can't delete this page because it has more than \$1 revisions"),
00765 'movenotallowedfile' => array('code' => 'cantmovefile', 'info' => "You don't have permission to move files"),
00766
00767
00768 'readrequired' => array('code' => 'readapidenied', 'info' => "You need read permission to use this module"),
00769 'writedisabled' => array('code' => 'noapiwrite', '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"),
00770 'writerequired' => array('code' => 'writeapidenied', 'info' => "You're not allowed to edit this wiki through the API"),
00771 'missingparam' => array('code' => 'no$1', 'info' => "The \$1 parameter must be set"),
00772 'invalidtitle' => array('code' => 'invalidtitle', 'info' => "Bad title ``\$1''"),
00773 'nosuchpageid' => array('code' => 'nosuchpageid', 'info' => "There is no page with ID \$1"),
00774 'nosuchrevid' => array('code' => 'nosuchrevid', 'info' => "There is no revision with ID \$1"),
00775 'invaliduser' => array('code' => 'invaliduser', 'info' => "Invalid username ``\$1''"),
00776 'invalidexpiry' => array('code' => 'invalidexpiry', 'info' => "Invalid expiry time ``\$1''"),
00777 'pastexpiry' => array('code' => 'pastexpiry', 'info' => "Expiry time ``\$1'' is in the past"),
00778 'create-titleexists' => array('code' => 'create-titleexists', 'info' => "Existing titles can't be protected with 'create'"),
00779 'missingtitle-createonly' => array('code' => 'missingtitle-createonly', 'info' => "Missing titles can only be protected with 'create'"),
00780 'cantblock' => array('code' => 'cantblock', 'info' => "You don't have permission to block users"),
00781 'canthide' => array('code' => 'canthide', 'info' => "You don't have permission to hide user names from the block log"),
00782 'cantblock-email' => array('code' => 'cantblock-email', 'info' => "You don't have permission to block users from sending e-mail through the wiki"),
00783 'unblock-notarget' => array('code' => 'notarget', 'info' => "Either the id or the user parameter must be set"),
00784 'unblock-idanduser' => array('code' => 'idanduser', 'info' => "The id and user parameters can't be used together"),
00785 'cantunblock' => array('code' => 'permissiondenied', 'info' => "You don't have permission to unblock users"),
00786 'cannotundelete' => array('code' => 'cantundelete', 'info' => "Couldn't undelete: the requested revisions may not exist, or may have been undeleted already"),
00787 'permdenied-undelete' => array('code' => 'permissiondenied', 'info' => "You don't have permission to restore deleted revisions"),
00788 'createonly-exists' => array('code' => 'articleexists', 'info' => "The article you tried to create has been created already"),
00789 'nocreate-missing' => array('code' => 'missingtitle', 'info' => "The article you tried to edit doesn't exist"),
00790 'nosuchrcid' => array('code' => 'nosuchrcid', 'info' => "There is no change with rcid ``\$1''"),
00791 'cantpurge' => array('code' => 'cantpurge', 'info' => "Only users with the 'purge' right can purge pages via the API"),
00792 'protect-invalidaction' => array('code' => 'protect-invalidaction', 'info' => "Invalid protection type ``\$1''"),
00793 'protect-invalidlevel' => array('code' => 'protect-invalidlevel', 'info' => "Invalid protection level ``\$1''"),
00794 'toofewexpiries' => array('code' => 'toofewexpiries', 'info' => "\$1 expiry timestamps were provided where \$2 were needed"),
00795 'cantimport' => array('code' => 'cantimport', 'info' => "You don't have permission to import pages"),
00796 'cantimport-upload' => array('code' => 'cantimport-upload', 'info' => "You don't have permission to import uploaded pages"),
00797 'importnofile' => array('code' => 'nofile', 'info' => "You didn't upload a file"),
00798 'importuploaderrorsize' => array('code' => 'filetoobig', 'info' => 'The file you uploaded is bigger than the maximum upload size'),
00799 'importuploaderrorpartial' => array('code' => 'partialupload', 'info' => 'The file was only partially uploaded'),
00800 'importuploaderrortemp' => array('code' => 'notempdir', 'info' => 'The temporary upload directory is missing'),
00801 'importcantopen' => array('code' => 'cantopenfile', 'info' => "Couldn't open the uploaded file"),
00802 'import-noarticle' => array('code' => 'badinterwiki', 'info' => 'Invalid interwiki title specified'),
00803 'importbadinterwiki' => array('code' => 'badinterwiki', 'info' => 'Invalid interwiki title specified'),
00804 'import-unknownerror' => array('code' => 'import-unknownerror', 'info' => "Unknown error on import: ``\$1''"),
00805
00806
00807 'noimageredirect-anon' => array('code' => 'noimageredirect-anon', 'info' => "Anonymous users can't create image redirects"),
00808 'noimageredirect-logged' => array('code' => 'noimageredirect', 'info' => "You don't have permission to create image redirects"),
00809 'spamdetected' => array('code' => 'spamdetected', 'info' => "Your edit was refused because it contained a spam fragment: ``\$1''"),
00810 'filtered' => array('code' => 'filtered', 'info' => "The filter callback function refused your edit"),
00811 'contenttoobig' => array('code' => 'contenttoobig', 'info' => "The content you supplied exceeds the article size limit of \$1 kilobytes"),
00812 'noedit-anon' => array('code' => 'noedit-anon', 'info' => "Anonymous users can't edit pages"),
00813 'noedit' => array('code' => 'noedit', 'info' => "You don't have permission to edit pages"),
00814 'wasdeleted' => array('code' => 'pagedeleted', 'info' => "The page has been deleted since you fetched its timestamp"),
00815 'blankpage' => array('code' => 'emptypage', 'info' => "Creating new, empty pages is not allowed"),
00816 'editconflict' => array('code' => 'editconflict', 'info' => "Edit conflict detected"),
00817 'hashcheckfailed' => array('code' => 'badmd5', 'info' => "The supplied MD5 hash was incorrect"),
00818 'missingtext' => array('code' => 'notext', 'info' => "One of the text, appendtext, prependtext and undo parameters must be set"),
00819 'emptynewsection' => array('code' => 'emptynewsection', 'info' => 'Creating empty new sections is not possible.'),
00820 'revwrongpage' => array('code' => 'revwrongpage', 'info' => "r\$1 is not a revision of ``\$2''"),
00821 'undo-failure' => array('code' => 'undofailure', 'info' => 'Undo failed due to conflicting intermediate edits'),
00822 );
00823
00828 public function dieUsageMsg($error) {
00829 $parsed = $this->parseMsg($error);
00830 $this->dieUsage($parsed['info'], $parsed['code']);
00831 }
00832
00838 public function parseMsg($error) {
00839 $key = array_shift($error);
00840 if(isset(self::$messageMap[$key]))
00841 return array( 'code' =>
00842 wfMsgReplaceArgs(self::$messageMap[$key]['code'], $error),
00843 'info' =>
00844 wfMsgReplaceArgs(self::$messageMap[$key]['info'], $error)
00845 );
00846
00847 return $this->parseMsg(array('unknownerror', $key));
00848 }
00849
00855 protected static function dieDebug($method, $message) {
00856 wfDebugDieBacktrace("Internal error in $method: $message");
00857 }
00858
00863 public function shouldCheckMaxlag() {
00864 return true;
00865 }
00866
00871 public function isReadMode() {
00872 return true;
00873 }
00878 public function isWriteMode() {
00879 return false;
00880 }
00881
00886 public function mustBePosted() {
00887 return false;
00888 }
00889
00890
00894 private $mTimeIn = 0, $mModuleTime = 0;
00895
00899 public function profileIn() {
00900 if ($this->mTimeIn !== 0)
00901 ApiBase :: dieDebug(__METHOD__, 'called twice without calling profileOut()');
00902 $this->mTimeIn = microtime(true);
00903 wfProfileIn($this->getModuleProfileName());
00904 }
00905
00909 public function profileOut() {
00910 if ($this->mTimeIn === 0)
00911 ApiBase :: dieDebug(__METHOD__, 'called without calling profileIn() first');
00912 if ($this->mDBTimeIn !== 0)
00913 ApiBase :: dieDebug(__METHOD__, 'must be called after database profiling is done with profileDBOut()');
00914
00915 $this->mModuleTime += microtime(true) - $this->mTimeIn;
00916 $this->mTimeIn = 0;
00917 wfProfileOut($this->getModuleProfileName());
00918 }
00919
00924 public function safeProfileOut() {
00925 if ($this->mTimeIn !== 0) {
00926 if ($this->mDBTimeIn !== 0)
00927 $this->profileDBOut();
00928 $this->profileOut();
00929 }
00930 }
00931
00936 public function getProfileTime() {
00937 if ($this->mTimeIn !== 0)
00938 ApiBase :: dieDebug(__METHOD__, 'called without calling profileOut() first');
00939 return $this->mModuleTime;
00940 }
00941
00945 private $mDBTimeIn = 0, $mDBTime = 0;
00946
00950 public function profileDBIn() {
00951 if ($this->mTimeIn === 0)
00952 ApiBase :: dieDebug(__METHOD__, 'must be called while profiling the entire module with profileIn()');
00953 if ($this->mDBTimeIn !== 0)
00954 ApiBase :: dieDebug(__METHOD__, 'called twice without calling profileDBOut()');
00955 $this->mDBTimeIn = microtime(true);
00956 wfProfileIn($this->getModuleProfileName(true));
00957 }
00958
00962 public function profileDBOut() {
00963 if ($this->mTimeIn === 0)
00964 ApiBase :: dieDebug(__METHOD__, 'must be called while profiling the entire module with profileIn()');
00965 if ($this->mDBTimeIn === 0)
00966 ApiBase :: dieDebug(__METHOD__, 'called without calling profileDBIn() first');
00967
00968 $time = microtime(true) - $this->mDBTimeIn;
00969 $this->mDBTimeIn = 0;
00970
00971 $this->mDBTime += $time;
00972 $this->getMain()->mDBTime += $time;
00973 wfProfileOut($this->getModuleProfileName(true));
00974 }
00975
00980 public function getProfileDBTime() {
00981 if ($this->mDBTimeIn !== 0)
00982 ApiBase :: dieDebug(__METHOD__, 'called without calling profileDBOut() first');
00983 return $this->mDBTime;
00984 }
00985
00992 public static function debugPrint($value, $name = 'unknown', $backtrace = false) {
00993 print "\n\n<pre><b>Debugging value '$name':</b>\n\n";
00994 var_export($value);
00995 if ($backtrace)
00996 print "\n" . wfBacktrace();
00997 print "\n</pre>\n";
00998 }
00999
01000
01005 public static function getBaseVersion() {
01006 return __CLASS__ . ': $Id: ApiBase.php 50217 2009-05-05 13:12:16Z tstarling $';
01007 }
01008 }