private static function getManager($continue, $allModules, $generatedModules) { $context = new DerivativeContext(RequestContext::getMain()); $context->setRequest(new FauxRequest(array('continue' => $continue))); $main = new ApiMain($context); return new ApiContinuationManager($main, $allModules, $generatedModules); }
/** * Main execution point * * @param string $par title fragment */ public function execute($par) { $this->setHeaders(); $this->outputHeader(); $out = $this->getOutput(); $lang = $this->getLanguage(); $out->setPageTitle($this->msg('ipblocklist')); $out->addModuleStyles('mediawiki.special'); $request = $this->getRequest(); $par = $request->getVal('ip', $par); $this->target = trim($request->getVal('wpTarget', $par)); $this->options = $request->getArray('wpOptions', array()); $action = $request->getText('action'); if ($action == 'unblock' || $action == 'submit' && $request->wasPosted()) { # B/C @since 1.18: Unblock interface is now at Special:Unblock $title = SpecialPage::getTitleFor('Unblock', $this->target); $out->redirect($title->getFullURL()); return; } # Just show the block list $fields = array('Target' => array('type' => 'text', 'label-message' => 'ipadressorusername', 'tabindex' => '1', 'size' => '45', 'default' => $this->target), 'Options' => array('type' => 'multiselect', 'options' => array($this->msg('blocklist-userblocks')->text() => 'userblocks', $this->msg('blocklist-tempblocks')->text() => 'tempblocks', $this->msg('blocklist-addressblocks')->text() => 'addressblocks', $this->msg('blocklist-rangeblocks')->text() => 'rangeblocks'), 'flatlist' => true), 'Limit' => array('class' => 'HTMLBlockedUsersItemSelect', 'label-message' => 'table_pager_limit_label', 'options' => array($lang->formatNum(20) => 20, $lang->formatNum(50) => 50, $lang->formatNum(100) => 100, $lang->formatNum(250) => 250, $lang->formatNum(500) => 500), 'name' => 'limit', 'default' => 50)); $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle()); // Remove subpage $form = new HTMLForm($fields, $context); $form->setMethod('get'); $form->setWrapperLegendMsg('ipblocklist-legend'); $form->setSubmitTextMsg('ipblocklist-submit'); $form->prepareForm(); $form->displayForm(''); $this->showList(); }
/** * @dataProvider provideValidate */ public function testForm($text, $value) { $form = HTMLForm::factory('ooui', ['restrictions' => ['class' => HTMLRestrictionsField::class]]); $request = new FauxRequest(['wprestrictions' => $text], true); $context = new DerivativeContext(RequestContext::getMain()); $context->setRequest($request); $form->setContext($context); $form->setTitle(Title::newFromText('Main Page'))->setSubmitCallback(function () { return true; })->prepareForm(); $status = $form->trySubmit(); if ($status instanceof StatusValue) { $this->assertEquals($value !== false, $status->isGood()); } elseif ($value === false) { $this->assertNotSame(true, $status); } else { $this->assertSame(true, $status); } if ($value !== false) { $restrictions = $form->mFieldData['restrictions']; $this->assertInstanceOf(MWRestrictions::class, $restrictions); $this->assertEquals($value, $restrictions->toArray()['IPAddresses']); } // sanity $form->getHTML($status); }
/** * Format the rows (generated by SpecialRecentchanges or SpecialRecentchangeslinked) * as an RSS/Atom feed. */ public function execute() { $config = $this->getConfig(); $this->params = $this->extractRequestParams(); if (!$config->get('Feed')) { $this->dieUsage('Syndication feeds are not available', 'feed-unavailable'); } $feedClasses = $config->get('FeedClasses'); if (!isset($feedClasses[$this->params['feedformat']])) { $this->dieUsage('Invalid subscription feed type', 'feed-invalid'); } $this->getMain()->setCacheMode('public'); if (!$this->getMain()->getParameter('smaxage')) { // bug 63249: This page gets hit a lot, cache at least 15 seconds. $this->getMain()->setCacheMaxAge(15); } $feedFormat = $this->params['feedformat']; $specialClass = $this->params['target'] !== null ? 'SpecialRecentchangeslinked' : 'SpecialRecentchanges'; $formatter = $this->getFeedObject($feedFormat, $specialClass); // Parameters are passed via the request in the context… :( $context = new DerivativeContext($this); $context->setRequest(new DerivativeRequest($this->getRequest(), $this->params, $this->getRequest()->wasPosted())); // The row-getting functionality should be factored out of ChangesListSpecialPage too… $rc = new $specialClass(); $rc->setContext($context); $rows = $rc->getRows(); $feedItems = $rows ? ChangesFeed::buildItems($rows) : array(); ApiFormatFeedWrapper::setResult($this->getResult(), $formatter, $feedItems); }
private function getContext($requestedAction = null) { $request = new FauxRequest(array('action' => $requestedAction)); $context = new DerivativeContext(RequestContext::getMain()); $context->setRequest($request); $context->setWikiPage($this->getPage()); return $context; }
/** * If we are trying to edit and no token is set, supply one. * * @param DerivativeContext $context */ private function setEditTokenFromUser(DerivativeContext $context) { $request = $context->getRequest(); // Edits via GET are a security issue and should not succeed. On the other hand, not all // POST requests are edits, but should ignore unused parameters. if (!$request->getCheck('wpEditToken') && $request->wasPosted()) { $request->setVal('wpEditToken', $context->getUser()->getEditToken()); } }
/** * @param string $url * @param array $cookies * @return MobileContext */ private function makeContext($url = '/', $cookies = array()) { $context = new DerivativeContext(RequestContext::getMain()); $context->setRequest(new MFFauxRequest($url, $cookies)); $context->setOutput(new OutputPage($context)); $instance = unserialize('O:13:"MobileContext":0:{}'); $instance->setContext($context); return $instance; }
/** * Returns a DerivativeContext with the request variables in place * * @param $request WebRequest request object including parameters and session * @param $user User or null * @return DerivativeContext */ public function newTestContext(WebRequest $request, User $user = null) { $context = new DerivativeContext($this); $context->setRequest($request); if ($user !== null) { $context->setUser($user); } return $context; }
private function showResetForm() { if (!$this->getUser()->isAllowed('editmyoptions')) { throw new PermissionsError('editmyoptions'); } $this->getOutput()->addWikiMsg('prefs-reset-intro'); $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle('reset')); // Reset subpage $htmlForm = new HTMLForm(array(), $context, 'prefs-restore'); $htmlForm->setSubmitTextMsg('restoreprefs'); $htmlForm->setSubmitCallback(array($this, 'submitReset')); $htmlForm->suppressReset(); $htmlForm->show(); }
/** * Returns warning messages in situations where a revision cannot be viewed by a user * explaining to them why. * Returns empty string when the revision can be viewed. * * @return string */ public function getWarningMessageText() { $msg = ''; if ($this->isHiddenFromUser()) { $allowed = $this->isUserAllowedToSee(); $suppressed = $this->isSuppressedDiff(); // This IContextSource object will be used to get a message object for the // messages used in this function. We need to to this to allow the message to // get the correct value for the FULLPAGENAME inclusion (which is used in // rev-suppressed-no-diff, e.g.). Otherwise it would use Special:MobileDiff as // the target for Special:Log/delete?page=Special:MobileDiff/..., which isn't // correct and very helpful. To fix this bug, we create a new context from the // current one and set the title object (which we can get from the new revision). // Bug: T122984 $context = new DerivativeContext($this->getContext()); $revision = $this->mNewRev; $context->setTitle($revision->getTitle()); if (!$allowed) { $msg = $context->msg($suppressed ? 'rev-suppressed-no-diff' : 'rev-deleted-no-diff')->parse(); } else { # Give explanation and add a link to view the diff... $query = $this->getRequest()->appendQueryValue('unhide', '1', true); $link = $this->getTitle()->getFullURL($query); $msg = $context->msg($suppressed ? 'rev-suppressed-unhide-diff' : 'rev-deleted-unhide-diff', $link)->parse(); } } return $msg; }
/** * @param string $url * @param array $cookies * @return MobileContext */ private function makeContext($url = '/', $cookies = array()) { $query = array(); if ($url) { $params = wfParseUrl(wfExpandUrl($url)); if (isset($params['query'])) { $query = wfCgiToArray($params['query']); } } $request = new FauxRequest($query); $request->setRequestURL($url); $request->setCookies($cookies, ''); $context = new DerivativeContext(RequestContext::getMain()); $context->setRequest($request); $context->setOutput(new OutputPage($context)); $instance = unserialize('O:13:"MobileContext":0:{}'); $instance->setContext($context); return $instance; }
/** * @dataProvider provideGetParameterFromSettings * @param string|null $input * @param array $paramSettings * @param mixed $expected * @param string[] $warnings */ public function testGetParameterFromSettings($input, $paramSettings, $expected, $warnings) { $mock = new MockApi(); $wrapper = TestingAccessWrapper::newFromObject($mock); $context = new DerivativeContext($mock); $context->setRequest(new FauxRequest($input !== null ? ['foo' => $input] : [])); $wrapper->mMainModule = new ApiMain($context); if ($expected instanceof UsageException) { try { $wrapper->getParameterFromSettings('foo', $paramSettings, true); } catch (UsageException $ex) { $this->assertEquals($expected, $ex); } } else { $result = $wrapper->getParameterFromSettings('foo', $paramSettings, true); $this->assertSame($expected, $result); $this->assertSame($warnings, $mock->warnings); } }
public function execute() { $params = $this->extractRequestParams(); $modules = array(); foreach ($params['modules'] as $path) { $modules[] = $this->getModuleFromPath($path); } // Get the help $context = new DerivativeContext($this->getMain()->getContext()); $context->setSkin(SkinFactory::getDefaultInstance()->makeSkin('apioutput')); $context->setLanguage($this->getMain()->getLanguage()); $context->setTitle(SpecialPage::getTitleFor('ApiHelp')); $out = new OutputPage($context); $out->setCopyrightUrl('https://www.mediawiki.org/wiki/Special:MyLanguage/Copyright'); $context->setOutput($out); self::getHelp($context, $modules, $params); // Grab the output from the skin ob_start(); $context->getOutput()->output(); $html = ob_get_clean(); $result = $this->getResult(); if ($params['wrap']) { $data = array('mime' => 'text/html', 'help' => $html); ApiResult::setSubelementsList($data, 'help'); $result->addValue(null, $this->getModuleName(), $data); } else { $result->reset(); $result->addValue(null, 'text', $html, ApiResult::NO_SIZE_CHECK); $result->addValue(null, 'mime', 'text/html', ApiResult::NO_SIZE_CHECK); } }
/** * Creates a new set of object for the actual test context, including a new * outputpage and skintemplate. * * @param string $mode The mode for the test cases (desktop, mobile) * @return array Array of objects, including MobileContext (context), * SkinTemplate (sk) and OutputPage (out) */ protected function getContextSetup($mode, $mfXAnalyticsItems) { // Create a new MobileContext object for this test MobileContext::setInstance(null); // create a new instance of MobileContext $context = MobileContext::singleton(); // create a DerivativeContext to use in MobileContext later $mainContext = new DerivativeContext(RequestContext::getMain()); // create a new, empty OutputPage $out = new OutputPage($context); // create a new, empty SkinTemplate $sk = new SkinTemplate(); // create a new Title (main page) $title = Title::newMainPage(); // create a FauxRequest to use instead of a WebRequest object (FauxRequest forces // the creation of a FauxResponse, which allows to investigate sent header values) $request = new FauxRequest(); // set the new request object to the context $mainContext->setRequest($request); // set the main page title to the context $mainContext->setTitle($title); // set the context to the SkinTemplate $sk->setContext($mainContext); // set the OutputPage to the context $mainContext->setOutput($out); // set the DerivativeContext as a base to MobileContext $context->setContext($mainContext); // set the mode to MobileContext $context->setUseFormat($mode); // if there are any XAnalytics items, add them foreach ($mfXAnalyticsItems as $key => $val) { $context->addAnalyticsLogItem($key, $val); } // set the newly created MobileContext object as the current instance to use MobileContext::setInstance($context); // return the stuff return array('out' => $out, 'sk' => $sk, 'context' => $context); }
/** * Finish printing and output buffered data. */ public function closePrinter() { if ($this->mDisabled) { return; } $mime = $this->getMimeType(); if ($this->getIsHtml() && $mime !== null) { $format = $this->getFormat(); $lcformat = strtolower($format); $result = $this->getBuffer(); $context = new DerivativeContext($this->getMain()); $context->setSkin(SkinFactory::getDefaultInstance()->makeSkin('apioutput')); $context->setTitle(SpecialPage::getTitleFor('ApiHelp')); $out = new OutputPage($context); $context->setOutput($out); $out->addModuleStyles('mediawiki.apipretty'); $out->setPageTitle($context->msg('api-format-title')); if (!$this->getIsWrappedHtml()) { // When the format without suffix 'fm' is defined, there is a non-html version if ($this->getMain()->getModuleManager()->isDefined($lcformat, 'format')) { $msg = $context->msg('api-format-prettyprint-header')->params($format, $lcformat); } else { $msg = $context->msg('api-format-prettyprint-header-only-html')->params($format); } $header = $msg->parseAsBlock(); $out->addHTML(Html::rawElement('div', ['class' => 'api-pretty-header'], ApiHelp::fixHelpLinks($header))); } if (Hooks::run('ApiFormatHighlight', [$context, $result, $mime, $format])) { $out->addHTML(Html::element('pre', ['class' => 'api-pretty-content'], $result)); } if ($this->getIsWrappedHtml()) { // This is a special output mode mainly intended for ApiSandbox use $time = microtime(true) - $this->getConfig()->get('RequestTime'); $json = FormatJson::encode(['html' => $out->getHTML(), 'modules' => array_values(array_unique(array_merge($out->getModules(), $out->getModuleScripts(), $out->getModuleStyles()))), 'time' => round($time * 1000)], false, FormatJson::ALL_OK); // Bug 66776: wfMangleFlashPolicy() is needed to avoid a nasty bug in // Flash, but what it does isn't friendly for the API, so we need to // work around it. if (preg_match('/\\<\\s*cross-domain-policy\\s*\\>/i', $json)) { $json = preg_replace('/\\<(\\s*cross-domain-policy\\s*)\\>/i', '\\u003C$1\\u003E', $json); } echo $json; } else { // API handles its own clickjacking protection. // Note, that $wgBreakFrames will still override $wgApiFrameOptions for format mode. $out->allowClickjacking(); $out->output(); } } else { // For non-HTML output, clear all errors that might have been // displayed if display_errors=On ob_clean(); echo $this->getBuffer(); } }
public function execute() { // The data is hot but user-dependent, like page views, so we set vary cookies $this->getMain()->setCacheMode('anon-public-user-private'); // Get parameters $params = $this->extractRequestParams(); $text = $params['text']; $title = $params['title']; if ($title === null) { $titleProvided = false; // A title is needed for parsing, so arbitrarily choose one $title = 'API'; } else { $titleProvided = true; } $page = $params['page']; $pageid = $params['pageid']; $oldid = $params['oldid']; $model = $params['contentmodel']; $format = $params['contentformat']; if (!is_null($page) && (!is_null($text) || $titleProvided)) { $this->dieUsage('The page parameter cannot be used together with the text and title parameters', 'params'); } $prop = array_flip($params['prop']); if (isset($params['section'])) { $this->section = $params['section']; } else { $this->section = false; } // The parser needs $wgTitle to be set, apparently the // $title parameter in Parser::parse isn't enough *sigh* // TODO: Does this still need $wgTitle? global $wgParser, $wgTitle; // Currently unnecessary, code to act as a safeguard against any change // in current behavior of uselang $oldLang = null; if (isset($params['uselang']) && $params['uselang'] != $this->getContext()->getLanguage()->getCode()) { $oldLang = $this->getContext()->getLanguage(); // Backup language $this->getContext()->setLanguage(Language::factory($params['uselang'])); } $redirValues = null; // Return result $result = $this->getResult(); if (!is_null($oldid) || !is_null($pageid) || !is_null($page)) { if (!is_null($oldid)) { // Don't use the parser cache $rev = Revision::newFromID($oldid); if (!$rev) { $this->dieUsage("There is no revision ID {$oldid}", 'missingrev'); } $this->checkReadPermissions($rev->getTitle()); if (!$rev->userCan(Revision::DELETED_TEXT, $this->getUser())) { $this->dieUsage("You don't have permission to view deleted revisions", 'permissiondenied'); } $titleObj = $rev->getTitle(); $wgTitle = $titleObj; $pageObj = WikiPage::factory($titleObj); $popts = $this->makeParserOptions($pageObj, $params); // If for some reason the "oldid" is actually the current revision, it may be cached if ($rev->isCurrent()) { // May get from/save to parser cache $p_result = $this->getParsedContent($pageObj, $popts, $pageid, isset($prop['wikitext'])); } else { // This is an old revision, so get the text differently $this->content = $rev->getContent(Revision::FOR_THIS_USER, $this->getUser()); if ($this->section !== false) { $this->content = $this->getSectionContent($this->content, 'r' . $rev->getId()); } // Should we save old revision parses to the parser cache? $p_result = $this->content->getParserOutput($titleObj, $rev->getId(), $popts); } } else { // Not $oldid, but $pageid or $page if ($params['redirects']) { $reqParams = array('action' => 'query', 'redirects' => ''); if (!is_null($pageid)) { $reqParams['pageids'] = $pageid; } else { // $page $reqParams['titles'] = $page; } $req = new FauxRequest($reqParams); $main = new ApiMain($req); $main->execute(); $data = $main->getResultData(); $redirValues = isset($data['query']['redirects']) ? $data['query']['redirects'] : array(); $to = $page; foreach ((array) $redirValues as $r) { $to = $r['to']; } $pageParams = array('title' => $to); } elseif (!is_null($pageid)) { $pageParams = array('pageid' => $pageid); } else { // $page $pageParams = array('title' => $page); } $pageObj = $this->getTitleOrPageId($pageParams, 'fromdb'); $titleObj = $pageObj->getTitle(); if (!$titleObj || !$titleObj->exists()) { $this->dieUsage("The page you specified doesn't exist", 'missingtitle'); } $this->checkReadPermissions($titleObj); $wgTitle = $titleObj; if (isset($prop['revid'])) { $oldid = $pageObj->getLatest(); } $popts = $this->makeParserOptions($pageObj, $params); // Potentially cached $p_result = $this->getParsedContent($pageObj, $popts, $pageid, isset($prop['wikitext'])); } } else { // Not $oldid, $pageid, $page. Hence based on $text $titleObj = Title::newFromText($title); if (!$titleObj || $titleObj->isExternal()) { $this->dieUsageMsg(array('invalidtitle', $title)); } $wgTitle = $titleObj; if ($titleObj->canExist()) { $pageObj = WikiPage::factory($titleObj); } else { // Do like MediaWiki::initializeArticle() $article = Article::newFromTitle($titleObj, $this->getContext()); $pageObj = $article->getPage(); } $popts = $this->makeParserOptions($pageObj, $params); $textProvided = !is_null($text); if (!$textProvided) { if ($titleProvided && ($prop || $params['generatexml'])) { $this->setWarning("'title' used without 'text', and parsed page properties were requested " . "(did you mean to use 'page' instead of 'title'?)"); } // Prevent warning from ContentHandler::makeContent() $text = ''; } // If we are parsing text, do not use the content model of the default // API title, but default to wikitext to keep BC. if ($textProvided && !$titleProvided && is_null($model)) { $model = CONTENT_MODEL_WIKITEXT; $this->setWarning("No 'title' or 'contentmodel' was given, assuming {$model}."); } try { $this->content = ContentHandler::makeContent($text, $titleObj, $model, $format); } catch (MWContentSerializationException $ex) { $this->dieUsage($ex->getMessage(), 'parseerror'); } if ($this->section !== false) { $this->content = $this->getSectionContent($this->content, $titleObj->getText()); } if ($params['pst'] || $params['onlypst']) { $this->pstContent = $this->content->preSaveTransform($titleObj, $this->getUser(), $popts); } if ($params['onlypst']) { // Build a result and bail out $result_array = array(); $result_array['text'] = array(); ApiResult::setContent($result_array['text'], $this->pstContent->serialize($format)); if (isset($prop['wikitext'])) { $result_array['wikitext'] = array(); ApiResult::setContent($result_array['wikitext'], $this->content->serialize($format)); } $result->addValue(null, $this->getModuleName(), $result_array); return; } // Not cached (save or load) if ($params['pst']) { $p_result = $this->pstContent->getParserOutput($titleObj, null, $popts); } else { $p_result = $this->content->getParserOutput($titleObj, null, $popts); } } $result_array = array(); $result_array['title'] = $titleObj->getPrefixedText(); if (!is_null($oldid)) { $result_array['revid'] = intval($oldid); } if ($params['redirects'] && !is_null($redirValues)) { $result_array['redirects'] = $redirValues; } if ($params['disabletoc']) { $p_result->setTOCEnabled(false); } if (isset($prop['text'])) { $result_array['text'] = array(); ApiResult::setContent($result_array['text'], $p_result->getText()); } if (!is_null($params['summary'])) { $result_array['parsedsummary'] = array(); ApiResult::setContent($result_array['parsedsummary'], Linker::formatComment($params['summary'], $titleObj)); } if (isset($prop['langlinks']) || isset($prop['languageshtml'])) { $langlinks = $p_result->getLanguageLinks(); if ($params['effectivelanglinks']) { // Link flags are ignored for now, but may in the future be // included in the result. $linkFlags = array(); wfRunHooks('LanguageLinks', array($titleObj, &$langlinks, &$linkFlags)); } } else { $langlinks = false; } if (isset($prop['langlinks'])) { $result_array['langlinks'] = $this->formatLangLinks($langlinks); } if (isset($prop['languageshtml'])) { $languagesHtml = $this->languagesHtml($langlinks); $result_array['languageshtml'] = array(); ApiResult::setContent($result_array['languageshtml'], $languagesHtml); } if (isset($prop['categories'])) { $result_array['categories'] = $this->formatCategoryLinks($p_result->getCategories()); } if (isset($prop['categorieshtml'])) { $categoriesHtml = $this->categoriesHtml($p_result->getCategories()); $result_array['categorieshtml'] = array(); ApiResult::setContent($result_array['categorieshtml'], $categoriesHtml); } if (isset($prop['links'])) { $result_array['links'] = $this->formatLinks($p_result->getLinks()); } if (isset($prop['templates'])) { $result_array['templates'] = $this->formatLinks($p_result->getTemplates()); } if (isset($prop['images'])) { $result_array['images'] = array_keys($p_result->getImages()); } if (isset($prop['externallinks'])) { $result_array['externallinks'] = array_keys($p_result->getExternalLinks()); } if (isset($prop['sections'])) { $result_array['sections'] = $p_result->getSections(); } if (isset($prop['displaytitle'])) { $result_array['displaytitle'] = $p_result->getDisplayTitle() ? $p_result->getDisplayTitle() : $titleObj->getPrefixedText(); } if (isset($prop['headitems']) || isset($prop['headhtml'])) { $context = new DerivativeContext($this->getContext()); $context->setTitle($titleObj); $context->setWikiPage($pageObj); // We need an OutputPage tied to $context, not to the // RequestContext at the root of the stack. $output = new OutputPage($context); $output->addParserOutputNoText($p_result); if (isset($prop['headitems'])) { $headItems = $this->formatHeadItems($p_result->getHeadItems()); $css = $this->formatCss($output->buildCssLinksArray()); $scripts = array($output->getHeadScripts()); $result_array['headitems'] = array_merge($headItems, $css, $scripts); } if (isset($prop['headhtml'])) { $result_array['headhtml'] = array(); ApiResult::setContent($result_array['headhtml'], $output->headElement($context->getSkin())); } } if (isset($prop['iwlinks'])) { $result_array['iwlinks'] = $this->formatIWLinks($p_result->getInterwikiLinks()); } if (isset($prop['wikitext'])) { $result_array['wikitext'] = array(); ApiResult::setContent($result_array['wikitext'], $this->content->serialize($format)); if (!is_null($this->pstContent)) { $result_array['psttext'] = array(); ApiResult::setContent($result_array['psttext'], $this->pstContent->serialize($format)); } } if (isset($prop['properties'])) { $result_array['properties'] = $this->formatProperties($p_result->getProperties()); } if (isset($prop['limitreportdata'])) { $result_array['limitreportdata'] = $this->formatLimitReportData($p_result->getLimitReportData()); } if (isset($prop['limitreporthtml'])) { $limitreportHtml = EditPage::getPreviewLimitReport($p_result); $result_array['limitreporthtml'] = array(); ApiResult::setContent($result_array['limitreporthtml'], $limitreportHtml); } if ($params['generatexml']) { if ($this->content->getModel() != CONTENT_MODEL_WIKITEXT) { $this->dieUsage("generatexml is only supported for wikitext content", "notwikitext"); } $wgParser->startExternalParse($titleObj, $popts, OT_PREPROCESS); $dom = $wgParser->preprocessToDom($this->content->getNativeData()); if (is_callable(array($dom, 'saveXML'))) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } $result_array['parsetree'] = array(); ApiResult::setContent($result_array['parsetree'], $xml); } $result_mapping = array('redirects' => 'r', 'langlinks' => 'll', 'categories' => 'cl', 'links' => 'pl', 'templates' => 'tl', 'images' => 'img', 'externallinks' => 'el', 'iwlinks' => 'iw', 'sections' => 's', 'headitems' => 'hi', 'properties' => 'pp', 'limitreportdata' => 'lr'); $this->setIndexedTagNames($result_array, $result_mapping); $result->addValue(null, $this->getModuleName(), $result_array); if (!is_null($oldLang)) { $this->getContext()->setLanguage($oldLang); // Reset language to $oldLang } }
/** * Extract information from the Revision * * @param Revision $revision * @param object $row Should have a field 'ts_tags' if $this->fld_tags is set * @return array */ protected function extractRevisionInfo(Revision $revision, $row) { $title = $revision->getTitle(); $user = $this->getUser(); $vals = array(); $anyHidden = false; if ($this->fld_ids) { $vals['revid'] = intval($revision->getId()); if (!is_null($revision->getParentId())) { $vals['parentid'] = intval($revision->getParentId()); } } if ($this->fld_flags) { $vals['minor'] = $revision->isMinor(); } if ($this->fld_user || $this->fld_userid) { if ($revision->isDeleted(Revision::DELETED_USER)) { $vals['userhidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_USER, $user)) { if ($this->fld_user) { $vals['user'] = $revision->getUserText(Revision::RAW); } $userid = $revision->getUser(Revision::RAW); if (!$userid) { $vals['anon'] = true; } if ($this->fld_userid) { $vals['userid'] = $userid; } } } if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $revision->getTimestamp()); } if ($this->fld_size) { if (!is_null($revision->getSize())) { $vals['size'] = intval($revision->getSize()); } else { $vals['size'] = 0; } } if ($this->fld_sha1) { if ($revision->isDeleted(Revision::DELETED_TEXT)) { $vals['sha1hidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_TEXT, $user)) { if ($revision->getSha1() != '') { $vals['sha1'] = wfBaseConvert($revision->getSha1(), 36, 16, 40); } else { $vals['sha1'] = ''; } } } if ($this->fld_contentmodel) { $vals['contentmodel'] = $revision->getContentModel(); } if ($this->fld_comment || $this->fld_parsedcomment) { if ($revision->isDeleted(Revision::DELETED_COMMENT)) { $vals['commenthidden'] = true; $anyHidden = true; } if ($revision->userCan(Revision::DELETED_COMMENT, $user)) { $comment = $revision->getComment(Revision::RAW); if ($this->fld_comment) { $vals['comment'] = $comment; } if ($this->fld_parsedcomment) { $vals['parsedcomment'] = Linker::formatComment($comment, $title); } } } if ($this->fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); ApiResult::setIndexedTagName($tags, 'tag'); $vals['tags'] = $tags; } else { $vals['tags'] = array(); } } $content = null; global $wgParser; if ($this->fetchContent) { $content = $revision->getContent(Revision::FOR_THIS_USER, $this->getUser()); // Expand templates after getting section content because // template-added sections don't count and Parser::preprocess() // will have less input if ($content && $this->section !== false) { $content = $content->getSection($this->section, false); if (!$content) { $this->dieUsage("There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection'); } } if ($revision->isDeleted(Revision::DELETED_TEXT)) { $vals['texthidden'] = true; $anyHidden = true; } elseif (!$content) { $vals['textmissing'] = true; } } if ($this->fld_content && $content) { $text = null; if ($this->generateXML) { if ($content->getModel() === CONTENT_MODEL_WIKITEXT) { $t = $content->getNativeData(); # note: don't set $text $wgParser->startExternalParse($title, ParserOptions::newFromContext($this->getContext()), Parser::OT_PREPROCESS); $dom = $wgParser->preprocessToDom($t); if (is_callable(array($dom, 'saveXML'))) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } $vals['parsetree'] = $xml; } else { $vals['badcontentformatforparsetree'] = true; $this->setWarning("Conversion to XML is supported for wikitext only, " . $title->getPrefixedDBkey() . " uses content model " . $content->getModel()); } } if ($this->expandTemplates && !$this->parseContent) { #XXX: implement template expansion for all content types in ContentHandler? if ($content->getModel() === CONTENT_MODEL_WIKITEXT) { $text = $content->getNativeData(); $text = $wgParser->preprocess($text, $title, ParserOptions::newFromContext($this->getContext())); } else { $this->setWarning("Template expansion is supported for wikitext only, " . $title->getPrefixedDBkey() . " uses content model " . $content->getModel()); $vals['badcontentformat'] = true; $text = false; } } if ($this->parseContent) { $po = $content->getParserOutput($title, $revision->getId(), ParserOptions::newFromContext($this->getContext())); $text = $po->getText(); } if ($text === null) { $format = $this->contentFormat ? $this->contentFormat : $content->getDefaultFormat(); $model = $content->getModel(); if (!$content->isSupportedFormat($format)) { $name = $title->getPrefixedDBkey(); $this->setWarning("The requested format {$this->contentFormat} is not " . "supported for content model {$model} used by {$name}"); $vals['badcontentformat'] = true; $text = false; } else { $text = $content->serialize($format); // always include format and model. // Format is needed to deserialize, model is needed to interpret. $vals['contentformat'] = $format; $vals['contentmodel'] = $model; } } if ($text !== false) { ApiResult::setContentValue($vals, 'content', $text); } } if ($content && (!is_null($this->diffto) || !is_null($this->difftotext))) { static $n = 0; // Number of uncached diffs we've had if ($n < $this->getConfig()->get('APIMaxUncachedDiffs')) { $vals['diff'] = array(); $context = new DerivativeContext($this->getContext()); $context->setTitle($title); $handler = $revision->getContentHandler(); if (!is_null($this->difftotext)) { $model = $title->getContentModel(); if ($this->contentFormat && !ContentHandler::getForModelID($model)->isSupportedFormat($this->contentFormat)) { $name = $title->getPrefixedDBkey(); $this->setWarning("The requested format {$this->contentFormat} is not " . "supported for content model {$model} used by {$name}"); $vals['diff']['badcontentformat'] = true; $engine = null; } else { $difftocontent = ContentHandler::makeContent($this->difftotext, $title, $model, $this->contentFormat); $engine = $handler->createDifferenceEngine($context); $engine->setContent($content, $difftocontent); } } else { $engine = $handler->createDifferenceEngine($context, $revision->getID(), $this->diffto); $vals['diff']['from'] = $engine->getOldid(); $vals['diff']['to'] = $engine->getNewid(); } if ($engine) { $difftext = $engine->getDiffBody(); ApiResult::setContentValue($vals['diff'], 'body', $difftext); if (!$engine->wasCacheHit()) { $n++; } } } else { $vals['diff']['notcached'] = true; } } if ($anyHidden && $revision->isDeleted(Revision::DELETED_RESTRICTED)) { $vals['suppressed'] = true; } return $vals; }
/** * Generates a form from the given request. * @param AuthenticationRequest[] $requests * @param string $action AuthManager action name * @param string|Message $msg * @param string $msgType * @return HTMLForm */ protected function getAuthForm(array $requests, $action, $msg = '', $msgType = 'error') { global $wgSecureLogin, $wgLoginLanguageSelector; // FIXME merge this with parent if (isset($this->authForm)) { return $this->authForm; } $usingHTTPS = $this->getRequest()->getProtocol() === 'https'; // get basic form description from the auth logic $fieldInfo = AuthenticationRequest::mergeFieldInfo($requests); $fakeTemplate = $this->getFakeTemplate($msg, $msgType); $this->fakeTemplate = $fakeTemplate; // FIXME there should be a saner way to pass this to the hook // this will call onAuthChangeFormFields() $formDescriptor = static::fieldInfoToFormDescriptor($requests, $fieldInfo, $this->authAction); $this->postProcessFormDescriptor($formDescriptor); $context = $this->getContext(); if ($context->getRequest() !== $this->getRequest()) { // We have overridden the request, need to make sure the form uses that too. $context = new DerivativeContext($this->getContext()); $context->setRequest($this->getRequest()); } $form = HTMLForm::factory('vform', $formDescriptor, $context); $form->addHiddenField('authAction', $this->authAction); if ($wgLoginLanguageSelector) { $form->addHiddenField('uselang', $this->mLanguage); } $form->addHiddenField('force', $this->securityLevel); $form->addHiddenField($this->getTokenName(), $this->getToken()->toString()); if ($wgSecureLogin) { // If using HTTPS coming from HTTP, then the 'fromhttp' parameter must be preserved if (!$this->isSignup()) { $form->addHiddenField('wpForceHttps', (int) $this->mStickHTTPS); $form->addHiddenField('wpFromhttp', $usingHTTPS); } } // set properties of the form itself $form->setAction($this->getPageTitle()->getLocalURL($this->getReturnToQueryStringFragment())); $form->setName('userlogin' . ($this->isSignup() ? '2' : '')); if ($this->isSignup()) { $form->setId('userlogin2'); } // add pre/post text // header used by ConfirmEdit, CondfirmAccount, Persona, WikimediaIncubator, SemanticSignup // should be above the error message but HTMLForm doesn't support that $form->addHeaderText($fakeTemplate->html('header')); // FIXME the old form used this for error/warning messages which does not play well with // HTMLForm (maybe it could with a subclass?); for now only display it for signups // (where the JS username validation needs it) and alway empty if ($this->isSignup()) { // used by the mediawiki.special.userlogin.signup.js module $statusAreaAttribs = ['id' => 'mw-createacct-status-area']; // $statusAreaAttribs += $msg ? [ 'class' => "{$msgType}box" ] : [ 'style' => 'display: none;' ]; $form->addHeaderText(Html::element('div', $statusAreaAttribs)); } // header used by MobileFrontend $form->addHeaderText($fakeTemplate->html('formheader')); // blank signup footer for site customization if ($this->isSignup() && $this->showExtraInformation()) { // Use signupend-https for HTTPS requests if it's not blank, signupend otherwise $signupendMsg = $this->msg('signupend'); $signupendHttpsMsg = $this->msg('signupend-https'); if (!$signupendMsg->isDisabled()) { $signupendText = $usingHTTPS && !$signupendHttpsMsg->isBlank() ? $signupendHttpsMsg->parse() : $signupendMsg->parse(); $form->addPostText(Html::rawElement('div', ['id' => 'signupend'], $signupendText)); } } // warning header for non-standard workflows (e.g. security reauthentication) if (!$this->isSignup() && $this->getUser()->isLoggedIn()) { $reauthMessage = $this->securityLevel ? 'userlogin-reauth' : 'userlogin-loggedin'; $form->addHeaderText(Html::rawElement('div', ['class' => 'warningbox'], $this->msg($reauthMessage)->params($this->getUser()->getName())->parse())); } if (!$this->isSignup() && $this->showExtraInformation()) { $passwordReset = new PasswordReset($this->getConfig(), AuthManager::singleton()); if ($passwordReset->isAllowed($this->getUser())) { $form->addFooterText(Html::rawElement('div', ['class' => 'mw-ui-vform-field mw-form-related-link-container'], Linker::link(SpecialPage::getTitleFor('PasswordReset'), $this->msg('userlogin-resetpassword-link')->escaped()))); } // Don't show a "create account" link if the user can't. if ($this->showCreateAccountLink()) { // link to the other action $linkTitle = $this->getTitleFor($this->isSignup() ? 'Userlogin' : 'CreateAccount'); $linkq = $this->getReturnToQueryStringFragment(); // Pass any language selection on to the mode switch link if ($wgLoginLanguageSelector && $this->mLanguage) { $linkq .= '&uselang=' . $this->mLanguage; } $createOrLoginHref = $linkTitle->getLocalURL($linkq); if ($this->getUser()->isLoggedIn()) { $createOrLoginHtml = Html::rawElement('div', ['class' => 'mw-ui-vform-field'], Html::element('a', ['id' => 'mw-createaccount-join', 'href' => $createOrLoginHref, 'tabindex' => 100], $this->msg('userlogin-createanother')->escaped())); } else { $createOrLoginHtml = Html::rawElement('div', ['id' => 'mw-createaccount-cta', 'class' => 'mw-ui-vform-field'], $this->msg('userlogin-noaccount')->escaped() . Html::element('a', ['id' => 'mw-createaccount-join', 'href' => $createOrLoginHref, 'class' => 'mw-ui-button', 'tabindex' => 100], $this->msg('userlogin-joinproject')->escaped())); } $form->addFooterText($createOrLoginHtml); } } $form->suppressDefaultSubmit(); $this->authForm = $form; return $form; }
public function execute($par) { $out = $this->getOutput(); $out->addModuleStyles('mediawiki.special'); $this->mTarget = is_null($par) ? $this->getRequest()->getVal('wpTarget', $this->getRequest()->getVal('target', '')) : $par; // This needs to be below assignment of $this->mTarget because // getDescription() needs it to determine the correct page title. $this->setHeaders(); $this->outputHeader(); // error out if sending user cannot do this $error = self::getPermissionsError($this->getUser(), $this->getRequest()->getVal('wpEditToken'), $this->getConfig()); switch ($error) { case null: # Wahey! break; case 'badaccess': throw new PermissionsError('sendemail'); case 'blockedemailuser': throw new UserBlockedError($this->getUser()->mBlock); case 'actionthrottledtext': throw new ThrottledError(); case 'mailnologin': case 'usermaildisabled': throw new ErrorPageError($error, "{$error}text"); default: # It's a hook error list($title, $msg, $params) = $error; throw new ErrorPageError($title, $msg, $params); } // Got a valid target user name? Else ask for one. $ret = self::getTarget($this->mTarget); if (!$ret instanceof User) { if ($this->mTarget != '') { // Messages used here: notargettext, noemailtext, nowikiemailtext $ret = $ret == 'notarget' ? 'emailnotarget' : $ret . 'text'; $out->wrapWikiMsg("<p class='error'>\$1</p>", $ret); } $out->addHTML($this->userForm($this->mTarget)); return; } $this->mTargetObj = $ret; $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle()); // Remove subpage $form = new HTMLForm($this->getFormFields(), $context); // By now we are supposed to be sure that $this->mTarget is a user name $form->addPreText($this->msg('emailpagetext', $this->mTarget)->parse()); $form->setSubmitTextMsg('emailsend'); $form->setSubmitCallback(array(__CLASS__, 'uiSubmit')); $form->setWrapperLegendMsg('email-legend'); $form->loadData(); if (!Hooks::run('EmailUserForm', array(&$form))) { return; } $result = $form->show(); if ($result === true || $result instanceof Status && $result->isGood()) { $out->setPageTitle($this->msg('emailsent')); $out->addWikiMsg('emailsenttext', $this->mTarget); $out->returnToMain(false, $this->mTargetObj->getUserPage()); } }
private function extractRowInfo($row) { $revision = new Revision($row); $title = $revision->getTitle(); $vals = array(); if ($this->fld_ids) { $vals['revid'] = intval($revision->getId()); // $vals['oldid'] = intval( $row->rev_text_id ); // todo: should this be exposed? if (!is_null($revision->getParentId())) { $vals['parentid'] = intval($revision->getParentId()); } } if ($this->fld_flags && $revision->isMinor()) { $vals['minor'] = ''; } if ($this->fld_user || $this->fld_userid) { if ($revision->isDeleted(Revision::DELETED_USER)) { $vals['userhidden'] = ''; } else { if ($this->fld_user) { $vals['user'] = $revision->getUserText(); } $userid = $revision->getUser(); if (!$userid) { $vals['anon'] = ''; } if ($this->fld_userid) { $vals['userid'] = $userid; } } } if ($this->fld_timestamp) { $vals['timestamp'] = wfTimestamp(TS_ISO_8601, $revision->getTimestamp()); } if ($this->fld_size) { if (!is_null($revision->getSize())) { $vals['size'] = intval($revision->getSize()); } else { $vals['size'] = 0; } } if ($this->fld_sha1) { if ($revision->getSha1() != '') { $vals['sha1'] = wfBaseConvert($revision->getSha1(), 36, 16, 40); } else { $vals['sha1'] = ''; } } if ($this->fld_comment || $this->fld_parsedcomment) { if ($revision->isDeleted(Revision::DELETED_COMMENT)) { $vals['commenthidden'] = ''; } else { $comment = $revision->getComment(); if ($this->fld_comment) { $vals['comment'] = $comment; } if ($this->fld_parsedcomment) { $vals['parsedcomment'] = Linker::formatComment($comment, $title); } } } if ($this->fld_tags) { if ($row->ts_tags) { $tags = explode(',', $row->ts_tags); $this->getResult()->setIndexedTagName($tags, 'tag'); $vals['tags'] = $tags; } else { $vals['tags'] = array(); } } if (!is_null($this->token)) { $tokenFunctions = $this->getTokenFunctions(); foreach ($this->token as $t) { $val = call_user_func($tokenFunctions[$t], $title->getArticleID(), $title, $revision); if ($val === false) { $this->setWarning("Action '{$t}' is not allowed for the current user"); } else { $vals[$t . 'token'] = $val; } } } $text = null; global $wgParser; if ($this->fld_content || !is_null($this->difftotext)) { $text = $revision->getText(); // Expand templates after getting section content because // template-added sections don't count and Parser::preprocess() // will have less input if ($this->section !== false) { $text = $wgParser->getSection($text, $this->section, false); if ($text === false) { $this->dieUsage("There is no section {$this->section} in r" . $revision->getId(), 'nosuchsection'); } } } if ($this->fld_content && !$revision->isDeleted(Revision::DELETED_TEXT)) { if ($this->generateXML) { $wgParser->startExternalParse($title, ParserOptions::newFromContext($this->getContext()), OT_PREPROCESS); $dom = $wgParser->preprocessToDom($text); if (is_callable(array($dom, 'saveXML'))) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } $vals['parsetree'] = $xml; } if ($this->expandTemplates && !$this->parseContent) { $text = $wgParser->preprocess($text, $title, ParserOptions::newFromContext($this->getContext())); } if ($this->parseContent) { $text = $wgParser->parse($text, $title, ParserOptions::newFromContext($this->getContext()))->getText(); } ApiResult::setContent($vals, $text); } elseif ($this->fld_content) { $vals['texthidden'] = ''; } if (!is_null($this->diffto) || !is_null($this->difftotext)) { global $wgAPIMaxUncachedDiffs; static $n = 0; // Number of uncached diffs we've had if ($n < $wgAPIMaxUncachedDiffs) { $vals['diff'] = array(); $context = new DerivativeContext($this->getContext()); $context->setTitle($title); if (!is_null($this->difftotext)) { $engine = new DifferenceEngine($context); $engine->setText($text, $this->difftotext); } else { $engine = new DifferenceEngine($context, $revision->getID(), $this->diffto); $vals['diff']['from'] = $engine->getOldid(); $vals['diff']['to'] = $engine->getNewid(); } $difftext = $engine->getDiffBody(); ApiResult::setContent($vals['diff'], $difftext); if (!$engine->wasCacheHit()) { $n++; } } else { $vals['diff']['notcached'] = ''; } } return $vals; }
public function execute() { // The data is hot but user-dependent, like page views, so we set vary cookies $this->getMain()->setCacheMode('anon-public-user-private'); // Get parameters $params = $this->extractRequestParams(); $text = $params['text']; $title = $params['title']; if ($title === null) { $titleProvided = false; // A title is needed for parsing, so arbitrarily choose one $title = 'API'; } else { $titleProvided = true; } $page = $params['page']; $pageid = $params['pageid']; $oldid = $params['oldid']; $model = $params['contentmodel']; $format = $params['contentformat']; if (!is_null($page) && (!is_null($text) || $titleProvided)) { $this->dieUsage('The page parameter cannot be used together with the text and title parameters', 'params'); } $prop = array_flip($params['prop']); if (isset($params['section'])) { $this->section = $params['section']; if (!preg_match('/^((T-)?\\d+|new)$/', $this->section)) { $this->dieUsage('The section parameter must be a valid section id or "new"', 'invalidsection'); } } else { $this->section = false; } // The parser needs $wgTitle to be set, apparently the // $title parameter in Parser::parse isn't enough *sigh* // TODO: Does this still need $wgTitle? global $wgParser, $wgTitle; $redirValues = null; // Return result $result = $this->getResult(); if (!is_null($oldid) || !is_null($pageid) || !is_null($page)) { if ($this->section === 'new') { $this->dieUsage('section=new cannot be combined with oldid, pageid or page parameters. ' . 'Please use text', 'params'); } if (!is_null($oldid)) { // Don't use the parser cache $rev = Revision::newFromId($oldid); if (!$rev) { $this->dieUsage("There is no revision ID {$oldid}", 'missingrev'); } $this->checkReadPermissions($rev->getTitle()); if (!$rev->userCan(Revision::DELETED_TEXT, $this->getUser())) { $this->dieUsage("You don't have permission to view deleted revisions", 'permissiondenied'); } $titleObj = $rev->getTitle(); $wgTitle = $titleObj; $pageObj = WikiPage::factory($titleObj); list($popts, $reset, $suppressCache) = $this->makeParserOptions($pageObj, $params); // If for some reason the "oldid" is actually the current revision, it may be cached // Deliberately comparing $pageObj->getLatest() with $rev->getId(), rather than // checking $rev->isCurrent(), because $pageObj is what actually ends up being used, // and if its ->getLatest() is outdated, $rev->isCurrent() won't tell us that. if (!$suppressCache && $rev->getId() == $pageObj->getLatest()) { // May get from/save to parser cache $p_result = $this->getParsedContent($pageObj, $popts, $pageid, isset($prop['wikitext'])); } else { // This is an old revision, so get the text differently $this->content = $rev->getContent(Revision::FOR_THIS_USER, $this->getUser()); if ($this->section !== false) { $this->content = $this->getSectionContent($this->content, 'r' . $rev->getId()); } // Should we save old revision parses to the parser cache? $p_result = $this->content->getParserOutput($titleObj, $rev->getId(), $popts); } } else { // Not $oldid, but $pageid or $page if ($params['redirects']) { $reqParams = ['redirects' => '']; if (!is_null($pageid)) { $reqParams['pageids'] = $pageid; } else { // $page $reqParams['titles'] = $page; } $req = new FauxRequest($reqParams); $main = new ApiMain($req); $pageSet = new ApiPageSet($main); $pageSet->execute(); $redirValues = $pageSet->getRedirectTitlesAsResult($this->getResult()); $to = $page; foreach ($pageSet->getRedirectTitles() as $title) { $to = $title->getFullText(); } $pageParams = ['title' => $to]; } elseif (!is_null($pageid)) { $pageParams = ['pageid' => $pageid]; } else { // $page $pageParams = ['title' => $page]; } $pageObj = $this->getTitleOrPageId($pageParams, 'fromdb'); $titleObj = $pageObj->getTitle(); if (!$titleObj || !$titleObj->exists()) { $this->dieUsage("The page you specified doesn't exist", 'missingtitle'); } $this->checkReadPermissions($titleObj); $wgTitle = $titleObj; if (isset($prop['revid'])) { $oldid = $pageObj->getLatest(); } list($popts, $reset, $suppressCache) = $this->makeParserOptions($pageObj, $params); // Don't pollute the parser cache when setting options that aren't // in ParserOptions::optionsHash() /// @todo: This should be handled closer to the actual cache instead of here, see T110269 $suppressCache = $suppressCache || $params['disablepp'] || $params['disablelimitreport'] || $params['preview'] || $params['sectionpreview'] || $params['disabletidy']; if ($suppressCache) { $this->content = $this->getContent($pageObj, $pageid); $p_result = $this->content->getParserOutput($titleObj, null, $popts); } else { // Potentially cached $p_result = $this->getParsedContent($pageObj, $popts, $pageid, isset($prop['wikitext'])); } } } else { // Not $oldid, $pageid, $page. Hence based on $text $titleObj = Title::newFromText($title); if (!$titleObj || $titleObj->isExternal()) { $this->dieUsageMsg(['invalidtitle', $title]); } $wgTitle = $titleObj; if ($titleObj->canExist()) { $pageObj = WikiPage::factory($titleObj); } else { // Do like MediaWiki::initializeArticle() $article = Article::newFromTitle($titleObj, $this->getContext()); $pageObj = $article->getPage(); } list($popts, $reset) = $this->makeParserOptions($pageObj, $params); $textProvided = !is_null($text); if (!$textProvided) { if ($titleProvided && ($prop || $params['generatexml'])) { $this->setWarning("'title' used without 'text', and parsed page properties were requested " . "(did you mean to use 'page' instead of 'title'?)"); } // Prevent warning from ContentHandler::makeContent() $text = ''; } // If we are parsing text, do not use the content model of the default // API title, but default to wikitext to keep BC. if ($textProvided && !$titleProvided && is_null($model)) { $model = CONTENT_MODEL_WIKITEXT; $this->setWarning("No 'title' or 'contentmodel' was given, assuming {$model}."); } try { $this->content = ContentHandler::makeContent($text, $titleObj, $model, $format); } catch (MWContentSerializationException $ex) { $this->dieUsage($ex->getMessage(), 'parseerror'); } if ($this->section !== false) { if ($this->section === 'new') { // Insert the section title above the content. if (!is_null($params['sectiontitle']) && $params['sectiontitle'] !== '') { $this->content = $this->content->addSectionHeader($params['sectiontitle']); } } else { $this->content = $this->getSectionContent($this->content, $titleObj->getPrefixedText()); } } if ($params['pst'] || $params['onlypst']) { $this->pstContent = $this->content->preSaveTransform($titleObj, $this->getUser(), $popts); } if ($params['onlypst']) { // Build a result and bail out $result_array = []; $result_array['text'] = $this->pstContent->serialize($format); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text'; if (isset($prop['wikitext'])) { $result_array['wikitext'] = $this->content->serialize($format); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext'; } if (!is_null($params['summary']) || !is_null($params['sectiontitle']) && $this->section === 'new') { $result_array['parsedsummary'] = $this->formatSummary($titleObj, $params); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary'; } $result->addValue(null, $this->getModuleName(), $result_array); return; } // Not cached (save or load) if ($params['pst']) { $p_result = $this->pstContent->getParserOutput($titleObj, null, $popts); } else { $p_result = $this->content->getParserOutput($titleObj, null, $popts); } } $result_array = []; $result_array['title'] = $titleObj->getPrefixedText(); $result_array['pageid'] = $pageid ?: $pageObj->getId(); if (!is_null($oldid)) { $result_array['revid'] = intval($oldid); } if ($params['redirects'] && !is_null($redirValues)) { $result_array['redirects'] = $redirValues; } if ($params['disabletoc']) { $p_result->setTOCEnabled(false); } if (isset($prop['text'])) { $result_array['text'] = $p_result->getText(); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'text'; } if (!is_null($params['summary']) || !is_null($params['sectiontitle']) && $this->section === 'new') { $result_array['parsedsummary'] = $this->formatSummary($titleObj, $params); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsedsummary'; } if (isset($prop['langlinks'])) { $langlinks = $p_result->getLanguageLinks(); if ($params['effectivelanglinks']) { // Link flags are ignored for now, but may in the future be // included in the result. $linkFlags = []; Hooks::run('LanguageLinks', [$titleObj, &$langlinks, &$linkFlags]); } } else { $langlinks = false; } if (isset($prop['langlinks'])) { $result_array['langlinks'] = $this->formatLangLinks($langlinks); } if (isset($prop['categories'])) { $result_array['categories'] = $this->formatCategoryLinks($p_result->getCategories()); } if (isset($prop['categorieshtml'])) { $result_array['categorieshtml'] = $this->categoriesHtml($p_result->getCategories()); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'categorieshtml'; } if (isset($prop['links'])) { $result_array['links'] = $this->formatLinks($p_result->getLinks()); } if (isset($prop['templates'])) { $result_array['templates'] = $this->formatLinks($p_result->getTemplates()); } if (isset($prop['images'])) { $result_array['images'] = array_keys($p_result->getImages()); } if (isset($prop['externallinks'])) { $result_array['externallinks'] = array_keys($p_result->getExternalLinks()); } if (isset($prop['sections'])) { $result_array['sections'] = $p_result->getSections(); } if (isset($prop['displaytitle'])) { $result_array['displaytitle'] = $p_result->getDisplayTitle() ?: $titleObj->getPrefixedText(); } if (isset($prop['headitems'])) { $result_array['headitems'] = $this->formatHeadItems($p_result->getHeadItems()); $this->logFeatureUsage('action=parse&prop=headitems'); $this->setWarning('headitems is deprecated since MediaWiki 1.28. ' . 'Use prop=headhtml when creating new HTML documents, or ' . 'prop=modules|jsconfigvars when updating a document client-side.'); } if (isset($prop['headhtml'])) { $context = new DerivativeContext($this->getContext()); $context->setTitle($titleObj); $context->setWikiPage($pageObj); // We need an OutputPage tied to $context, not to the // RequestContext at the root of the stack. $output = new OutputPage($context); $output->addParserOutputMetadata($p_result); $result_array['headhtml'] = $output->headElement($context->getSkin()); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'headhtml'; } if (isset($prop['modules'])) { $result_array['modules'] = array_values(array_unique($p_result->getModules())); $result_array['modulescripts'] = array_values(array_unique($p_result->getModuleScripts())); $result_array['modulestyles'] = array_values(array_unique($p_result->getModuleStyles())); } if (isset($prop['jsconfigvars'])) { $result_array['jsconfigvars'] = ApiResult::addMetadataToResultVars($p_result->getJsConfigVars()); } if (isset($prop['encodedjsconfigvars'])) { $result_array['encodedjsconfigvars'] = FormatJson::encode($p_result->getJsConfigVars(), false, FormatJson::ALL_OK); $result_array[ApiResult::META_SUBELEMENTS][] = 'encodedjsconfigvars'; } if (isset($prop['modules']) && !isset($prop['jsconfigvars']) && !isset($prop['encodedjsconfigvars'])) { $this->setWarning('Property "modules" was set but not "jsconfigvars" ' . 'or "encodedjsconfigvars". Configuration variables are necessary ' . 'for proper module usage.'); } if (isset($prop['indicators'])) { $result_array['indicators'] = (array) $p_result->getIndicators(); ApiResult::setArrayType($result_array['indicators'], 'BCkvp', 'name'); } if (isset($prop['iwlinks'])) { $result_array['iwlinks'] = $this->formatIWLinks($p_result->getInterwikiLinks()); } if (isset($prop['wikitext'])) { $result_array['wikitext'] = $this->content->serialize($format); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'wikitext'; if (!is_null($this->pstContent)) { $result_array['psttext'] = $this->pstContent->serialize($format); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'psttext'; } } if (isset($prop['properties'])) { $result_array['properties'] = (array) $p_result->getProperties(); ApiResult::setArrayType($result_array['properties'], 'BCkvp', 'name'); } if (isset($prop['limitreportdata'])) { $result_array['limitreportdata'] = $this->formatLimitReportData($p_result->getLimitReportData()); } if (isset($prop['limitreporthtml'])) { $result_array['limitreporthtml'] = EditPage::getPreviewLimitReport($p_result); $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'limitreporthtml'; } if (isset($prop['parsetree']) || $params['generatexml']) { if ($this->content->getModel() != CONTENT_MODEL_WIKITEXT) { $this->dieUsage('parsetree is only supported for wikitext content', 'notwikitext'); } $wgParser->startExternalParse($titleObj, $popts, Parser::OT_PREPROCESS); $dom = $wgParser->preprocessToDom($this->content->getNativeData()); if (is_callable([$dom, 'saveXML'])) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } $result_array['parsetree'] = $xml; $result_array[ApiResult::META_BC_SUBELEMENTS][] = 'parsetree'; } $result_mapping = ['redirects' => 'r', 'langlinks' => 'll', 'categories' => 'cl', 'links' => 'pl', 'templates' => 'tl', 'images' => 'img', 'externallinks' => 'el', 'iwlinks' => 'iw', 'sections' => 's', 'headitems' => 'hi', 'modules' => 'm', 'indicators' => 'ind', 'modulescripts' => 'm', 'modulestyles' => 'm', 'properties' => 'pp', 'limitreportdata' => 'lr']; $this->setIndexedTagNames($result_array, $result_mapping); $result->addValue(null, $this->getModuleName(), $result_array); }
/** * Show the Special:ChangePassword form, with custom message * @param Message $msg */ protected function resetLoginForm(Message $msg) { // Allow hooks to explain this password reset in more detail Hooks::run('LoginPasswordResetMessage', array(&$msg, $this->mUsername)); $reset = new SpecialChangePassword(); $derivative = new DerivativeContext($this->getContext()); $derivative->setTitle($reset->getPageTitle()); $reset->setContext($derivative); if (!$this->mTempPasswordUsed) { $reset->setOldPasswordMessage('oldpassword'); } $reset->setChangeMessage($msg); $reset->execute(null); }
/** * Parse a 'continue' parameter and return status information. * * This must be balanced by a call to endContinuation(). * * @since 1.24 * @deprecated since 1.25, use ApiContinuationManager instead * @param string|null $continue * @param ApiBase[] $allModules * @param array $generatedModules * @return array */ public function beginContinuation($continue, array $allModules = array(), array $generatedModules = array()) { wfDeprecated(__METHOD__, '1.25'); if ($this->mainForContinuation->getContinuationManager()) { throw new UnexpectedValueException(__METHOD__ . ': Continuation already in progress from ' . $this->mainForContinuation->getContinuationManager()->getSource()); } // Ugh. If $continue doesn't match that in the request, temporarily // replace the request when creating the ApiContinuationManager. if ($continue === null) { $continue = ''; } if ($this->mainForContinuation->getVal('continue', '') !== $continue) { $oldCtx = $this->mainForContinuation->getContext(); $newCtx = new DerivativeContext($oldCtx); $newCtx->setRequest(new DerivativeRequest($oldCtx->getRequest(), array('continue' => $continue) + $oldCtx->getRequest()->getValues(), $oldCtx->getRequest()->wasPosted())); $this->mainForContinuation->setContext($newCtx); $reset = new ScopedCallback(array($this->mainForContinuation, 'setContext'), array($oldCtx)); } $manager = new ApiContinuationManager($this->mainForContinuation, $allModules, $generatedModules); $reset = null; $this->mainForContinuation->setContinuationManager($manager); return array($manager->isGeneratorDone(), $manager->getRunModules()); }
/** * Get an UploadForm instance with title and text properly set. * * @param string $message HTML string to add to the form * @param string $sessionKey Session key in case this is a stashed upload * @param bool $hideIgnoreWarning Whether to hide "ignore warning" check box * @return UploadForm */ protected function getUploadForm($message = '', $sessionKey = '', $hideIgnoreWarning = false) { # Initialize form $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle()); // Remove subpage $form = new UploadForm(array('watch' => $this->getWatchCheck(), 'forreupload' => $this->mForReUpload, 'sessionkey' => $sessionKey, 'hideignorewarning' => $hideIgnoreWarning, 'destwarningack' => (bool) $this->mDestWarningAck, 'description' => $this->mComment, 'texttop' => $this->uploadFormTextTop, 'textaftersummary' => $this->uploadFormTextAfterSummary, 'destfile' => $this->mDesiredDestName), $context); # Check the token, but only if necessary if (!$this->mTokenOk && !$this->mCancelUpload && ($this->mUpload && $this->mUploadClicked)) { $form->addPreText($this->msg('session_fail_preview')->parse()); } # Give a notice if the user is uploading a file that has been deleted or moved # Note that this is independent from the message 'filewasdeleted' that requires JS $desiredTitleObj = Title::makeTitleSafe(NS_FILE, $this->mDesiredDestName); $delNotice = ''; // empty by default if ($desiredTitleObj instanceof Title && !$desiredTitleObj->exists()) { LogEventsList::showLogExtract($delNotice, array('delete', 'move'), $desiredTitleObj, '', array('lim' => 10, 'conds' => array("log_action != 'revision'"), 'showIfEmpty' => false, 'msgKey' => array('upload-recreate-warning'))); } $form->addPreText($delNotice); # Add text to form $form->addPreText('<div id="uploadtext">' . $this->msg('uploadtext', array($this->mDesiredDestName))->parseAsBlock() . '</div>'); # Add upload error message $form->addPreText($message); # Add footer to form $uploadFooter = $this->msg('uploadfooter'); if (!$uploadFooter->isDisabled()) { $form->addPostText('<div id="mw-upload-footer-message">' . $uploadFooter->parseAsBlock() . "</div>\n"); } return $form; }
/** * Get a form for clearing the watchlist * * @return HTMLForm */ protected function getClearForm() { $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle('clear')); // Reset subpage $form = new HTMLForm(array(), $context); $form->setSubmitTextMsg('watchlistedit-clear-submit'); # Used message keys: 'accesskey-watchlistedit-clear-submit', 'tooltip-watchlistedit-clear-submit' $form->setSubmitTooltip('watchlistedit-clear-submit'); $form->setWrapperLegendMsg('watchlistedit-clear-legend'); $form->addHeaderText($this->msg('watchlistedit-clear-explain')->parse()); $form->setSubmitCallback(array($this, 'submitClear')); $form->setSubmitDestructive(); return $form; }
/** * Flatten an array, using the content language for any messages. * * @param array $vals Array of values * @param string $type Type of array (either lang, ul, ol). * lang = language assoc array with keys being the lang code * ul = unordered list, ol = ordered list * type can also come from the '_type' member of $vals. * @param bool $noHtml If to avoid returning anything resembling HTML. * (Ugly hack for backwards compatibility with old MediaWiki). * @param bool|IContextSource $context * @return string Single value (in wiki-syntax). * @since 1.23 */ public static function flattenArrayContentLang($vals, $type = 'ul', $noHtml = false, $context = false) { global $wgContLang; $obj = new FormatMetadata(); if ($context) { $obj->setContext($context); } $context = new DerivativeContext($obj->getContext()); $context->setLanguage($wgContLang); $obj->setContext($context); return $obj->flattenArrayReal($vals, $type, $noHtml); }
function getForm() { $fields = array('like' => array('type' => 'text', 'label-message' => 'newimages-label', 'name' => 'like'), 'showbots' => array('type' => 'check', 'label-message' => 'newimages-showbots', 'name' => 'showbots'), 'limit' => array('type' => 'hidden', 'default' => $this->mLimit, 'name' => 'limit'), 'offset' => array('type' => 'hidden', 'default' => $this->getRequest()->getText('offset'), 'name' => 'offset')); if ($this->getConfig()->get('MiserMode')) { unset($fields['like']); } $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getTitle()); // Remove subpage $form = new HTMLForm($fields, $context); $form->setSubmitTextMsg('ilsubmit'); $form->setMethod('get'); $form->setWrapperLegendMsg('newimages-legend'); return $form; }
/** * @param AuthenticationRequest[] $requests * @param string $action AuthManager action name (one of the AuthManager::ACTION_* constants) * @return HTMLForm */ protected function getAuthForm(array $requests, $action) { $formDescriptor = $this->getAuthFormDescriptor($requests, $action); $context = $this->getContext(); if ($context->getRequest() !== $this->getRequest()) { // We have overridden the request, need to make sure the form uses that too. $context = new DerivativeContext($this->getContext()); $context->setRequest($this->getRequest()); } $form = HTMLForm::factory('ooui', $formDescriptor, $context); $form->setAction($this->getFullTitle()->getFullURL($this->getPreservedParams())); $form->addHiddenField($this->getTokenName(), $this->getToken()->toString()); $form->addHiddenField('authAction', $this->authAction); $form->suppressDefaultSubmit(!$this->needsSubmitButton($formDescriptor)); return $form; }
/** * Executes the log-in attempt using the parameters passed. If * the log-in succeeeds, it attaches a cookie to the session * and outputs the user id, username, and session token. If a * log-in fails, as the result of a bad password, a nonexistent * user, or any other reason, the host is cached with an expiry * and no log-in attempts will be accepted until that expiry * is reached. The expiry is $this->mLoginThrottle. */ public function execute() { $params = $this->extractRequestParams(); $result = array(); // Init session if necessary if (session_id() == '') { wfSetupSession(); } $context = new DerivativeContext($this->getContext()); $context->setRequest(new DerivativeRequest($this->getContext()->getRequest(), array('wpName' => $params['name'], 'wpPassword' => $params['password'], 'wpDomain' => $params['domain'], 'wpLoginToken' => $params['token'], 'wpRemember' => ''))); $loginForm = new LoginForm(); $loginForm->setContext($context); global $wgCookiePrefix, $wgPasswordAttemptThrottle; $authRes = $loginForm->authenticateUserData(); switch ($authRes) { case LoginForm::SUCCESS: $user = $context->getUser(); $this->getContext()->setUser($user); $user->setOption('rememberpassword', 1); $user->setCookies($this->getRequest()); ApiQueryInfo::resetTokenCache(); // Run hooks. // @todo FIXME: Split back and frontend from this hook. // @todo FIXME: This hook should be placed in the backend $injected_html = ''; wfRunHooks('UserLoginComplete', array(&$user, &$injected_html)); $result['result'] = 'Success'; $result['lguserid'] = intval($user->getId()); $result['lgusername'] = $user->getName(); $result['lgtoken'] = $user->getToken(); $result['cookieprefix'] = $wgCookiePrefix; $result['sessionid'] = session_id(); break; case LoginForm::NEED_TOKEN: $result['result'] = 'NeedToken'; $result['token'] = $loginForm->getLoginToken(); $result['cookieprefix'] = $wgCookiePrefix; $result['sessionid'] = session_id(); break; case LoginForm::WRONG_TOKEN: $result['result'] = 'WrongToken'; break; case LoginForm::NO_NAME: $result['result'] = 'NoName'; break; case LoginForm::ILLEGAL: $result['result'] = 'Illegal'; break; case LoginForm::WRONG_PLUGIN_PASS: $result['result'] = 'WrongPluginPass'; break; case LoginForm::NOT_EXISTS: $result['result'] = 'NotExists'; break; case LoginForm::RESET_PASS: // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin - "The e-mailed temporary password should not be used for actual logins;" // bug 20223 - Treat a temporary password as wrong. Per SpecialUserLogin - "The e-mailed temporary password should not be used for actual logins;" case LoginForm::WRONG_PASS: $result['result'] = 'WrongPass'; break; case LoginForm::EMPTY_PASS: $result['result'] = 'EmptyPass'; break; case LoginForm::CREATE_BLOCKED: $result['result'] = 'CreateBlocked'; $result['details'] = 'Your IP address is blocked from account creation'; break; case LoginForm::THROTTLED: $result['result'] = 'Throttled'; $result['wait'] = intval($wgPasswordAttemptThrottle['seconds']); break; case LoginForm::USER_BLOCKED: $result['result'] = 'Blocked'; break; case LoginForm::ABORTED: $result['result'] = 'Aborted'; $result['reason'] = $loginForm->mAbortLoginErrorMsg; break; default: ApiBase::dieDebug(__METHOD__, "Unhandled case value: {$authRes}"); } $this->getResult()->addValue(null, 'login', $result); }
protected function diffWikitext($title, $wikitext) { $apiParams = array('action' => 'query', 'prop' => 'revisions', 'titles' => $title->getPrefixedDBkey(), 'rvdifftotext' => $this->pstWikitext($title, $wikitext)); $api = new ApiMain(new DerivativeRequest($this->getRequest(), $apiParams, false), false); $api->execute(); if (defined('ApiResult::META_CONTENT')) { $result = $api->getResult()->getResultData(null, array('BC' => array(), 'Types' => array())); } else { $result = $api->getResultData(); } if (!isset($result['query']['pages'][$title->getArticleID()]['revisions'][0]['diff']['*'])) { return array('result' => 'fail'); } $diffRows = $result['query']['pages'][$title->getArticleID()]['revisions'][0]['diff']['*']; if ($diffRows !== '') { $context = new DerivativeContext($this->getContext()); $context->setTitle($title); $engine = new DifferenceEngine($context); return array('result' => 'success', 'diff' => $engine->addHeader($diffRows, $context->msg('currentrev')->parse(), $context->msg('yourtext')->parse())); } else { return array('result' => 'nochanges'); } }