/** * Get the formatter output for the given input data * @param array $params Query parameters * @param array $data Data to encode * @param string $class Printer class to use instead of the normal one * @return string * @throws Exception */ protected function encodeData(array $params, array $data, $class = null) { $context = new RequestContext(); $context->setRequest(new FauxRequest($params, true)); $main = new ApiMain($context); if ($class !== null) { $main->getModuleManager()->addModule($this->printerName, 'format', $class); } $result = $main->getResult(); $result->addArrayType(null, 'default'); foreach ($data as $k => $v) { $result->addValue(null, $k, $v); } $printer = $main->createPrinterByName($this->printerName); $printer->initPrinter(); $printer->execute(); ob_start(); try { $printer->closePrinter(); return ob_get_clean(); } catch (Exception $ex) { ob_end_clean(); throw $ex; } }
/** * @dataProvider provideGetDesktopUrl * @param string $class * @param string $subPage * @param array $params * @param string|null $expected */ public function testGetDesktopUrl($class, $subPage, array $params, $expected) { $context = new RequestContext(); $context->setRequest(new FauxRequest($params)); $page = new $class(); $page->setContext($context); $this->assertEquals($expected, $page->getDesktopUrl($subPage)); }
function setUp() { $this->page = new MIMESearchPage(); $context = new RequestContext(); $context->setTitle(Title::makeTitle(NS_SPECIAL, 'MIMESearch')); $context->setRequest(new FauxRequest()); $this->page->setContext($context); parent::setUp(); }
public function testHandleNormalization() { $context = new RequestContext(); $context->setRequest(new FauxRequest(['titles' => "a|B|å"])); $main = new ApiMain($context); $pageSet = new ApiPageSet($main); $pageSet->execute(); $this->assertSame([0 => ['A' => -1, 'B' => -2, 'Å' => -3]], $pageSet->getAllTitlesByNamespace()); $this->assertSame([['fromencoded' => true, 'from' => 'a%CC%8A', 'to' => 'å'], ['fromencoded' => false, 'from' => 'a', 'to' => 'A'], ['fromencoded' => false, 'from' => 'å', 'to' => 'Å']], $pageSet->getNormalizedTitlesAsResult()); }
/** helper to test SpecialRecentchanges::buildMainQueryConds() */ private function assertConditions($expected, $requestOptions = null, $message = '') { $context = new RequestContext(); $context->setRequest(new FauxRequest($requestOptions)); # setup the rc object $this->rc = new SpecialRecentChanges(); $this->rc->setContext($context); $formOptions = $this->rc->setup(null); # Filter out rc_timestamp conditions which depends on the test runtime # This condition is not needed as of march 2, 2011 -- hashar # @todo FIXME: Find a way to generate the correct rc_timestamp $queryConditions = array_filter($this->rc->buildMainQueryConds($formOptions), 'SpecialRecentchangesTest::filterOutRcTimestampCondition'); $this->assertEquals($expected, $queryConditions, $message); }
/** * @covers MWDebug::appendDebugInfoToApiResult */ public function testAppendDebugInfoToApiResultXmlFormat() { $request = $this->newApiRequest(['action' => 'help', 'format' => 'xml'], '/api.php?action=help&format=xml'); $context = new RequestContext(); $context->setRequest($request); $apiMain = new ApiMain($context); $result = new ApiResult($apiMain); MWDebug::appendDebugInfoToApiResult($context, $result); $this->assertInstanceOf('ApiResult', $result); $data = $result->getResultData(); $expectedKeys = ['mwVersion', 'phpEngine', 'phpVersion', 'gitRevision', 'gitBranch', 'gitViewUrl', 'time', 'log', 'debugLog', 'queries', 'request', 'memory', 'memoryPeak', 'includes', '_element']; foreach ($expectedKeys as $expectedKey) { $this->assertArrayHasKey($expectedKey, $data['debuginfo'], "debuginfo has {$expectedKey}"); } $xml = ApiFormatXml::recXmlPrint('help', $data); // exception not thrown $this->assertInternalType('string', $xml); }
protected function createPageSetWithRedirect() { $target = Title::makeTitle(NS_MAIN, 'UTRedirectTarget'); $sourceA = Title::makeTitle(NS_MAIN, 'UTRedirectSourceA'); $sourceB = Title::makeTitle(NS_MAIN, 'UTRedirectSourceB'); self::editPage('UTRedirectTarget', 'api page set test'); self::editPage('UTRedirectSourceA', '#REDIRECT [[UTRedirectTarget]]'); self::editPage('UTRedirectSourceB', '#REDIRECT [[UTRedirectTarget]]'); $request = new FauxRequest(array('redirects' => 1)); $context = new RequestContext(); $context->setRequest($request); $main = new ApiMain($context); $pageSet = new ApiPageSet($main); $pageSet->setGeneratorData($sourceA, array('index' => 1)); $pageSet->setGeneratorData($sourceB, array('index' => 3)); $pageSet->populateFromTitles(array($sourceA, $sourceB)); return array($target, $pageSet); }
/** * Make sure a nickname which is longer than $wgMaxSigChars * is not throwing a fatal error. * * Test specifications by Alexandre "ialex" Emsenhuber. * @todo give this test a real name explaining what is being tested here */ public function testBug41337() { // Set a low limit $this->setMwGlobals('wgMaxSigChars', 2); $user = $this->getMock('User'); $user->expects($this->any())->method('isAnon')->will($this->returnValue(false)); # Yeah foreach requires an array, not NULL =( $user->expects($this->any())->method('getEffectiveGroups')->will($this->returnValue([])); # The mocked user has a long nickname $user->expects($this->any())->method('getOption')->will($this->returnValueMap([['nickname', null, false, 'superlongnickname']])); # Forge a request to call the special page $context = new RequestContext(); $context->setRequest(new FauxRequest()); $context->setUser($user); $context->setTitle(Title::newFromText('Test')); # Do the call, should not spurt a fatal error. $special = new SpecialPreferences(); $special->setContext($context); $this->assertNull($special->execute([])); }
/** * @covers SpecialSearch::load * @dataProvider provideSearchOptionsTests * @param $requested Array Request parameters. For example array( 'ns5' => true, 'ns6' => true). NULL to use default options. * @param $userOptions Array User options to test with. For example array('searchNs5' => 1 );. NULL to use default options. * @param $expectedProfile An expected search profile name * @param $expectedNs Array Expected namespaces */ function testProfileAndNamespaceLoading($requested, $userOptions, $expectedProfile, $expectedNS, $message = 'Profile name and namespaces mismatches!') { $context = new RequestContext(); $context->setUser($this->newUserWithSearchNS($userOptions)); /* $context->setRequest( new FauxRequest( array( 'ns5'=>true, 'ns6'=>true, ) )); */ $context->setRequest(new FauxRequest($requested)); $search = new SpecialSearch(); $search->setContext($context); $search->load(); /** * Verify profile name and namespace in the same assertion to make * sure we will be able to fully compare the above code. PHPUnit stop * after an assertion fail. */ $this->assertEquals(array('ProfileName' => $expectedProfile, 'Namespaces' => $expectedNS), array('ProfileName' => $search->getProfile(), 'Namespaces' => $search->getNamespaces()), $message); }
public function testPrependHtmlForViewActionOnly() { $context = new \RequestContext(); $context->setRequest(new \FauxRequest(array('action' => 'view'), true)); $htmlBreadcrumbLinksBuilder = $this->getMockBuilder('\\SBL\\HtmlBreadcrumbLinksBuilder')->disableOriginalConstructor()->getMock(); $htmlBreadcrumbLinksBuilder->expects($this->once())->method('buildBreadcrumbs')->will($this->returnSelf()); $language = $this->getMockBuilder('\\Language')->disableOriginalConstructor()->getMock(); $title = $this->getMockBuilder('\\Title')->disableOriginalConstructor()->getMock(); $title->expects($this->once())->method('isKnown')->will($this->returnValue(true)); $title->expects($this->once())->method('isSpecialPage')->will($this->returnValue(false)); $title->expects($this->once())->method('getPageLanguage')->will($this->returnValue($language)); $output = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $output->expects($this->once())->method('prependHTML'); $output->expects($this->once())->method('getContext')->will($this->returnValue($context)); $output->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $instance = new SkinTemplateOutputModifier($htmlBreadcrumbLinksBuilder); $this->assertTrue($instance->modifyOutput($output)); $template = new \stdClass(); $template->data = array(); $instance->modifyTemplate($template); $this->assertEmpty($template->data['subtitle']); }
/** * Just like executePath() except it returns the HTML instead of outputting it * Returns false if there was no such special page, or a title object if it was * a redirect. * * Also saves the current $wgTitle, $wgOut, and $wgRequest variables so that * the special page will get the context it'd expect on a normal request, * and then restores them to their previous values after. * * @param $title Title * * @return String: HTML fragment */ static function capturePath(&$title) { global $wgOut, $wgTitle, $wgRequest; $oldTitle = $wgTitle; $oldOut = $wgOut; $oldRequest = $wgRequest; // Don't want special pages interpreting ?feed=atom parameters. $wgRequest = new FauxRequest(array()); $context = new RequestContext(); $context->setTitle($title); $context->setRequest($wgRequest); $wgOut = $context->getOutput(); $ret = self::executePath($title, $context, true); if ($ret === true) { $ret = $wgOut->getHTML(); } $wgTitle = $oldTitle; $wgOut = $oldOut; $wgRequest = $oldRequest; return $ret; }
public function outputDataProvider() { $language = Language::factory('en'); $title = MockTitle::buildMockForMainNamespace(__METHOD__ . 'mock-subject'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $subject = DIWikiPage::newFromTitle($title); $semanticData = $this->getMockBuilder('\\SMW\\SemanticData')->disableOriginalConstructor()->getMock(); $semanticData->expects($this->atLeastOnce())->method('getSubject')->will($this->returnValue($subject)); $semanticData->expects($this->atLeastOnce())->method('hasVisibleProperties')->will($this->returnValue(true)); $semanticData->expects($this->atLeastOnce())->method('getPropertyValues')->will($this->returnValue(array(DIWikiPage::newFromTitle($title)))); $semanticData->expects($this->atLeastOnce())->method('getProperties')->will($this->returnValue(array(new DIProperty(__METHOD__ . 'property')))); $store = $this->getMockBuilder('\\SMW\\Store')->disableOriginalConstructor()->getMockForAbstractClass(); #0 Factbox build, being visible $title = MockTitle::buildMock(__METHOD__ . 'title-being-visible'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $title->expects($this->atLeastOnce())->method('getNamespace')->will($this->returnValue(NS_MAIN)); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(10001)); $title->expects($this->atLeastOnce())->method('getLatestRevID')->will($this->returnValue(10001)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY, 'outputPage' => $outputPage, 'store' => $store, 'parserOutput' => $this->makeParserOutput($semanticData)), array('text' => $subject->getDBKey())); #1 Factbox build, being visible, using WebRequest oldid $title = MockTitle::buildMock(__METHOD__ . 'title-with-oldid'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $title->expects($this->atLeastOnce())->method('getNamespace')->will($this->returnValue(NS_MAIN)); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(10002)); $title->expects($this->atLeastOnce())->method('getLatestRevID')->will($this->returnValue(10002)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $context = new \RequestContext(); $context->setRequest(new \FauxRequest(array('oldid' => 9001), true)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue($context)); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY, 'outputPage' => $outputPage, 'store' => $store, 'parserOutput' => $this->makeParserOutput($semanticData)), array('text' => $subject->getDBKey())); #2 Factbox is expected not to be visible $title = MockTitle::buildMock(__METHOD__ . 'title-ns-disabled'); $title->expects($this->atLeastOnce())->method('getNamespace')->will($this->returnValue(NS_MAIN)); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(10003)); $title->expects($this->atLeastOnce())->method('getLatestRevID')->will($this->returnValue(10003)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => false), 'smwgShowFactbox' => SMW_FACTBOX_HIDDEN, 'outputPage' => $outputPage, 'store' => $store, 'parserOutput' => $this->makeParserOutput($semanticData)), array('text' => null)); #3 No semantic data $title = MockTitle::buildMock(__METHOD__ . 'title-empty-semanticdata'); $title->expects($this->atLeastOnce())->method('getNamespace')->will($this->returnValue(NS_MAIN)); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(10004)); $title->expects($this->atLeastOnce())->method('getLatestRevID')->will($this->returnValue(10004)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $semanticData = $this->getMockBuilder('\\SMW\\SemanticData')->disableOriginalConstructor()->getMock(); $semanticData->expects($this->atLeastOnce())->method('isEmpty')->will($this->returnValue(true)); $store = $this->getMockBuilder('\\SMW\\Store')->disableOriginalConstructor()->getMockForAbstractClass(); $store->expects($this->atLeastOnce())->method('getSemanticData')->will($this->returnValue($semanticData)); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY, 'outputPage' => $outputPage, 'store' => $store, 'parserOutput' => $this->makeParserOutput(null)), array('text' => null)); // #4 SpecialPage $title = MockTitle::buildMock(__METHOD__ . 'title-specialpage'); $title->expects($this->atLeastOnce())->method('getNamespace')->will($this->returnValue(NS_MAIN)); $title->expects($this->atLeastOnce())->method('isSpecialPage')->will($this->returnValue(true)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $store = $this->getMockBuilder('\\SMW\\Store')->disableOriginalConstructor()->getMockForAbstractClass(); $store->expects($this->atLeastOnce())->method('getSemanticData')->will($this->returnValue($semanticData)); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY, 'outputPage' => $outputPage, 'store' => $store, 'parserOutput' => $this->makeParserOutput(null)), array('text' => '')); // #5 does not exist $title = MockTitle::buildMock(__METHOD__ . 'title-not-exists'); $title->expects($this->atLeastOnce())->method('getNamespace')->will($this->returnValue(NS_MAIN)); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(false)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $store = $this->getMockBuilder('\\SMW\\Store')->disableOriginalConstructor()->getMockForAbstractClass(); $store->expects($this->atLeastOnce())->method('getSemanticData')->will($this->returnValue($semanticData)); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'smwgShowFactbox' => SMW_FACTBOX_NONEMPTY, 'outputPage' => $outputPage, 'store' => $store, 'parserOutput' => $this->makeParserOutput(null)), array('text' => '')); return $provider; }
/** * Return the text of a template, after recursively * replacing any variables or templates within the template. * * @param array $piece The parts of the template * $piece['title']: the title, i.e. the part before the | * $piece['parts']: the parameter array * $piece['lineStart']: whether the brace was at the start of a line * @param PPFrame $frame The current frame, contains template arguments * @throws MWException * @return string The text of the template * @private */ function braceSubstitution($piece, $frame) { wfProfileIn(__METHOD__); wfProfileIn(__METHOD__ . '-setup'); # Flags $found = false; # $text has been filled $nowiki = false; # wiki markup in $text should be escaped $isHTML = false; # $text is HTML, armour it against wikitext transformation $forceRawInterwiki = false; # Force interwiki transclusion to be done in raw mode not rendered $isChildObj = false; # $text is a DOM node needing expansion in a child frame $isLocalObj = false; # $text is a DOM node needing expansion in the current frame # Title object, where $text came from $title = false; # $part1 is the bit before the first |, and must contain only title characters. # Various prefixes will be stripped from it later. $titleWithSpaces = $frame->expand($piece['title']); $part1 = trim($titleWithSpaces); $titleText = false; # Original title text preserved for various purposes $originalTitle = $part1; # $args is a list of argument nodes, starting from index 0, not including $part1 # @todo FIXME: If piece['parts'] is null then the call to getLength() below won't work b/c this $args isn't an object $args = null == $piece['parts'] ? array() : $piece['parts']; wfProfileOut(__METHOD__ . '-setup'); $titleProfileIn = null; // profile templates # SUBST wfProfileIn(__METHOD__ . '-modifiers'); if (!$found) { $substMatch = $this->mSubstWords->matchStartAndRemove($part1); # Possibilities for substMatch: "subst", "safesubst" or FALSE # Decide whether to expand template or keep wikitext as-is. if ($this->ot['wiki']) { if ($substMatch === false) { $literal = true; # literal when in PST with no prefix } else { $literal = false; # expand when in PST with subst: or safesubst: } } else { if ($substMatch == 'subst') { $literal = true; # literal when not in PST with plain subst: } else { $literal = false; # expand when not in PST with safesubst: or no prefix } } if ($literal) { $text = $frame->virtualBracketedImplode('{{', '|', '}}', $titleWithSpaces, $args); $isLocalObj = true; $found = true; } } # Variables if (!$found && $args->getLength() == 0) { $id = $this->mVariables->matchStartToEnd($part1); if ($id !== false) { $text = $this->getVariableValue($id, $frame); if (MagicWord::getCacheTTL($id) > -1) { $this->mOutput->updateCacheExpiry(MagicWord::getCacheTTL($id)); } $found = true; } } # MSG, MSGNW and RAW if (!$found) { # Check for MSGNW: $mwMsgnw = MagicWord::get('msgnw'); if ($mwMsgnw->matchStartAndRemove($part1)) { $nowiki = true; } else { # Remove obsolete MSG: $mwMsg = MagicWord::get('msg'); $mwMsg->matchStartAndRemove($part1); } # Check for RAW: $mwRaw = MagicWord::get('raw'); if ($mwRaw->matchStartAndRemove($part1)) { $forceRawInterwiki = true; } } wfProfileOut(__METHOD__ . '-modifiers'); # Parser functions if (!$found) { wfProfileIn(__METHOD__ . '-pfunc'); $colonPos = strpos($part1, ':'); if ($colonPos !== false) { $func = substr($part1, 0, $colonPos); $funcArgs = array(trim(substr($part1, $colonPos + 1))); for ($i = 0; $i < $args->getLength(); $i++) { $funcArgs[] = $args->item($i); } try { $result = $this->callParserFunction($frame, $func, $funcArgs); } catch (Exception $ex) { wfProfileOut(__METHOD__ . '-pfunc'); wfProfileOut(__METHOD__); throw $ex; } # The interface for parser functions allows for extracting # flags into the local scope. Extract any forwarded flags # here. extract($result); } wfProfileOut(__METHOD__ . '-pfunc'); } # Finish mangling title and then check for loops. # Set $title to a Title object and $titleText to the PDBK if (!$found) { $ns = NS_TEMPLATE; # Split the title into page and subpage $subpage = ''; $relative = $this->maybeDoSubpageLink($part1, $subpage); if ($part1 !== $relative) { $part1 = $relative; $ns = $this->mTitle->getNamespace(); } $title = Title::newFromText($part1, $ns); if ($title) { $titleText = $title->getPrefixedText(); # Check for language variants if the template is not found if ($this->getConverterLanguage()->hasVariants() && $title->getArticleID() == 0) { $this->getConverterLanguage()->findVariantLink($part1, $title, true); } # Do recursion depth check $limit = $this->mOptions->getMaxTemplateDepth(); if ($frame->depth >= $limit) { $found = true; $text = '<span class="error">' . wfMessage('parser-template-recursion-depth-warning')->numParams($limit)->inContentLanguage()->text() . '</span>'; } } } # Load from database if (!$found && $title) { if (!Profiler::instance()->isPersistent()) { # Too many unique items can kill profiling DBs/collectors $titleProfileIn = __METHOD__ . "-title-" . $title->getPrefixedDBkey(); wfProfileIn($titleProfileIn); // template in } wfProfileIn(__METHOD__ . '-loadtpl'); if (!$title->isExternal()) { if ($title->isSpecialPage() && $this->mOptions->getAllowSpecialInclusion() && $this->ot['html']) { // Pass the template arguments as URL parameters. // "uselang" will have no effect since the Language object // is forced to the one defined in ParserOptions. $pageArgs = array(); for ($i = 0; $i < $args->getLength(); $i++) { $bits = $args->item($i)->splitArg(); if (strval($bits['index']) === '') { $name = trim($frame->expand($bits['name'], PPFrame::STRIP_COMMENTS)); $value = trim($frame->expand($bits['value'])); $pageArgs[$name] = $value; } } // Create a new context to execute the special page $context = new RequestContext(); $context->setTitle($title); $context->setRequest(new FauxRequest($pageArgs)); $context->setUser($this->getUser()); $context->setLanguage($this->mOptions->getUserLangObj()); $ret = SpecialPageFactory::capturePath($title, $context); if ($ret) { $text = $context->getOutput()->getHTML(); $this->mOutput->addOutputPageMetadata($context->getOutput()); $found = true; $isHTML = true; $this->disableCache(); } } elseif (MWNamespace::isNonincludable($title->getNamespace())) { $found = false; # access denied wfDebug(__METHOD__ . ": template inclusion denied for " . $title->getPrefixedDBkey() . "\n"); } else { list($text, $title) = $this->getTemplateDom($title); if ($text !== false) { $found = true; $isChildObj = true; } } # If the title is valid but undisplayable, make a link to it if (!$found && ($this->ot['html'] || $this->ot['pre'])) { $text = "[[:{$titleText}]]"; $found = true; } } elseif ($title->isTrans()) { # Interwiki transclusion if ($this->ot['html'] && !$forceRawInterwiki) { $text = $this->interwikiTransclude($title, 'render'); $isHTML = true; } else { $text = $this->interwikiTransclude($title, 'raw'); # Preprocess it like a template $text = $this->preprocessToDom($text, self::PTD_FOR_INCLUSION); $isChildObj = true; } $found = true; } # Do infinite loop check # This has to be done after redirect resolution to avoid infinite loops via redirects if (!$frame->loopCheck($title)) { $found = true; $text = '<span class="error">' . wfMessage('parser-template-loop-warning', $titleText)->inContentLanguage()->text() . '</span>'; wfDebug(__METHOD__ . ": template loop broken at '{$titleText}'\n"); } wfProfileOut(__METHOD__ . '-loadtpl'); } # If we haven't found text to substitute by now, we're done # Recover the source wikitext and return it if (!$found) { $text = $frame->virtualBracketedImplode('{{', '|', '}}', $titleWithSpaces, $args); if ($titleProfileIn) { wfProfileOut($titleProfileIn); // template out } wfProfileOut(__METHOD__); return array('object' => $text); } # Expand DOM-style return values in a child frame if ($isChildObj) { # Clean up argument array $newFrame = $frame->newChild($args, $title); if ($nowiki) { $text = $newFrame->expand($text, PPFrame::RECOVER_ORIG); } elseif ($titleText !== false && $newFrame->isEmpty()) { # Expansion is eligible for the empty-frame cache if (isset($this->mTplExpandCache[$titleText])) { $text = $this->mTplExpandCache[$titleText]; } else { $text = $newFrame->expand($text); $this->mTplExpandCache[$titleText] = $text; } } else { # Uncached expansion $text = $newFrame->expand($text); } } if ($isLocalObj && $nowiki) { $text = $frame->expand($text, PPFrame::RECOVER_ORIG); $isLocalObj = false; } if ($titleProfileIn) { wfProfileOut($titleProfileIn); // template out } # Replace raw HTML by a placeholder if ($isHTML) { $text = $this->insertStripItem($text); } elseif ($nowiki && ($this->ot['html'] || $this->ot['pre'])) { # Escape nowiki-style return values $text = wfEscapeWikiText($text); } elseif (is_string($text) && !$piece['lineStart'] && preg_match('/^(?:{\\||:|;|#|\\*)/', $text)) { # Bug 529: if the template begins with a table or block-level # element, it should be treated as beginning a new line. # This behavior is somewhat controversial. $text = "\n" . $text; } if (is_string($text) && !$this->incrementIncludeSize('post-expand', strlen($text))) { # Error, oversize inclusion if ($titleText !== false) { # Make a working, properly escaped link if possible (bug 23588) $text = "[[:{$titleText}]]"; } else { # This will probably not be a working link, but at least it may # provide some hint of where the problem is preg_replace('/^:/', '', $originalTitle); $text = "[[:{$originalTitle}]]"; } $text .= $this->insertStripItem('<!-- WARNING: template omitted, post-expand include size too large -->'); $this->limitationWarn('post-expand-template-inclusion'); } if ($isLocalObj) { $ret = array('object' => $text); } else { $ret = array('text' => $text); } wfProfileOut(__METHOD__); return $ret; }
public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); if (is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']) && $params['undo'] == 0) { $this->dieUsageMsg('missingtext'); } $pageObj = $this->getTitleOrPageId($params); $titleObj = $pageObj->getTitle(); $apiResult = $this->getResult(); if ($params['redirect']) { if ($params['prependtext'] === null && $params['appendtext'] === null && $params['section'] !== 'new') { $this->dieUsage('You have attempted to edit using the "redirect"-following' . ' mode, which must be used in conjuction with section=new, prependtext' . ', or appendtext.', 'redirect-appendonly'); } if ($titleObj->isRedirect()) { $oldTitle = $titleObj; $titles = Revision::newFromTitle($oldTitle, false, Revision::READ_LATEST)->getContent(Revision::FOR_THIS_USER, $user)->getRedirectChain(); // array_shift( $titles ); $redirValues = array(); /** @var $newTitle Title */ foreach ($titles as $id => $newTitle) { if (!isset($titles[$id - 1])) { $titles[$id - 1] = $oldTitle; } $redirValues[] = array('from' => $titles[$id - 1]->getPrefixedText(), 'to' => $newTitle->getPrefixedText()); $titleObj = $newTitle; } ApiResult::setIndexedTagName($redirValues, 'r'); $apiResult->addValue(null, 'redirects', $redirValues); // Since the page changed, update $pageObj $pageObj = WikiPage::factory($titleObj); } } if (!isset($params['contentmodel']) || $params['contentmodel'] == '') { $contentHandler = $pageObj->getContentHandler(); } else { $contentHandler = ContentHandler::getForModelID($params['contentmodel']); } $name = $titleObj->getPrefixedDBkey(); $model = $contentHandler->getModelID(); if ($contentHandler->supportsDirectApiEditing() === false) { $this->dieUsage("Direct editing via API is not supported for content model {$model} used by {$name}", 'no-direct-editing'); } if (!isset($params['contentformat']) || $params['contentformat'] == '') { $params['contentformat'] = $contentHandler->getDefaultFormat(); } $contentFormat = $params['contentformat']; if (!$contentHandler->isSupportedFormat($contentFormat)) { $this->dieUsage("The requested format {$contentFormat} is not supported for content model " . " {$model} used by {$name}", 'badformat'); } if ($params['createonly'] && $titleObj->exists()) { $this->dieUsageMsg('createonly-exists'); } if ($params['nocreate'] && !$titleObj->exists()) { $this->dieUsageMsg('nocreate-missing'); } // Now let's check whether we're even allowed to do this $errors = $titleObj->getUserPermissionsErrors('edit', $user); if (!$titleObj->exists()) { $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $user)); } if (count($errors)) { if (is_array($errors[0])) { switch ($errors[0][0]) { case 'blockedtext': $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); break; case 'autoblockedtext': $this->dieUsage('Your IP address has been blocked automatically, because it was used by a blocked user', 'autoblocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); break; default: $this->dieUsageMsg($errors[0]); } } else { $this->dieUsageMsg($errors[0]); } } $toMD5 = $params['text']; if (!is_null($params['appendtext']) || !is_null($params['prependtext'])) { $content = $pageObj->getContent(); if (!$content) { if ($titleObj->getNamespace() == NS_MEDIAWIKI) { # If this is a MediaWiki:x message, then load the messages # and return the message value for x. $text = $titleObj->getDefaultMessageText(); if ($text === false) { $text = ''; } try { $content = ContentHandler::makeContent($text, $this->getTitle()); } catch (MWContentSerializationException $ex) { $this->dieUsage($ex->getMessage(), 'parseerror'); return; } } else { # Otherwise, make a new empty content. $content = $contentHandler->makeEmptyContent(); } } // @todo Add support for appending/prepending to the Content interface if (!$content instanceof TextContent) { $mode = $contentHandler->getModelID(); $this->dieUsage("Can't append to pages using content model {$mode}", 'appendnotsupported'); } if (!is_null($params['section'])) { if (!$contentHandler->supportsSections()) { $modelName = $contentHandler->getModelID(); $this->dieUsage("Sections are not supported for this content model: {$modelName}.", 'sectionsnotsupported'); } if ($params['section'] == 'new') { // DWIM if they're trying to prepend/append to a new section. $content = null; } else { // Process the content for section edits $section = $params['section']; $content = $content->getSection($section); if (!$content) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } } } if (!$content) { $text = ''; } else { $text = $content->serialize($contentFormat); } $params['text'] = $params['prependtext'] . $text . $params['appendtext']; $toMD5 = $params['prependtext'] . $params['appendtext']; } if ($params['undo'] > 0) { if ($params['undoafter'] > 0) { if ($params['undo'] < $params['undoafter']) { list($params['undo'], $params['undoafter']) = array($params['undoafter'], $params['undo']); } $undoafterRev = Revision::newFromId($params['undoafter']); } $undoRev = Revision::newFromId($params['undo']); if (is_null($undoRev) || $undoRev->isDeleted(Revision::DELETED_TEXT)) { $this->dieUsageMsg(array('nosuchrevid', $params['undo'])); } if ($params['undoafter'] == 0) { $undoafterRev = $undoRev->getPrevious(); } if (is_null($undoafterRev) || $undoafterRev->isDeleted(Revision::DELETED_TEXT)) { $this->dieUsageMsg(array('nosuchrevid', $params['undoafter'])); } if ($undoRev->getPage() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); } if ($undoafterRev->getPage() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); } $newContent = $contentHandler->getUndoContent($pageObj->getRevision(), $undoRev, $undoafterRev); if (!$newContent) { $this->dieUsageMsg('undo-failure'); } $params['text'] = $newContent->serialize($params['contentformat']); // If no summary was given and we only undid one rev, // use an autosummary if (is_null($params['summary']) && $titleObj->getNextRevisionID($undoafterRev->getID()) == $params['undo']) { $params['summary'] = wfMessage('undo-summary')->params($params['undo'], $undoRev->getUserText())->inContentLanguage()->text(); } } // See if the MD5 hash checks out if (!is_null($params['md5']) && md5($toMD5) !== $params['md5']) { $this->dieUsageMsg('hashcheckfailed'); } // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable $requestArray = array('wpTextbox1' => $params['text'], 'format' => $contentFormat, 'model' => $contentHandler->getModelID(), 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => true, 'wpIgnoreBlankArticle' => true, 'wpIgnoreSelfRedirect' => true, 'bot' => $params['bot']); if (!is_null($params['summary'])) { $requestArray['wpSummary'] = $params['summary']; } if (!is_null($params['sectiontitle'])) { $requestArray['wpSectionTitle'] = $params['sectiontitle']; } // TODO: Pass along information from 'undoafter' as well if ($params['undo'] > 0) { $requestArray['wpUndidRevision'] = $params['undo']; } // Watch out for basetimestamp == '' or '0' // It gets treated as NOW, almost certainly causing an edit conflict if ($params['basetimestamp'] !== null && (bool) $this->getMain()->getVal('basetimestamp')) { $requestArray['wpEdittime'] = $params['basetimestamp']; } else { $requestArray['wpEdittime'] = $pageObj->getTimestamp(); } if ($params['starttimestamp'] !== null) { $requestArray['wpStarttime'] = $params['starttimestamp']; } else { $requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime } if ($params['minor'] || !$params['notminor'] && $user->getOption('minordefault')) { $requestArray['wpMinoredit'] = ''; } if ($params['recreate']) { $requestArray['wpRecreate'] = ''; } if (!is_null($params['section'])) { $section = $params['section']; if (!preg_match('/^((T-)?\\d+|new)$/', $section)) { $this->dieUsage("The section parameter must be a valid section id or 'new'", "invalidsection"); } $content = $pageObj->getContent(); if ($section !== '0' && $section != 'new' && (!$content || !$content->getSection($section))) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } $requestArray['wpSection'] = $params['section']; } else { $requestArray['wpSection'] = ''; } $watch = $this->getWatchlistValue($params['watchlist'], $titleObj); // Deprecated parameters if ($params['watch']) { $this->logFeatureUsage('action=edit&watch'); $watch = true; } elseif ($params['unwatch']) { $this->logFeatureUsage('action=edit&unwatch'); $watch = false; } if ($watch) { $requestArray['wpWatchthis'] = ''; } // Apply change tags if (count($params['tags'])) { if ($user->isAllowed('applychangetags')) { $requestArray['wpChangeTags'] = implode(',', $params['tags']); } else { $this->dieUsage('You don\'t have permission to set change tags.', 'taggingnotallowed'); } } // Pass through anything else we might have been given, to support extensions // This is kind of a hack but it's the best we can do to make extensions work $requestArray += $this->getRequest()->getValues(); global $wgTitle, $wgRequest; $req = new DerivativeRequest($this->getRequest(), $requestArray, true); // Some functions depend on $wgTitle == $ep->mTitle // TODO: Make them not or check if they still do $wgTitle = $titleObj; $articleContext = new RequestContext(); $articleContext->setRequest($req); $articleContext->setWikiPage($pageObj); $articleContext->setUser($this->getUser()); /** @var $articleObject Article */ $articleObject = Article::newFromWikiPage($pageObj, $articleContext); $ep = new EditPage($articleObject); $ep->setApiEditOverride(true); $ep->setContextTitle($titleObj); $ep->importFormData($req); $content = $ep->textbox1; // The following is needed to give the hook the full content of the // new revision rather than just the current section. (Bug 52077) if (!is_null($params['section']) && $contentHandler->supportsSections() && $titleObj->exists()) { // If sectiontitle is set, use it, otherwise use the summary as the section title (for // backwards compatibility with old forms/bots). if ($ep->sectiontitle !== '') { $sectionTitle = $ep->sectiontitle; } else { $sectionTitle = $ep->summary; } $contentObj = $contentHandler->unserializeContent($content, $contentFormat); $fullContentObj = $articleObject->replaceSectionContent($params['section'], $contentObj, $sectionTitle); if ($fullContentObj) { $content = $fullContentObj->serialize($contentFormat); } else { // This most likely means we have an edit conflict which means that the edit // wont succeed anyway. $this->dieUsageMsg('editconflict'); } } // Run hooks // Handle APIEditBeforeSave parameters $r = array(); if (!Hooks::run('APIEditBeforeSave', array($ep, $content, &$r))) { if (count($r)) { $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } $this->dieUsageMsg('hookaborted'); } // Do the actual save $oldRevId = $articleObject->getRevIdFetched(); $result = null; // Fake $wgRequest for some hooks inside EditPage // @todo FIXME: This interface SUCKS $oldRequest = $wgRequest; $wgRequest = $req; $status = $ep->attemptSave($result); $wgRequest = $oldRequest; switch ($status->value) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: if (isset($status->apiHookResult)) { $r = $status->apiHookResult; $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } else { $this->dieUsageMsg('hookaborted'); } case EditPage::AS_PARSE_ERROR: $this->dieUsage($status->getMessage(), 'parseerror'); case EditPage::AS_IMAGE_REDIRECT_ANON: $this->dieUsageMsg('noimageredirect-anon'); case EditPage::AS_IMAGE_REDIRECT_LOGGED: $this->dieUsageMsg('noimageredirect-logged'); case EditPage::AS_SPAM_ERROR: $this->dieUsageMsg(array('spamdetected', $result['spam'])); case EditPage::AS_BLOCKED_PAGE_FOR_USER: $this->dieUsage('You have been blocked from editing', 'blocked', 0, array('blockinfo' => ApiQueryUserInfo::getBlockInfo($user->getBlock()))); case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: case EditPage::AS_CONTENT_TOO_BIG: $this->dieUsageMsg(array('contenttoobig', $this->getConfig()->get('MaxArticleSize'))); case EditPage::AS_READ_ONLY_PAGE_ANON: $this->dieUsageMsg('noedit-anon'); case EditPage::AS_READ_ONLY_PAGE_LOGGED: $this->dieUsageMsg('noedit'); case EditPage::AS_READ_ONLY_PAGE: $this->dieReadOnly(); case EditPage::AS_RATE_LIMITED: $this->dieUsageMsg('actionthrottledtext'); case EditPage::AS_ARTICLE_WAS_DELETED: $this->dieUsageMsg('wasdeleted'); case EditPage::AS_NO_CREATE_PERMISSION: $this->dieUsageMsg('nocreate-loggedin'); case EditPage::AS_NO_CHANGE_CONTENT_MODEL: $this->dieUsageMsg('cantchangecontentmodel'); case EditPage::AS_BLANK_ARTICLE: $this->dieUsageMsg('blankpage'); case EditPage::AS_CONFLICT_DETECTED: $this->dieUsageMsg('editconflict'); case EditPage::AS_TEXTBOX_EMPTY: $this->dieUsageMsg('emptynewsection'); case EditPage::AS_CHANGE_TAG_ERROR: $this->dieStatus($status); case EditPage::AS_SUCCESS_NEW_ARTICLE: $r['new'] = true; // fall-through // fall-through case EditPage::AS_SUCCESS_UPDATE: $r['result'] = 'Success'; $r['pageid'] = intval($titleObj->getArticleID()); $r['title'] = $titleObj->getPrefixedText(); $r['contentmodel'] = $articleObject->getContentModel(); $newRevId = $articleObject->getLatest(); if ($newRevId == $oldRevId) { $r['nochange'] = true; } else { $r['oldrevid'] = intval($oldRevId); $r['newrevid'] = intval($newRevId); $r['newtimestamp'] = wfTimestamp(TS_ISO_8601, $pageObj->getTimestamp()); } break; case EditPage::AS_SUMMARY_NEEDED: // Shouldn't happen since we set wpIgnoreBlankSummary, but just in case $this->dieUsageMsg('summaryrequired'); case EditPage::AS_END: default: // $status came from WikiPage::doEdit() $errors = $status->getErrorsArray(); $this->dieUsageMsg($errors[0]); // TODO: Add new errors to message map break; } $apiResult->addValue(null, $this->getModuleName(), $r); }
public function outputDataProvider() { $text = __METHOD__ . 'text-0'; #0 Retrive content from outputPage property $title = MockTitle::buildMock(__METHOD__ . 'from-property'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(10001)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->mSMWFactboxText = $text; $skin = $this->getMockBuilder('\\Skin')->disableOriginalConstructor()->getMock(); $skin->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue(null)); $skin->expects($this->atLeastOnce())->method('getOutput')->will($this->returnValue($outputPage)); $skin->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('skin' => $skin), array('text' => $text)); #1 Retrive content from cache $title = MockTitle::buildMock(__METHOD__ . 'from-cache'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(10002)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $text = __METHOD__ . 'text-1'; $skin = $this->getMockBuilder('\\Skin')->disableOriginalConstructor()->getMock(); $skin->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $skin->expects($this->atLeastOnce())->method('getOutput')->will($this->returnValue($outputPage)); $skin->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('skin' => $skin, 'text' => $text, 'title' => $outputPage->getTitle()), array('text' => $text)); // #2 Special page $text = __METHOD__ . 'text-2'; $title = MockTitle::buildMock(__METHOD__ . 'specialpage'); $title->expects($this->atLeastOnce())->method('isSpecialPage')->will($this->returnValue(true)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->mSMWFactboxText = $text; $skin = $this->getMockBuilder('\\Skin')->disableOriginalConstructor()->getMock(); $skin->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $skin->expects($this->atLeastOnce())->method('getOutput')->will($this->returnValue($outputPage)); $skin->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('skin' => $skin, 'text' => $text), array('text' => '')); // #3 "edit" request $text = __METHOD__ . 'text-3'; $title = MockTitle::buildMock(__METHOD__ . 'edit-request'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(10003)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->mSMWFactboxText = $text; $skin = $this->getMockBuilder('\\Skin')->disableOriginalConstructor()->getMock(); $skin->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $skin->expects($this->atLeastOnce())->method('getOutput')->will($this->returnValue($outputPage)); $context = new \RequestContext(); $context->setRequest(new \FauxRequest(array('action' => 'edit'), true)); $skin->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue($context)); $provider[] = array(array('skin' => $skin, 'text' => $text), array('text' => $text)); // #4 "delete" request $text = __METHOD__ . 'text-4'; $title = MockTitle::buildMock(__METHOD__ . 'delete-request'); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $skin = $this->getMockBuilder('\\Skin')->disableOriginalConstructor()->getMock(); $skin->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $skin->expects($this->atLeastOnce())->method('getOutput')->will($this->returnValue($outputPage)); $context = new \RequestContext(); $context->setRequest(new \FauxRequest(array('action' => 'delete'), true)); $skin->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue($context)); $provider[] = array(array('skin' => $skin, 'text' => $text), array('text' => '')); return $provider; }
public function testRedirectToSpecialPageDoesntTriggerNotices() { $props = array('lastmodified', 'lastmodifiedby', 'revision', 'id', 'languagecount', 'hasvariants', 'displaytitle'); $this->setMwGlobals('wgAPIModules', array('mobileview' => 'MockApiMobileView')); $request = new FauxRequest(array('action' => 'mobileview', 'page' => 'Foo', 'sections' => '1-', 'noheadings' => '', 'text' => 'Lead == Section 1 == Text 1 == Section 2 == Text 2 ', 'prop' => implode('|', $props), 'page' => 'Redirected', 'redirect' => 'yes')); $context = new RequestContext(); $context->setRequest($request); $api = new MockApiMobileView(new ApiMain($context), 'mobileview'); $api->execute(); if (defined('ApiResult::META_CONTENT')) { $result = $api->getResult()->getResultData(); } else { $result = $api->getResultData(); } foreach ($props as $prop) { $this->assertFalse(isset($result[$prop]), "{$prop} isn't included in the response when it can't be fetched."); } }
/** * @covers OutputPage::haveCacheVaryCookies */ function testHaveCacheVaryCookies() { $request = new FauxRequest(); $context = new RequestContext(); $context->setRequest($request); $outputPage = new OutputPage($context); // No cookies are set. $this->assertFalse($outputPage->haveCacheVaryCookies()); // 'Token' is present but empty, so it shouldn't count. $request->setCookie('Token', ''); $this->assertFalse($outputPage->haveCacheVaryCookies()); // 'Token' present and nonempty. $request->setCookie('Token', '123'); $this->assertTrue($outputPage->haveCacheVaryCookies()); }
public function outputDataProvider() { $language = Language::factory('en'); $title = MockTitle::buildMockForMainNamespace(__METHOD__ . 'mock-subject'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $subject = DIWikiPage::newFromTitle($title); $semanticData = $this->getMockBuilder('\\SMW\\SemanticData')->disableOriginalConstructor()->getMock(); $semanticData->expects($this->atLeastOnce())->method('getSubject')->will($this->returnValue($subject)); $semanticData->expects($this->atLeastOnce())->method('hasVisibleProperties')->will($this->returnValue(true)); $semanticData->expects($this->atLeastOnce())->method('getPropertyValues')->will($this->returnValue(array(DIWikiPage::newFromTitle($title)))); $semanticData->expects($this->atLeastOnce())->method('getProperties')->will($this->returnValue(array(new DIProperty(__METHOD__ . 'property')))); #0 Simple factbox build, returning content $title = MockTitle::buildMock(__METHOD__ . 'title-with-content'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $title->expects($this->atLeastOnce())->method('getNamespace')->will($this->returnValue(NS_MAIN)); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(9098)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'outputPage' => $outputPage, 'parserOutput' => $this->makeParserOutput($semanticData)), array('text' => $subject->getDBKey())); #1 Disabled namespace, no return value expected $title = MockTitle::buildMock(__METHOD__ . 'title-ns-disabled'); $title->expects($this->atLeastOnce())->method('getNamespace')->will($this->returnValue(NS_MAIN)); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $title->expects($this->atLeastOnce())->method('getArticleID')->will($this->returnValue(90000)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => false), 'outputPage' => $outputPage, 'parserOutput' => $this->makeParserOutput($semanticData)), array('text' => '')); // #2 Specialpage, no return value expected $title = MockTitle::buildMock(__METHOD__ . 'mock-specialpage'); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $title->expects($this->atLeastOnce())->method('isSpecialPage')->will($this->returnValue(true)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'outputPage' => $outputPage, 'parserOutput' => $this->makeParserOutput($semanticData)), array('text' => '')); // #3 Redirect, no return value expected $title = MockTitle::buildMock(__METHOD__ . 'mock-redirect'); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $title->expects($this->atLeastOnce())->method('isRedirect')->will($this->returnValue(true)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue(new \RequestContext())); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'outputPage' => $outputPage, 'parserOutput' => $this->makeParserOutput($semanticData)), array('text' => '')); // #4 Oldid $title = MockTitle::buildMockForMainNamespace(__METHOD__ . 'mock-oldid'); $title->expects($this->atLeastOnce())->method('exists')->will($this->returnValue(true)); $title->expects($this->atLeastOnce())->method('getPageLanguage')->will($this->returnValue($language)); $outputPage = $this->getMockBuilder('\\OutputPage')->disableOriginalConstructor()->getMock(); $outputPage->expects($this->atLeastOnce())->method('getTitle')->will($this->returnValue($title)); $context = new \RequestContext(); $context->setRequest(new \FauxRequest(array('oldid' => 9001), true)); $outputPage->expects($this->atLeastOnce())->method('getContext')->will($this->returnValue($context)); $provider[] = array(array('smwgNamespacesWithSemanticLinks' => array(NS_MAIN => true), 'outputPage' => $outputPage, 'parserOutput' => $this->makeParserOutput($semanticData)), array('text' => $subject->getDBKey())); return $provider; }
public function execute() { $user = $this->getUser(); $params = $this->extractRequestParams(); if (is_null($params['text']) && is_null($params['appendtext']) && is_null($params['prependtext']) && $params['undo'] == 0) { $this->dieUsageMsg('missingtext'); } $pageObj = $this->getTitleOrPageId($params); $titleObj = $pageObj->getTitle(); $apiResult = $this->getResult(); if ($params['redirect']) { if ($titleObj->isRedirect()) { $oldTitle = $titleObj; $titles = Revision::newFromTitle($oldTitle, false, Revision::READ_LATEST)->getContent(Revision::FOR_THIS_USER, $user)->getRedirectChain(); // array_shift( $titles ); $redirValues = array(); /** @var $newTitle Title */ foreach ($titles as $id => $newTitle) { if (!isset($titles[$id - 1])) { $titles[$id - 1] = $oldTitle; } $redirValues[] = array('from' => $titles[$id - 1]->getPrefixedText(), 'to' => $newTitle->getPrefixedText()); $titleObj = $newTitle; } $apiResult->setIndexedTagName($redirValues, 'r'); $apiResult->addValue(null, 'redirects', $redirValues); // Since the page changed, update $pageObj $pageObj = WikiPage::factory($titleObj); } } if (!isset($params['contentmodel']) || $params['contentmodel'] == '') { $contentHandler = $pageObj->getContentHandler(); } else { $contentHandler = ContentHandler::getForModelID($params['contentmodel']); } // @todo ask handler whether direct editing is supported at all! make allowFlatEdit() method or some such if (!isset($params['contentformat']) || $params['contentformat'] == '') { $params['contentformat'] = $contentHandler->getDefaultFormat(); } $contentFormat = $params['contentformat']; if (!$contentHandler->isSupportedFormat($contentFormat)) { $name = $titleObj->getPrefixedDBkey(); $model = $contentHandler->getModelID(); $this->dieUsage("The requested format {$contentFormat} is not supported for content model " . " {$model} used by {$name}", 'badformat'); } if ($params['createonly'] && $titleObj->exists()) { $this->dieUsageMsg('createonly-exists'); } if ($params['nocreate'] && !$titleObj->exists()) { $this->dieUsageMsg('nocreate-missing'); } // Now let's check whether we're even allowed to do this $errors = $titleObj->getUserPermissionsErrors('edit', $user); if (!$titleObj->exists()) { $errors = array_merge($errors, $titleObj->getUserPermissionsErrors('create', $user)); } if (count($errors)) { $this->dieUsageMsg($errors[0]); } $toMD5 = $params['text']; if (!is_null($params['appendtext']) || !is_null($params['prependtext'])) { $content = $pageObj->getContent(); if (!$content) { if ($titleObj->getNamespace() == NS_MEDIAWIKI) { # If this is a MediaWiki:x message, then load the messages # and return the message value for x. $text = $titleObj->getDefaultMessageText(); if ($text === false) { $text = ''; } try { $content = ContentHandler::makeContent($text, $this->getTitle()); } catch (MWContentSerializationException $ex) { $this->dieUsage($ex->getMessage(), 'parseerror'); return; } } else { # Otherwise, make a new empty content. $content = $contentHandler->makeEmptyContent(); } } // @todo Add support for appending/prepending to the Content interface if (!$content instanceof TextContent) { $mode = $contentHandler->getModelID(); $this->dieUsage("Can't append to pages using content model {$mode}", 'appendnotsupported'); } if (!is_null($params['section'])) { if (!$contentHandler->supportsSections()) { $modelName = $contentHandler->getModelID(); $this->dieUsage("Sections are not supported for this content model: {$modelName}.", 'sectionsnotsupported'); } // Process the content for section edits $section = intval($params['section']); $content = $content->getSection($section); if (!$content) { $this->dieUsage("There is no section {$section}.", 'nosuchsection'); } } if (!$content) { $text = ''; } else { $text = $content->serialize($contentFormat); } $params['text'] = $params['prependtext'] . $text . $params['appendtext']; $toMD5 = $params['prependtext'] . $params['appendtext']; } if ($params['undo'] > 0) { if ($params['undoafter'] > 0) { if ($params['undo'] < $params['undoafter']) { list($params['undo'], $params['undoafter']) = array($params['undoafter'], $params['undo']); } $undoafterRev = Revision::newFromID($params['undoafter']); } $undoRev = Revision::newFromID($params['undo']); if (is_null($undoRev) || $undoRev->isDeleted(Revision::DELETED_TEXT)) { $this->dieUsageMsg(array('nosuchrevid', $params['undo'])); } if ($params['undoafter'] == 0) { $undoafterRev = $undoRev->getPrevious(); } if (is_null($undoafterRev) || $undoafterRev->isDeleted(Revision::DELETED_TEXT)) { $this->dieUsageMsg(array('nosuchrevid', $params['undoafter'])); } if ($undoRev->getPage() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoRev->getID(), $titleObj->getPrefixedText())); } if ($undoafterRev->getPage() != $pageObj->getID()) { $this->dieUsageMsg(array('revwrongpage', $undoafterRev->getID(), $titleObj->getPrefixedText())); } $newContent = $contentHandler->getUndoContent($pageObj->getRevision(), $undoRev, $undoafterRev); if (!$newContent) { $this->dieUsageMsg('undo-failure'); } $params['text'] = $newContent->serialize($params['contentformat']); // If no summary was given and we only undid one rev, // use an autosummary if (is_null($params['summary']) && $titleObj->getNextRevisionID($undoafterRev->getID()) == $params['undo']) { $params['summary'] = wfMessage('undo-summary', $params['undo'], $undoRev->getUserText())->inContentLanguage()->text(); } } // See if the MD5 hash checks out if (!is_null($params['md5']) && md5($toMD5) !== $params['md5']) { $this->dieUsageMsg('hashcheckfailed'); } // EditPage wants to parse its stuff from a WebRequest // That interface kind of sucks, but it's workable $requestArray = array('wpTextbox1' => $params['text'], 'format' => $contentFormat, 'model' => $contentHandler->getModelID(), 'wpEditToken' => $params['token'], 'wpIgnoreBlankSummary' => ''); if (!is_null($params['summary'])) { $requestArray['wpSummary'] = $params['summary']; } if (!is_null($params['sectiontitle'])) { $requestArray['wpSectionTitle'] = $params['sectiontitle']; } // TODO: Pass along information from 'undoafter' as well if ($params['undo'] > 0) { $requestArray['wpUndidRevision'] = $params['undo']; } // Watch out for basetimestamp == '' // wfTimestamp() treats it as NOW, almost certainly causing an edit conflict if (!is_null($params['basetimestamp']) && $params['basetimestamp'] != '') { $requestArray['wpEdittime'] = wfTimestamp(TS_MW, $params['basetimestamp']); } else { $requestArray['wpEdittime'] = $pageObj->getTimestamp(); } if (!is_null($params['starttimestamp']) && $params['starttimestamp'] != '') { $requestArray['wpStarttime'] = wfTimestamp(TS_MW, $params['starttimestamp']); } else { $requestArray['wpStarttime'] = wfTimestampNow(); // Fake wpStartime } if ($params['minor'] || !$params['notminor'] && $user->getOption('minordefault')) { $requestArray['wpMinoredit'] = ''; } if ($params['recreate']) { $requestArray['wpRecreate'] = ''; } if (!is_null($params['section'])) { $section = intval($params['section']); if ($section == 0 && $params['section'] != '0' && $params['section'] != 'new') { $this->dieUsage("The section parameter must be set to an integer or 'new'", "invalidsection"); } $requestArray['wpSection'] = $params['section']; } else { $requestArray['wpSection'] = ''; } $watch = $this->getWatchlistValue($params['watchlist'], $titleObj); // Deprecated parameters if ($params['watch']) { $watch = true; } elseif ($params['unwatch']) { $watch = false; } if ($watch) { $requestArray['wpWatchthis'] = ''; } global $wgTitle, $wgRequest; $req = new DerivativeRequest($this->getRequest(), $requestArray, true); // Some functions depend on $wgTitle == $ep->mTitle // TODO: Make them not or check if they still do $wgTitle = $titleObj; $articleContext = new RequestContext(); $articleContext->setRequest($req); $articleContext->setWikiPage($pageObj); $articleContext->setUser($this->getUser()); /** @var $articleObject Article */ $articleObject = Article::newFromWikiPage($pageObj, $articleContext); $ep = new EditPage($articleObject); // allow editing of non-textual content. $ep->allowNonTextContent = true; $ep->setContextTitle($titleObj); $ep->importFormData($req); // Run hooks // Handle APIEditBeforeSave parameters $r = array(); if (!wfRunHooks('APIEditBeforeSave', array($ep, $ep->textbox1, &$r))) { if (count($r)) { $r['result'] = 'Failure'; $apiResult->addValue(null, $this->getModuleName(), $r); return; } else { $this->dieUsageMsg('hookaborted'); } } // Do the actual save $oldRevId = $articleObject->getRevIdFetched(); $result = null; // Fake $wgRequest for some hooks inside EditPage // @todo FIXME: This interface SUCKS $oldRequest = $wgRequest; $wgRequest = $req; $status = $ep->internalAttemptSave($result, $user->isAllowed('bot') && $params['bot']); $wgRequest = $oldRequest; global $wgMaxArticleSize; switch ($status->value) { case EditPage::AS_HOOK_ERROR: case EditPage::AS_HOOK_ERROR_EXPECTED: $this->dieUsageMsg('hookaborted'); case EditPage::AS_PARSE_ERROR: $this->dieUsage($status->getMessage(), 'parseerror'); case EditPage::AS_IMAGE_REDIRECT_ANON: $this->dieUsageMsg('noimageredirect-anon'); case EditPage::AS_IMAGE_REDIRECT_LOGGED: $this->dieUsageMsg('noimageredirect-logged'); case EditPage::AS_SPAM_ERROR: $this->dieUsageMsg(array('spamdetected', $result['spam'])); case EditPage::AS_BLOCKED_PAGE_FOR_USER: $this->dieUsageMsg('blockedtext'); case EditPage::AS_MAX_ARTICLE_SIZE_EXCEEDED: case EditPage::AS_CONTENT_TOO_BIG: $this->dieUsageMsg(array('contenttoobig', $wgMaxArticleSize)); case EditPage::AS_READ_ONLY_PAGE_ANON: $this->dieUsageMsg('noedit-anon'); case EditPage::AS_READ_ONLY_PAGE_LOGGED: $this->dieUsageMsg('noedit'); case EditPage::AS_READ_ONLY_PAGE: $this->dieReadOnly(); case EditPage::AS_RATE_LIMITED: $this->dieUsageMsg('actionthrottledtext'); case EditPage::AS_ARTICLE_WAS_DELETED: $this->dieUsageMsg('wasdeleted'); case EditPage::AS_NO_CREATE_PERMISSION: $this->dieUsageMsg('nocreate-loggedin'); case EditPage::AS_BLANK_ARTICLE: $this->dieUsageMsg('blankpage'); case EditPage::AS_CONFLICT_DETECTED: $this->dieUsageMsg('editconflict'); // case EditPage::AS_SUMMARY_NEEDED: Can't happen since we set wpIgnoreBlankSummary // case EditPage::AS_SUMMARY_NEEDED: Can't happen since we set wpIgnoreBlankSummary case EditPage::AS_TEXTBOX_EMPTY: $this->dieUsageMsg('emptynewsection'); case EditPage::AS_SUCCESS_NEW_ARTICLE: $r['new'] = ''; // fall-through // fall-through case EditPage::AS_SUCCESS_UPDATE: $r['result'] = 'Success'; $r['pageid'] = intval($titleObj->getArticleID()); $r['title'] = $titleObj->getPrefixedText(); $r['contentmodel'] = $titleObj->getContentModel(); $newRevId = $articleObject->getLatest(); if ($newRevId == $oldRevId) { $r['nochange'] = ''; } else { $r['oldrevid'] = intval($oldRevId); $r['newrevid'] = intval($newRevId); $r['newtimestamp'] = wfTimestamp(TS_ISO_8601, $pageObj->getTimestamp()); } break; case EditPage::AS_SUMMARY_NEEDED: $this->dieUsageMsg('summaryrequired'); case EditPage::AS_END: default: // $status came from WikiPage::doEdit() $errors = $status->getErrorsArray(); $this->dieUsageMsg($errors[0]); // TODO: Add new errors to message map break; } $apiResult->addValue(null, $this->getModuleName(), $r); }
/** * Create a new RequestContext object to use e.g. for calls to other parts * the software. * The object will have the WebRequest and the User object set to the ones * used in this instance. * * @return RequestContext */ public function createContext() { global $wgUser; $context = new RequestContext(); $context->setRequest($this->getMain()->getRequest()); $context->setUser($wgUser); /// @todo FIXME: we should store the User object return $context; }