/** * 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(); }
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); } }
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(); }
/** * 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); }
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'); } }
/** * 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); }
/** * 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')); // 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', array('class' => 'api-pretty-header'), ApiHelp::fixHelpLinks($header))); if (Hooks::run('ApiFormatHighlight', array($context, $result, $mime, $format))) { $out->addHTML(Html::element('pre', array('class' => 'api-pretty-content'), $result)); } // 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(); } }
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; }
/** * 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; }
/** * @param $error string */ function resetLoginForm($error) { $this->getOutput()->addHTML(Xml::element('p', array('class' => 'error'), $error)); $reset = new SpecialChangePassword(); $derivative = new DerivativeContext($this->getContext()); $derivative->setTitle($reset->getPageTitle()); $reset->setContext($derivative); $reset->execute(null); }
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()); } }
/** * Get a Filepage as parsed HTML * @param Title $title * @return string */ private function getFilePage(Title $title) { //HACK: HACK: HACK: $context = new DerivativeContext($this->getContext()); $context->setTitle($title); $context->setOutput(new OutputPage($context)); $page = new ImagePage($title); $page->setContext($context); $page->view(); $html = $context->getOutput()->getHTML(); return $html; }
/** * Default action when we don't have a subpage -- just show links to the uploads we have, * Also show a button to clear stashed files * @return bool */ private function showUploads() { // sets the title, etc. $this->setHeaders(); $this->outputHeader(); // create the form, which will also be used to execute a callback to process incoming form data // this design is extremely dubious, but supposedly HTMLForm is our standard now? $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle()); // Remove subpage $form = HTMLForm::factory('ooui', ['Clear' => ['type' => 'hidden', 'default' => true, 'name' => 'clear']], $context, 'clearStashedUploads'); $form->setSubmitDestructive(); $form->setSubmitCallback([__CLASS__, 'tryClearStashedUploads']); $form->setSubmitTextMsg('uploadstash-clear'); $form->prepareForm(); $formResult = $form->tryAuthorizedSubmit(); // show the files + form, if there are any, or just say there are none $refreshHtml = Html::element('a', ['href' => $this->getPageTitle()->getLocalURL()], $this->msg('uploadstash-refresh')->text()); $files = $this->stash->listFiles(); if ($files && count($files)) { sort($files); $fileListItemsHtml = ''; $linkRenderer = $this->getLinkRenderer(); foreach ($files as $file) { $itemHtml = $linkRenderer->makeKnownLink($this->getPageTitle("file/{$file}"), $file); try { $fileObj = $this->stash->getFile($file); $thumb = $fileObj->generateThumbName($file, ['width' => 220]); $itemHtml .= $this->msg('word-separator')->escaped() . $this->msg('parentheses')->rawParams($linkRenderer->makeKnownLink($this->getPageTitle("thumb/{$file}/{$thumb}"), $this->msg('uploadstash-thumbnail')->text()))->escaped(); } catch (Exception $e) { } $fileListItemsHtml .= Html::rawElement('li', [], $itemHtml); } $this->getOutput()->addHTML(Html::rawElement('ul', [], $fileListItemsHtml)); $form->displayForm($formResult); $this->getOutput()->addHTML(Html::rawElement('p', [], $refreshHtml)); } else { $this->getOutput()->addHTML(Html::rawElement('p', [], Html::element('span', [], $this->msg('uploadstash-nofiles')->text()) . ' ' . $refreshHtml)); } return true; }
/** * Get a Filepage as parsed HTML * @param Title $title * @return string */ private function getFilePage(Title $title) { // HACK: HACK: HACK: $context = new DerivativeContext($this->getContext()); $context->setTitle($title); $context->setOutput(new OutputPage($context)); $page = new ImagePage($title); $page->setContext($context); // T123821: Without setting the wiki page on the derivative context, // DerivativeContext#getWikiPage will (eventually) fall back to // RequestContext#getWikiPage. Here, the request context is distinct from the // derivative context and deliberately constructed with a bad title in the prelude // of api.php. $context->setWikiPage($page->getPage()); $page->view(); $html = $context->getOutput()->getHTML(); return $html; }
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_contentmodel) { $vals['contentmodel'] = $revision->getContentModel(); } 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; } } } $content = null; global $wgParser; if ($this->fld_content || !is_null($this->difftotext)) { $content = $revision->getContent(); // Expand templates after getting section content because // template-added sections don't count and Parser::preprocess() // will have less input if ($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 ($this->fld_content && !$revision->isDeleted(Revision::DELETED_TEXT)) { $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()), OT_PREPROCESS); $dom = $wgParser->preprocessToDom($t); if (is_callable(array($dom, 'saveXML'))) { $xml = $dom->saveXML(); } else { $xml = $dom->__toString(); } $vals['parsetree'] = $xml; } else { $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() . ")"); $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(); if (!$content->isSupportedFormat($format)) { $model = $content->getModel(); $name = $title->getPrefixedDBkey(); $this->dieUsage("The requested format {$this->contentFormat} is not supported " . "for content model {$model} used by {$name}", 'badformat'); } $text = $content->serialize($format); $vals['contentformat'] = $format; } if ($text !== false) { 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); $handler = $revision->getContentHandler(); if (!is_null($this->difftotext)) { $model = $title->getContentModel(); if ($this->contentFormat && !ContentHandler::getForModelID($model)->isSupportedFormat($this->contentFormat)) { $name = $title->getPrefixedDBkey(); $this->dieUsage("The requested format {$this->contentFormat} is not supported for " . "content model {$model} used by {$name}", 'badformat'); } $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(); } $difftext = $engine->getDiffBody(); ApiResult::setContent($vals['diff'], $difftext); if (!$engine->wasCacheHit()) { $n++; } } else { $vals['diff']['notcached'] = ''; } } return $vals; }
/** * Generate a form to allow users to enter an ISBN * * @param string $isbn */ private function buildForm($isbn) { $formDescriptor = ['isbn' => ['type' => 'text', 'name' => 'isbn', 'label-message' => 'booksources-isbn', 'default' => $isbn, 'autofocus' => true, 'required' => true]]; $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle()); HTMLForm::factory('ooui', $formDescriptor, $context)->setWrapperLegendMsg('booksources-search-legend')->setSubmitTextMsg('booksources-search')->setMethod('get')->prepareForm()->displayForm(false); }
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); }
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']; } 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; }
public function sendEmailForm() { $out = $this->getOutput(); $ret = $this->mTargetObj; if (!$ret instanceof User) { if ($this->mTarget != '') { // Messages used here: notargettext, noemailtext, nowikiemailtext $ret = $ret == 'notarget' ? 'emailnotarget' : $ret . 'text'; return Status::newFatal($ret); } return false; } $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle()); // Remove subpage $form = HTMLForm::factory('ooui', $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 false; } $result = $form->show(); if ($result === true || $result instanceof Status && $result->isGood()) { $out->setPageTitle($this->msg('emailsent')); $out->addWikiMsg('emailsenttext', $this->mTarget); $out->returnToMain(false, $ret->getUserPage()); } return true; }
/** * 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 onSubmit(array $data) { global $wgContLang; if ($data['pagetitle'] === '') { // Initial form view of special page, pass return false; } // At this point, it has to be a POST request. This is enforced by HTMLForm, // but lets be safe verify that. if (!$this->getRequest()->wasPosted()) { throw new RuntimeException("Form submission was not POSTed"); } $this->title = Title::newFromText($data['pagetitle']); $titleWithNewContentModel = clone $this->title; $titleWithNewContentModel->setContentModel($data['model']); $user = $this->getUser(); // Check permissions and make sure the user has permission to: $errors = wfMergeErrorArrays($this->title->getUserPermissionsErrors('editcontentmodel', $user), $this->title->getUserPermissionsErrors('edit', $user), $titleWithNewContentModel->getUserPermissionsErrors('editcontentmodel', $user), $titleWithNewContentModel->getUserPermissionsErrors('edit', $user)); if ($errors) { $out = $this->getOutput(); $wikitext = $out->formatPermissionsErrorMessage($errors); // Hack to get our wikitext parsed return Status::newFatal(new RawMessage('$1', [$wikitext])); } $page = WikiPage::factory($this->title); if ($this->oldRevision === null) { $this->oldRevision = $page->getRevision() ?: false; } $oldModel = $this->title->getContentModel(); if ($this->oldRevision) { $oldContent = $this->oldRevision->getContent(); try { $newContent = ContentHandler::makeContent($oldContent->getNativeData(), $this->title, $data['model']); } catch (MWException $e) { return Status::newFatal($this->msg('changecontentmodel-cannot-convert')->params($this->title->getPrefixedText(), ContentHandler::getLocalizedName($data['model']))); } } else { // Page doesn't exist, create an empty content object $newContent = ContentHandler::getForModelID($data['model'])->makeEmptyContent(); } // All other checks have passed, let's check rate limits if ($user->pingLimiter('editcontentmodel')) { throw new ThrottledError(); } $flags = $this->oldRevision ? EDIT_UPDATE : EDIT_NEW; $flags |= EDIT_INTERNAL; if ($user->isAllowed('bot')) { $flags |= EDIT_FORCE_BOT; } $log = new ManualLogEntry('contentmodel', $this->oldRevision ? 'change' : 'new'); $log->setPerformer($user); $log->setTarget($this->title); $log->setComment($data['reason']); $log->setParameters(['4::oldmodel' => $oldModel, '5::newmodel' => $data['model']]); $formatter = LogFormatter::newFromEntry($log); $formatter->setContext(RequestContext::newExtraneousContext($this->title)); $reason = $formatter->getPlainActionText(); if ($data['reason'] !== '') { $reason .= $this->msg('colon-separator')->inContentLanguage()->text() . $data['reason']; } # Truncate for whole multibyte characters. $reason = $wgContLang->truncate($reason, 255); // Run edit filters $derivativeContext = new DerivativeContext($this->getContext()); $derivativeContext->setTitle($this->title); $derivativeContext->setWikiPage($page); $status = new Status(); if (!Hooks::run('EditFilterMergedContent', [$derivativeContext, $newContent, $status, $reason, $user, false])) { if ($status->isGood()) { // TODO: extensions should really specify an error message $status->fatal('hookaborted'); } return $status; } $status = $page->doEditContent($newContent, $reason, $flags, $this->oldRevision ? $this->oldRevision->getId() : false, $user); if (!$status->isOK()) { return $status; } $logid = $log->insert(); $log->publish($logid); return $status; }
/** * 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; }
/** * Default action when we don't have a subpage -- just show links to the uploads we have, * Also show a button to clear stashed files * @return bool */ private function showUploads() { // sets the title, etc. $this->setHeaders(); $this->outputHeader(); // create the form, which will also be used to execute a callback to process incoming form data // this design is extremely dubious, but supposedly HTMLForm is our standard now? $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle()); // Remove subpage $form = new HTMLForm(array('Clear' => array('type' => 'hidden', 'default' => true, 'name' => 'clear')), $context, 'clearStashedUploads'); $form->setSubmitCallback(array(__CLASS__, 'tryClearStashedUploads')); $form->setSubmitTextMsg('uploadstash-clear'); $form->prepareForm(); $formResult = $form->tryAuthorizedSubmit(); // show the files + form, if there are any, or just say there are none $refreshHtml = Html::element('a', array('href' => $this->getPageTitle()->getLocalURL()), $this->msg('uploadstash-refresh')->text()); $files = $this->stash->listFiles(); if ($files && count($files)) { sort($files); $fileListItemsHtml = ''; foreach ($files as $file) { // TODO: Use Linker::link or even construct the list in plain wikitext $fileListItemsHtml .= Html::rawElement('li', array(), Html::element('a', array('href' => $this->getPageTitle("file/{$file}")->getLocalURL()), $file)); } $this->getOutput()->addHtml(Html::rawElement('ul', array(), $fileListItemsHtml)); $form->displayForm($formResult); $this->getOutput()->addHtml(Html::rawElement('p', array(), $refreshHtml)); } else { $this->getOutput()->addHtml(Html::rawElement('p', array(), Html::element('span', array(), $this->msg('uploadstash-nofiles')->text()) . ' ' . $refreshHtml)); } return true; }
/** * 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; }
/** * Get a form for editing the watchlist in "raw" mode * * @return HTMLForm */ protected function getRawForm() { $titles = implode($this->getWatchlist(), "\n"); $fields = array('Titles' => array('type' => 'textarea', 'label-message' => 'watchlistedit-raw-titles', 'default' => $titles, 'cols' => 65)); $context = new DerivativeContext($this->getContext()); $context->setTitle($this->getPageTitle('raw')); // Reset subpage $form = new HTMLForm($fields, $context); $form->setSubmitTextMsg('watchlistedit-raw-submit'); # Used message keys: 'accesskey-watchlistedit-raw-submit', 'tooltip-watchlistedit-raw-submit' $form->setSubmitTooltip('watchlistedit-raw-submit'); $form->setWrapperLegendMsg('watchlistedit-raw-legend'); $form->addHeaderText($this->msg('watchlistedit-raw-explain')->parse()); $form->setSubmitCallback(array($this, 'submitRaw')); return $form; }