/** * Constructor * @param $dbSource ApiBase Module implementing getDB(). * Allows PageSet to reuse existing db connection from the shared state like ApiQuery. * @param int $flags Zero or more flags like DISABLE_GENERATORS * @param int $defaultNamespace the namespace to use if none is specified by a prefix. * @since 1.21 accepts $flags instead of two boolean values */ public function __construct( ApiBase $dbSource, $flags = 0, $defaultNamespace = NS_MAIN ) { parent::__construct( $dbSource->getMain(), $dbSource->getModuleName() ); $this->mDbSource = $dbSource; $this->mAllowGenerator = ( $flags & ApiPageSet::DISABLE_GENERATORS ) == 0; $this->mDefaultNamespace = $defaultNamespace; $this->profileIn(); $this->mParams = $this->extractRequestParams(); $this->mResolveRedirects = $this->mParams['redirects']; $this->mConvertTitles = $this->mParams['converttitles']; $this->profileOut(); }
/** * APIAfterExecute hook handler * @see: https://www.mediawiki.org/wiki/Manual:Hooks/ * @param ApiBase $module * @return bool */ public static function onAPIAfterExecute(ApiBase &$module) { global $wgMFSpecialCaseMainPage; if ($module->getModuleName() == 'parse') { if (defined('ApiResult::META_CONTENT')) { $data = $module->getResult()->getResultData(); } else { $data = $module->getResultData(); } $params = $module->extractRequestParams(); if (isset($data['parse']['text']) && $params['mobileformat']) { $result = $module->getResult(); $result->reset(); $title = Title::newFromText($data['parse']['title']); $text = $data['parse']['text']; if (is_array($text)) { if (defined('ApiResult::META_CONTENT') && isset($text[ApiResult::META_CONTENT])) { $contentKey = $text[ApiResult::META_CONTENT]; } else { $contentKey = '*'; } $html = MobileFormatter::wrapHTML($text[$contentKey]); } else { $html = MobileFormatter::wrapHTML($text); } $mf = new MobileFormatter($html, $title); $mf->setRemoveMedia($params['noimages']); $mf->setIsMainPage($params['mainpage'] && $wgMFSpecialCaseMainPage); $mf->enableExpandableSections(!$params['mainpage']); // HACK: need a nice way to request a TOC- and edit link-free HTML in the first place // FIXME: Should this be .mw-editsection? $mf->remove(array('.toc', 'mw-editsection', '.mw-headline-anchor')); $mf->filterContent(); if (is_array($text)) { $text[$contentKey] = $mf->getText(); } else { $text = $mf->getText(); } $data['parse']['text'] = $text; $result->addValue(null, $module->getModuleName(), $data['parse']); } } return true; }
/** * Execute an action, and in case of an error, erase whatever partial results * have been accumulated, and replace it with an error message and a help screen. */ protected function executeActionWithErrorHandling() { // Verify the CORS header before executing the action if (!$this->handleCORS()) { // handleCORS() has sent a 403, abort return; } // Exit here if the request method was OPTIONS // (assume there will be a followup GET or POST) if ($this->getRequest()->getMethod() === 'OPTIONS') { return; } // In case an error occurs during data output, // clear the output buffer and print just the error information $obLevel = ob_get_level(); ob_start(); $t = microtime(true); $isError = false; try { $this->executeAction(); $runTime = microtime(true) - $t; $this->logRequest($runTime); if ($this->mModule->isWriteMode() && $this->getRequest()->wasPosted()) { $this->getStats()->timing('api.' . $this->mModule->getModuleName() . '.executeTiming', 1000 * $runTime); } } catch (Exception $e) { $this->handleException($e); $this->logRequest(microtime(true) - $t, $e); $isError = true; } // Commit DBs and send any related cookies and headers MediaWiki::preOutputCommit($this->getContext()); // Send cache headers after any code which might generate an error, to // avoid sending public cache headers for errors. $this->sendCacheHeaders($isError); // Executing the action might have already messed with the output // buffers. while (ob_get_level() > $obLevel) { ob_end_flush(); } }
/** * @deprecated since 1.25 * @param ApiBase $module * @param string $paramName What type of request is this? e.g. action, * query, list, prop, meta, format * @return string */ public static function makeHelpMsgHeader($module, $paramName) { wfDeprecated(__METHOD__, '1.25'); $modulePrefix = $module->getModulePrefix(); if (strval($modulePrefix) !== '') { $modulePrefix = "({$modulePrefix}) "; } return "* {$paramName}={$module->getModuleName()} {$modulePrefix}*"; }
/** * Set the continuation parameter for the generator module * * @since 1.24 * @param ApiBase $module * @param string $paramName * @param string|array $paramValue */ public function setGeneratorContinueParam(ApiBase $module, $paramName, $paramValue) { $name = $module->getModuleName(); $paramName = $module->encodeParamName($paramName); if (is_array($paramValue)) { $paramValue = join('|', $paramValue); } $this->generatorContinuationData[$name][$paramName] = $paramValue; }
/** * @param ApiBase $module * @return ApiResult */ private function getModuleInfo($module) { $ret = []; $path = $module->getModulePath(); $ret['name'] = $module->getModuleName(); $ret['classname'] = get_class($module); $ret['path'] = $path; if (!$module->isMain()) { $ret['group'] = $module->getParent()->getModuleManager()->getModuleGroup($module->getModuleName()); } $ret['prefix'] = $module->getModulePrefix(); $sourceInfo = $module->getModuleSourceInfo(); if ($sourceInfo) { $ret['source'] = $sourceInfo['name']; if (isset($sourceInfo['namemsg'])) { $ret['sourcename'] = $this->context->msg($sourceInfo['namemsg'])->text(); } else { $ret['sourcename'] = $ret['source']; } $link = SpecialPage::getTitleFor('Version', 'License/' . $sourceInfo['name'])->getFullURL(); if (isset($sourceInfo['license-name'])) { $ret['licensetag'] = $sourceInfo['license-name']; $ret['licenselink'] = (string) $link; } elseif (SpecialVersion::getExtLicenseFileName(dirname($sourceInfo['path']))) { $ret['licenselink'] = (string) $link; } } $this->formatHelpMessages($ret, 'description', $module->getFinalDescription()); foreach ($module->getHelpFlags() as $flag) { $ret[$flag] = true; } $ret['helpurls'] = (array) $module->getHelpUrls(); if (isset($ret['helpurls'][0]) && $ret['helpurls'][0] === false) { $ret['helpurls'] = []; } ApiResult::setIndexedTagName($ret['helpurls'], 'helpurl'); if ($this->helpFormat !== 'none') { $ret['examples'] = []; $examples = $module->getExamplesMessages(); foreach ($examples as $qs => $msg) { $item = ['query' => $qs]; $msg = ApiBase::makeMessage($msg, $this->context, [$module->getModulePrefix(), $module->getModuleName(), $module->getModulePath()]); $this->formatHelpMessages($item, 'description', [$msg]); if (isset($item['description'])) { if (is_array($item['description'])) { $item['description'] = $item['description'][0]; } else { ApiResult::setSubelementsList($item, 'description'); } } $ret['examples'][] = $item; } ApiResult::setIndexedTagName($ret['examples'], 'example'); } $ret['parameters'] = []; $params = $module->getFinalParams(ApiBase::GET_VALUES_FOR_HELP); $paramDesc = $module->getFinalParamDescription(); foreach ($params as $name => $settings) { if (!is_array($settings)) { $settings = [ApiBase::PARAM_DFLT => $settings]; } $item = ['name' => $name]; if (isset($paramDesc[$name])) { $this->formatHelpMessages($item, 'description', $paramDesc[$name], true); } $item['required'] = !empty($settings[ApiBase::PARAM_REQUIRED]); if (!empty($settings[ApiBase::PARAM_DEPRECATED])) { $item['deprecated'] = true; } if ($name === 'token' && $module->needsToken()) { $item['tokentype'] = $module->needsToken(); } if (!isset($settings[ApiBase::PARAM_TYPE])) { $dflt = isset($settings[ApiBase::PARAM_DFLT]) ? $settings[ApiBase::PARAM_DFLT] : null; if (is_bool($dflt)) { $settings[ApiBase::PARAM_TYPE] = 'boolean'; } elseif (is_string($dflt) || is_null($dflt)) { $settings[ApiBase::PARAM_TYPE] = 'string'; } elseif (is_int($dflt)) { $settings[ApiBase::PARAM_TYPE] = 'integer'; } } if (isset($settings[ApiBase::PARAM_DFLT])) { switch ($settings[ApiBase::PARAM_TYPE]) { case 'boolean': $item['default'] = (bool) $settings[ApiBase::PARAM_DFLT]; break; case 'string': case 'text': case 'password': $item['default'] = strval($settings[ApiBase::PARAM_DFLT]); break; case 'integer': case 'limit': $item['default'] = intval($settings[ApiBase::PARAM_DFLT]); break; case 'timestamp': $item['default'] = wfTimestamp(TS_ISO_8601, $settings[ApiBase::PARAM_DFLT]); break; default: $item['default'] = $settings[ApiBase::PARAM_DFLT]; break; } } $item['multi'] = !empty($settings[ApiBase::PARAM_ISMULTI]); if ($item['multi']) { $item['limit'] = $this->getMain()->canApiHighLimits() ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_SML1; $item['lowlimit'] = ApiBase::LIMIT_SML1; $item['highlimit'] = ApiBase::LIMIT_SML2; } if (!empty($settings[ApiBase::PARAM_ALLOW_DUPLICATES])) { $item['allowsduplicates'] = true; } if (isset($settings[ApiBase::PARAM_TYPE])) { if ($settings[ApiBase::PARAM_TYPE] === 'submodule') { if (isset($settings[ApiBase::PARAM_SUBMODULE_MAP])) { ksort($settings[ApiBase::PARAM_SUBMODULE_MAP]); $item['type'] = array_keys($settings[ApiBase::PARAM_SUBMODULE_MAP]); $item['submodules'] = $settings[ApiBase::PARAM_SUBMODULE_MAP]; } else { $item['type'] = $module->getModuleManager()->getNames($name); sort($item['type']); $prefix = $module->isMain() ? '' : $module->getModulePath() . '+'; $item['submodules'] = []; foreach ($item['type'] as $v) { $item['submodules'][$v] = $prefix . $v; } } if (isset($settings[ApiBase::PARAM_SUBMODULE_PARAM_PREFIX])) { $item['submoduleparamprefix'] = $settings[ApiBase::PARAM_SUBMODULE_PARAM_PREFIX]; } } elseif ($settings[ApiBase::PARAM_TYPE] === 'tags') { $item['type'] = ChangeTags::listExplicitlyDefinedTags(); } else { $item['type'] = $settings[ApiBase::PARAM_TYPE]; } if (is_array($item['type'])) { // To prevent sparse arrays from being serialized to JSON as objects $item['type'] = array_values($item['type']); ApiResult::setIndexedTagName($item['type'], 't'); } } if (isset($settings[ApiBase::PARAM_MAX])) { $item['max'] = $settings[ApiBase::PARAM_MAX]; } if (isset($settings[ApiBase::PARAM_MAX2])) { $item['highmax'] = $settings[ApiBase::PARAM_MAX2]; } if (isset($settings[ApiBase::PARAM_MIN])) { $item['min'] = $settings[ApiBase::PARAM_MIN]; } if (!empty($settings[ApiBase::PARAM_RANGE_ENFORCE])) { $item['enforcerange'] = true; } if (!empty($settings[ApiBase::PARAM_HELP_MSG_INFO])) { $item['info'] = []; foreach ($settings[ApiBase::PARAM_HELP_MSG_INFO] as $i) { $tag = array_shift($i); $info = ['name' => $tag]; if (count($i)) { $info['values'] = $i; ApiResult::setIndexedTagName($info['values'], 'v'); } $this->formatHelpMessages($info, 'text', [$this->context->msg("apihelp-{$path}-paraminfo-{$tag}")->numParams(count($i))->params($this->context->getLanguage()->commaList($i))->params($module->getModulePrefix())]); ApiResult::setSubelementsList($info, 'text'); $item['info'][] = $info; } ApiResult::setIndexedTagName($item['info'], 'i'); } $ret['parameters'][] = $item; } ApiResult::setIndexedTagName($ret['parameters'], 'param'); $dynamicParams = $module->dynamicParameterDocumentation(); if ($dynamicParams !== null) { if ($this->helpFormat === 'none') { $ret['dynamicparameters'] = true; } else { $dynamicParams = ApiBase::makeMessage($dynamicParams, $this->context, [$module->getModulePrefix(), $module->getModuleName(), $module->getModulePath()]); $this->formatHelpMessages($ret, 'dynamicparameters', [$dynamicParams]); } } return $ret; }
/** * Validate "centralauthtoken", and disable certain modules that make no * sense with "centralauthtoken". * @param ApiBase $module API module * @param User $user User * @param array &$message Error message key and params * @return bool */ static function onApiCheckCanExecute($module, $user, &$message) { global $wgCentralAuthCookies; if (!$wgCentralAuthCookies) { return true; } if (self::hasApiToken()) { $module->getMain()->getVal('centralauthtoken'); # Mark used $apiCentralUser = self::getApiCentralUser(true); $centralUser = CentralAuthUser::getInstance($user); if (!$apiCentralUser || !$centralUser || $apiCentralUser->getId() !== $centralUser->getId()) { // Bad design, API. ApiBase::$messageMap['centralauth-api-badtoken'] = array('code' => 'badtoken', 'info' => 'The centralauthtoken is not valid'); $message = array('centralauth-api-badtoken'); return false; } if ($module instanceof ApiLogin || $module instanceof ApiLogout) { // Bad design, API. ApiBase::$messageMap['centralauth-api-blacklistedmodule'] = array('code' => 'badparams', 'info' => 'The module "$1" may not be used with centralauthtoken'); $message = array('centralauth-api-blacklistedmodule', $module->getModuleName()); return false; } } return true; }
/** * @param ApiBase $module * @param string $paramName What type of request is this? e.g. action, * query, list, prop, meta, format * @return string */ public static function makeHelpMsgHeader($module, $paramName) { $modulePrefix = $module->getModulePrefix(); if (strval($modulePrefix) !== '') { $modulePrefix = "({$modulePrefix}) "; } return "* {$paramName}={$module->getModuleName()} {$modulePrefix}*"; }
/** * @param ApiBase $module * @return ApiResult */ private function getModuleInfo($module) { $result = $this->getResult(); $ret = array(); $path = $module->getModulePath(); $ret['name'] = $module->getModuleName(); $ret['classname'] = get_class($module); $ret['path'] = $path; if (!$module->isMain()) { $ret['group'] = $module->getParent()->getModuleManager()->getModuleGroup($module->getModuleName()); } $ret['prefix'] = $module->getModulePrefix(); $this->formatHelpMessages($ret, 'description', $module->getFinalDescription()); foreach ($module->getHelpFlags() as $flag) { $ret[$flag] = true; } $ret['helpurls'] = (array) $module->getHelpUrls(); if (isset($ret['helpurls'][0]) && $ret['helpurls'][0] === false) { $ret['helpurls'] = array(); } ApiResult::setIndexedTagName($ret['helpurls'], 'helpurl'); if ($this->helpFormat !== 'none') { $ret['examples'] = array(); $examples = $module->getExamplesMessages(); foreach ($examples as $qs => $msg) { $item = array('query' => $qs); $msg = ApiBase::makeMessage($msg, $this->context, array($module->getModulePrefix(), $module->getModuleName(), $module->getModulePath())); $this->formatHelpMessages($item, 'description', array($msg)); if (isset($item['description'])) { if (is_array($item['description'])) { $item['description'] = $item['description'][0]; } else { ApiResult::setSubelementsList($item, 'description'); } } $ret['examples'][] = $item; } ApiResult::setIndexedTagName($ret['examples'], 'example'); } $ret['parameters'] = array(); $params = $module->getFinalParams(ApiBase::GET_VALUES_FOR_HELP); $paramDesc = $module->getFinalParamDescription(); foreach ($params as $name => $settings) { if (!is_array($settings)) { $settings = array(ApiBase::PARAM_DFLT => $settings); } $item = array('name' => $name); if (isset($paramDesc[$name])) { $this->formatHelpMessages($item, 'description', $paramDesc[$name], true); } $item['required'] = !empty($settings[ApiBase::PARAM_REQUIRED]); if (!empty($settings[ApiBase::PARAM_DEPRECATED])) { $item['deprecated'] = true; } if ($name === 'token' && $module->needsToken()) { $item['tokentype'] = $module->needsToken(); } if (!isset($settings[ApiBase::PARAM_TYPE])) { $dflt = isset($settings[ApiBase::PARAM_DFLT]) ? $settings[ApiBase::PARAM_DFLT] : null; if (is_bool($dflt)) { $settings[ApiBase::PARAM_TYPE] = 'boolean'; } elseif (is_string($dflt) || is_null($dflt)) { $settings[ApiBase::PARAM_TYPE] = 'string'; } elseif (is_int($dflt)) { $settings[ApiBase::PARAM_TYPE] = 'integer'; } } if (isset($settings[ApiBase::PARAM_DFLT])) { switch ($settings[ApiBase::PARAM_TYPE]) { case 'boolean': $item['default'] = $settings[ApiBase::PARAM_DFLT] ? 'true' : 'false'; break; case 'string': $item['default'] = strval($settings[ApiBase::PARAM_DFLT]); break; case 'integer': $item['default'] = intval($settings[ApiBase::PARAM_DFLT]); break; default: $item['default'] = $settings[ApiBase::PARAM_DFLT]; break; } } $item['multi'] = !empty($settings[ApiBase::PARAM_ISMULTI]); if ($item['multi']) { $item['limit'] = $this->getMain()->canApiHighLimits() ? ApiBase::LIMIT_SML2 : ApiBase::LIMIT_SML1; $item['lowlimit'] = ApiBase::LIMIT_SML1; $item['highlimit'] = ApiBase::LIMIT_SML2; } if (!empty($settings[ApiBase::PARAM_ALLOW_DUPLICATES])) { $item['allowsduplicates'] = true; } if (isset($settings[ApiBase::PARAM_TYPE])) { if ($settings[ApiBase::PARAM_TYPE] === 'submodule') { $item['type'] = $module->getModuleManager()->getNames($name); sort($item['type']); $item['submodules'] = true; } else { $item['type'] = $settings[ApiBase::PARAM_TYPE]; } if (is_array($item['type'])) { // To prevent sparse arrays from being serialized to JSON as objects $item['type'] = array_values($item['type']); ApiResult::setIndexedTagName($item['type'], 't'); } } if (isset($settings[ApiBase::PARAM_MAX])) { $item['max'] = $settings[ApiBase::PARAM_MAX]; } if (isset($settings[ApiBase::PARAM_MAX2])) { $item['highmax'] = $settings[ApiBase::PARAM_MAX2]; } if (isset($settings[ApiBase::PARAM_MIN])) { $item['min'] = $settings[ApiBase::PARAM_MIN]; } if (!empty($settings[ApiBase::PARAM_HELP_MSG_INFO])) { $item['info'] = array(); foreach ($settings[ApiBase::PARAM_HELP_MSG_INFO] as $i) { $tag = array_shift($i); $info = array('name' => $tag); if (count($i)) { $info['values'] = $i; ApiResult::setIndexedTagName($info['values'], 'v'); } $this->formatHelpMessages($info, 'text', array($this->context->msg("apihelp-{$path}-paraminfo-{$tag}")->numParams(count($i))->params($this->context->getLanguage()->commaList($i))->params($module->getModulePrefix()))); ApiResult::setSubelementsList($info, 'text'); $item['info'][] = $info; } ApiResult::setIndexedTagName($item['info'], 'i'); } $ret['parameters'][] = $item; } ApiResult::setIndexedTagName($ret['parameters'], 'param'); return $ret; }