Пример #1
0
 function setUp()
 {
     global $wgContLang, $wgUser, $wgLanguageCode;
     $wgContLang = Language::factory($wgLanguageCode);
     $this->popts = ParserOptions::newFromUserAndLang($wgUser, $wgContLang);
     $this->pcache = ParserCache::singleton();
 }
 /**
  * returns the xml output of the sequence with all wiki-text templates/magic words swapped out
  * also resolves all image and media locations with absolute paths.
  */
 function getSequenceSMIL()
 {
     global $wgParser, $wgOut, $wgUser, $wgEnableParserCache;
     //temporally stop cache:
     $wgEnableParserCache = false;
     $parserOptions = ParserOptions::newFromUser($wgUser);
     $parserOptions->addExtraKey('mv:seq-xml');
     //differentiate the articles xml from article
     if ($wgEnableParserCache) {
         $mvParserCache = ParserCache::singleton();
         $parserOutput = $mvParserCache->get($this, $parserOptions);
         if ($parserOutput != false) {
             return $parserOutput->getText();
         }
     }
     //get the high level sequence description:
     $this->getSequenceHLRD();
     $this->parseHLRD_DOM();
     //this is the heavy lifting of the getSequenceSMIL function:
     $this->resolveHLRD_to_SMIL();
     //@@todo get parser Output Object (maybe cleaner way to do this?
     //maybe parser cache is not the right place to cache the sequence xml? )
     $parserOutput = $wgParser->parse('', $this->mTitle, $parserOptions);
     //output header:
     $parserOutput->mText .= $this->smilDoc->saveXML();
     //save to cache if parser cache enabled:
     if ($wgEnableParserCache) {
         $mvParserCache->save($parserOutput, $this, $parserOptions);
     }
     return $parserOutput->getText();
 }
Пример #3
0
 /**
  * Purges the cache of a page
  */
 public function execute()
 {
     $params = $this->extractRequestParams();
     $continuationManager = new ApiContinuationManager($this, array(), array());
     $this->setContinuationManager($continuationManager);
     $forceLinkUpdate = $params['forcelinkupdate'];
     $forceRecursiveLinkUpdate = $params['forcerecursivelinkupdate'];
     $pageSet = $this->getPageSet();
     $pageSet->execute();
     $result = $pageSet->getInvalidTitlesAndRevisions();
     foreach ($pageSet->getGoodTitles() as $title) {
         $r = array();
         ApiQueryBase::addTitleInfo($r, $title);
         $page = WikiPage::factory($title);
         $page->doPurge();
         // Directly purge and skip the UI part of purge().
         $r['purged'] = true;
         if ($forceLinkUpdate || $forceRecursiveLinkUpdate) {
             if (!$this->getUser()->pingLimiter('linkpurge')) {
                 $popts = $page->makeParserOptions('canonical');
                 # Parse content; note that HTML generation is only needed if we want to cache the result.
                 $content = $page->getContent(Revision::RAW);
                 $enableParserCache = $this->getConfig()->get('EnableParserCache');
                 $p_result = $content->getParserOutput($title, $page->getLatest(), $popts, $enableParserCache);
                 # Update the links tables
                 $updates = $content->getSecondaryDataUpdates($title, null, $forceRecursiveLinkUpdate, $p_result);
                 DataUpdate::runUpdates($updates);
                 $r['linkupdate'] = true;
                 if ($enableParserCache) {
                     $pcache = ParserCache::singleton();
                     $pcache->save($p_result, $page, $popts);
                 }
             } else {
                 $error = $this->parseMsg(array('actionthrottledtext'));
                 $this->setWarning($error['info']);
                 $forceLinkUpdate = false;
             }
         }
         $result[] = $r;
     }
     $apiResult = $this->getResult();
     ApiResult::setIndexedTagName($result, 'page');
     $apiResult->addValue(null, $this->getModuleName(), $result);
     $values = $pageSet->getNormalizedTitlesAsResult($apiResult);
     if ($values) {
         $apiResult->addValue(null, 'normalized', $values);
     }
     $values = $pageSet->getConvertedTitlesAsResult($apiResult);
     if ($values) {
         $apiResult->addValue(null, 'converted', $values);
     }
     $values = $pageSet->getRedirectTitlesAsResult($apiResult);
     if ($values) {
         $apiResult->addValue(null, 'redirects', $values);
     }
     $this->setContinuationManager(null);
     $continuationManager->setContinuationIntoResult($apiResult);
 }
 /**
  * Get template and image versions from parsing a revision
  * @param Page $article
  * @param Revision $rev
  * @param User $user
  * @param string $regen use 'regen' to force regeneration
  * @return array( templateIds, fileSHA1Keys )
  * templateIds like ParserOutput->mTemplateIds
  * fileSHA1Keys like ParserOutput->mImageTimeKeys
  */
 public static function getRevIncludes(Page $article, Revision $rev, User $user, $regen = '')
 {
     global $wgParser, $wgMemc;
     wfProfileIn(__METHOD__);
     $versions = false;
     $key = self::getCacheKey($article->getTitle(), $rev->getId());
     if ($regen !== 'regen') {
         // check cache
         $versions = FlaggedRevs::getMemcValue($wgMemc->get($key), $article, 'allowStale');
     }
     if (!is_array($versions)) {
         // cache miss
         $pOut = false;
         if ($rev->isCurrent()) {
             $parserCache = ParserCache::singleton();
             # Try current version parser cache (as anon)...
             $pOut = $parserCache->get($article, $article->makeParserOptions($user));
             if ($pOut == false && $rev->getUser()) {
                 // try the user who saved the change
                 $author = User::newFromId($rev->getUser());
                 $pOut = $parserCache->get($article, $article->makeParserOptions($author));
             }
         }
         // ParserOutput::mImageTimeKeys wasn't always there
         if ($pOut == false || !FlaggedRevs::parserOutputIsVersioned($pOut)) {
             $title = $article->getTitle();
             $pOpts = ParserOptions::newFromUser($user);
             // Note: tidy off
             $pOut = $wgParser->parse($rev->getText(), $title, $pOpts, true, true, $rev->getId());
         }
         # Get the template/file versions used...
         $versions = array($pOut->getTemplateIds(), $pOut->getFileSearchOptions());
         # Save to cache (check cache expiry for dynamic elements)...
         $data = FlaggedRevs::makeMemcObj($versions);
         $wgMemc->set($key, $data, $pOut->getCacheExpiry());
     } else {
         $tVersions =& $versions[0];
         // templates
         # Do a link batch query for page_latest...
         $lb = new LinkBatch();
         foreach ($tVersions as $ns => $tmps) {
             foreach ($tmps as $dbKey => $revIdDraft) {
                 $lb->add($ns, $dbKey);
             }
         }
         $lb->execute();
         # Update array with the current page_latest values.
         # This kludge is there since $newTemplates (thus $revIdDraft) is cached.
         foreach ($tVersions as $ns => &$tmps) {
             foreach ($tmps as $dbKey => &$revIdDraft) {
                 $title = Title::makeTitle($ns, $dbKey);
                 $revIdDraft = (int) $title->getLatestRevID();
             }
         }
     }
     wfProfileOut(__METHOD__);
     return $versions;
 }
 /**
  * Get template and image versions from parsing a revision
  * @param Page $article
  * @param Revision $rev
  * @param User $user
  * @param string $regen use 'regen' to force regeneration
  * @return array( templateIds, fileSHA1Keys )
  * templateIds like ParserOutput->mTemplateIds
  * fileSHA1Keys like ParserOutput->mImageTimeKeys
  */
 public static function getRevIncludes(Page $article, Revision $rev, User $user, $regen = '')
 {
     global $wgMemc;
     wfProfileIn(__METHOD__);
     $key = self::getCacheKey($article->getTitle(), $rev->getId());
     if ($regen === 'regen') {
         $versions = false;
         // skip cache
     } elseif ($rev->isCurrent()) {
         // Check cache entry against page_touched
         $versions = FlaggedRevs::getMemcValue($wgMemc->get($key), $article);
     } else {
         // Old revs won't always be invalidated with template/file changes.
         // Also, we don't care if page_touched changed due to a direct edit.
         $versions = FlaggedRevs::getMemcValue($wgMemc->get($key), $article, 'allowStale');
         if (is_array($versions)) {
             // entry exists
             // Sanity check that the cache is reasonably up to date
             list($templates, $files) = $versions;
             if (self::templatesStale($templates) || self::filesStale($files)) {
                 $versions = false;
                 // no good
             }
         }
     }
     if (!is_array($versions)) {
         // cache miss
         $pOut = false;
         if ($rev->isCurrent()) {
             $parserCache = ParserCache::singleton();
             # Try current version parser cache for this user...
             $pOut = $parserCache->get($article, $article->makeParserOptions($user));
             if ($pOut == false) {
                 # Try current version parser cache for the revision author...
                 $optsUser = $rev->getUser() ? User::newFromId($rev->getUser()) : 'canonical';
                 $pOut = $parserCache->get($article, $article->makeParserOptions($optsUser));
             }
         }
         // ParserOutput::mImageTimeKeys wasn't always there
         if ($pOut == false || !FlaggedRevs::parserOutputIsVersioned($pOut)) {
             $content = $rev->getContent(Revision::RAW);
             if (!$content) {
                 // Just for extra sanity
                 $pOut = new ParserOutput();
             } else {
                 $pOut = $content->getParserOutput($article->getTitle(), $rev->getId(), ParserOptions::newFromUser($user));
             }
         }
         # Get the template/file versions used...
         $versions = array($pOut->getTemplateIds(), $pOut->getFileSearchOptions());
         # Save to cache (check cache expiry for dynamic elements)...
         $data = FlaggedRevs::makeMemcObj($versions);
         $wgMemc->set($key, $data, $pOut->getCacheExpiry());
     }
     wfProfileOut(__METHOD__);
     return $versions;
 }
 protected function setUp()
 {
     global $wgLanguageCode, $wgUser;
     parent::setUp();
     $langObj = Language::factory($wgLanguageCode);
     $this->setMwGlobals(array('wgContLang' => $langObj, 'wgUseDynamicDates' => true));
     $this->popts = ParserOptions::newFromUserAndLang($wgUser, $langObj);
     $this->pcache = ParserCache::singleton();
 }
Пример #7
0
 function setUp()
 {
     ParserTest::setUp();
     //reuse setup from parser tests
     global $wgContLang, $wgUser, $wgLanguageCode;
     $wgContLang = Language::factory($wgLanguageCode);
     $this->popts = new ParserOptions($wgUser);
     $this->pcache = ParserCache::singleton();
 }
Пример #8
0
 /**
  * Purges the cache of a page
  */
 public function execute()
 {
     global $wgUser;
     $params = $this->extractRequestParams();
     if (!$wgUser->isAllowed('purge') && !$this->getMain()->isInternalMode() && !$this->getMain()->getRequest()->wasPosted()) {
         $this->dieUsageMsg(array('mustbeposted', $this->getModuleName()));
     }
     $forceLinkUpdate = $params['forcelinkupdate'];
     $result = array();
     foreach ($params['titles'] as $t) {
         $r = array();
         $title = Title::newFromText($t);
         if (!$title instanceof Title) {
             $r['title'] = $t;
             $r['invalid'] = '';
             $result[] = $r;
             continue;
         }
         ApiQueryBase::addTitleInfo($r, $title);
         if (!$title->exists()) {
             $r['missing'] = '';
             $result[] = $r;
             continue;
         }
         $context = $this->createContext();
         $context->setTitle($title);
         $article = Article::newFromTitle($title, $context);
         $article->doPurge();
         // Directly purge and skip the UI part of purge().
         $r['purged'] = '';
         if ($forceLinkUpdate) {
             if (!$wgUser->pingLimiter()) {
                 global $wgParser, $wgEnableParserCache;
                 $popts = new ParserOptions();
                 $p_result = $wgParser->parse($article->getContent(), $title, $popts);
                 # Update the links tables
                 $u = new LinksUpdate($title, $p_result);
                 $u->doUpdate();
                 $r['linkupdate'] = '';
                 if ($wgEnableParserCache) {
                     $pcache = ParserCache::singleton();
                     $pcache->save($p_result, $article, $popts);
                 }
             } else {
                 $this->setWarning($this->parseMsg(array('actionthrottledtext')));
                 $forceLinkUpdate = false;
             }
         }
         $result[] = $r;
     }
     $apiResult = $this->getResult();
     $apiResult->setIndexedTagName($result, 'page');
     $apiResult->addValue(null, $this->getModuleName(), $result);
 }
Пример #9
0
 public function execute()
 {
     $pages = $this->getOption('maxpages');
     $dbr = $this->getDB(DB_REPLICA);
     $totalsec = 0.0;
     $scanned = 0;
     $withcache = 0;
     $withdiff = 0;
     while ($pages-- > 0) {
         $row = $dbr->selectRow('page', '*', ['page_namespace' => $this->getOption('namespace'), 'page_is_redirect' => 0, 'page_random >= ' . wfRandom()], __METHOD__, ['ORDER BY' => 'page_random']);
         if (!$row) {
             continue;
         }
         ++$scanned;
         $title = Title::newFromRow($row);
         $page = WikiPage::factory($title);
         $revision = $page->getRevision();
         $content = $revision->getContent(Revision::RAW);
         $parserOptions = $page->makeParserOptions('canonical');
         $parserOutputOld = ParserCache::singleton()->get($page, $parserOptions);
         if ($parserOutputOld) {
             $t1 = microtime(true);
             $parserOutputNew = $content->getParserOutput($title, $revision->getId(), $parserOptions, false);
             $sec = microtime(true) - $t1;
             $totalsec += $sec;
             $this->output("Parsed '{$title->getPrefixedText()}' in {$sec} seconds.\n");
             $this->output("Found cache entry found for '{$title->getPrefixedText()}'...");
             $oldHtml = trim(preg_replace('#<!-- .+-->#Us', '', $parserOutputOld->getText()));
             $newHtml = trim(preg_replace('#<!-- .+-->#Us', '', $parserOutputNew->getText()));
             $diff = wfDiff($oldHtml, $newHtml);
             if (strlen($diff)) {
                 $this->output("differences found:\n\n{$diff}\n\n");
                 ++$withdiff;
             } else {
                 $this->output("No differences found.\n");
             }
             ++$withcache;
         } else {
             $this->output("No parser cache entry found for '{$title->getPrefixedText()}'.\n");
         }
     }
     $ave = $totalsec ? $totalsec / $scanned : 0;
     $this->output("Checked {$scanned} pages; {$withcache} had prior cache entries.\n");
     $this->output("Pages with differences found: {$withdiff}\n");
     $this->output("Average parse time: {$ave} sec\n");
 }
Пример #10
0
 protected function getText($title)
 {
     $titleObject = Title::newFromText($title);
     $article = new Article($titleObject);
     // make sure the article exists.
     if ($article->getId() == 0) {
         return null;
     }
     // prepare the parser cache for action.
     $parserCache =& ParserCache::singleton();
     global $wgUser;
     $parserOutput = $parserCache->get($article, $wgUser);
     // did we find it in the parser cache?
     if ($parserOutput !== false) {
         return $parserOutput->getText();
     }
     // no... that's too bad; go the long way then.
     $rev = Revision::newFromTitle($titleObject);
     if (is_object($rev)) {
         return $this->parse($titleObject, $rev->getText());
     }
     return null;
 }
Пример #11
0
	/**
	 * @return bool
	 */
	function fallback() {
		$this->parserOutput = ParserCache::singleton()->getDirty( $this->page, $this->parserOptions );

		if ( $this->parserOutput === false ) {
			wfDebugLog( 'dirty', "dirty missing\n" );
			wfDebug( __METHOD__ . ": no dirty cache\n" );
			return false;
		} else {
			wfDebug( __METHOD__ . ": sending dirty output\n" );
			wfDebugLog( 'dirty', "dirty output {$this->cacheKey}\n" );
			$this->isDirty = true;
			return true;
		}
	}
Пример #12
0
 /**
  * This is the default action of the index.php entry point: just view the
  * page of the given title.
  */
 public function view()
 {
     global $wgUseFileCache, $wgUseETag, $wgDebugToolbar, $wgMaxRedirects;
     # Get variables from query string
     # As side effect this will load the revision and update the title
     # in a revision ID is passed in the request, so this should remain
     # the first call of this method even if $oldid is used way below.
     $oldid = $this->getOldID();
     $user = $this->getContext()->getUser();
     # Another whitelist check in case getOldID() is altering the title
     $permErrors = $this->getTitle()->getUserPermissionsErrors('read', $user);
     if (count($permErrors)) {
         wfDebug(__METHOD__ . ": denied on secondary read check\n");
         throw new PermissionsError('read', $permErrors);
     }
     $outputPage = $this->getContext()->getOutput();
     # getOldID() may as well want us to redirect somewhere else
     if ($this->mRedirectUrl) {
         $outputPage->redirect($this->mRedirectUrl);
         wfDebug(__METHOD__ . ": redirecting due to oldid\n");
         return;
     }
     # If we got diff in the query, we want to see a diff page instead of the article.
     if ($this->getContext()->getRequest()->getCheck('diff')) {
         wfDebug(__METHOD__ . ": showing diff page\n");
         $this->showDiffPage();
         return;
     }
     # Set page title (may be overridden by DISPLAYTITLE)
     $outputPage->setPageTitle($this->getTitle()->getPrefixedText());
     $outputPage->setArticleFlag(true);
     # Allow frames by default
     $outputPage->allowClickjacking();
     $parserCache = ParserCache::singleton();
     $parserOptions = $this->getParserOptions();
     # Render printable version, use printable version cache
     if ($outputPage->isPrintable()) {
         $parserOptions->setIsPrintable(true);
         $parserOptions->setEditSection(false);
     } elseif (!$this->isCurrent() || !$this->getTitle()->quickUserCan('edit', $user)) {
         $parserOptions->setEditSection(false);
     }
     # Try client and file cache
     if (!$wgDebugToolbar && $oldid === 0 && $this->mPage->checkTouched()) {
         if ($wgUseETag) {
             $outputPage->setETag($parserCache->getETag($this, $parserOptions));
         }
         # Use the greatest of the page's timestamp or the timestamp of any
         # redirect in the chain (bug 67849)
         $timestamp = $this->mPage->getTouched();
         if (isset($this->mRedirectedFrom)) {
             $timestamp = max($timestamp, $this->mRedirectedFrom->getTouched());
             # If there can be more than one redirect in the chain, we have
             # to go through the whole chain too in case an intermediate
             # redirect was changed.
             if ($wgMaxRedirects > 1) {
                 $titles = Revision::newFromTitle($this->mRedirectedFrom)->getContent(Revision::FOR_THIS_USER, $user)->getRedirectChain();
                 $thisTitle = $this->getTitle();
                 foreach ($titles as $title) {
                     if (Title::compare($title, $thisTitle) === 0) {
                         break;
                     }
                     $timestamp = max($timestamp, $title->getTouched());
                 }
             }
         }
         # Is it client cached?
         if ($outputPage->checkLastModified($timestamp)) {
             wfDebug(__METHOD__ . ": done 304\n");
             return;
             # Try file cache
         } elseif ($wgUseFileCache && $this->tryFileCache()) {
             wfDebug(__METHOD__ . ": done file cache\n");
             # tell wgOut that output is taken care of
             $outputPage->disable();
             $this->mPage->doViewUpdates($user, $oldid);
             return;
         }
     }
     # Should the parser cache be used?
     $useParserCache = $this->mPage->shouldCheckParserCache($parserOptions, $oldid);
     wfDebug('Article::view using parser cache: ' . ($useParserCache ? 'yes' : 'no') . "\n");
     if ($user->getStubThreshold()) {
         $this->getContext()->getStats()->increment('pcache_miss_stub');
     }
     $this->showRedirectedFromHeader();
     $this->showNamespaceHeader();
     # Iterate through the possible ways of constructing the output text.
     # Keep going until $outputDone is set, or we run out of things to do.
     $pass = 0;
     $outputDone = false;
     $this->mParserOutput = false;
     while (!$outputDone && ++$pass) {
         switch ($pass) {
             case 1:
                 Hooks::run('ArticleViewHeader', array(&$this, &$outputDone, &$useParserCache));
                 break;
             case 2:
                 # Early abort if the page doesn't exist
                 if (!$this->mPage->exists()) {
                     wfDebug(__METHOD__ . ": showing missing article\n");
                     $this->showMissingArticle();
                     $this->mPage->doViewUpdates($user);
                     return;
                 }
                 # Try the parser cache
                 if ($useParserCache) {
                     $this->mParserOutput = $parserCache->get($this, $parserOptions);
                     if ($this->mParserOutput !== false) {
                         if ($oldid) {
                             wfDebug(__METHOD__ . ": showing parser cache contents for current rev permalink\n");
                             $this->setOldSubtitle($oldid);
                         } else {
                             wfDebug(__METHOD__ . ": showing parser cache contents\n");
                         }
                         $outputPage->addParserOutput($this->mParserOutput);
                         # Ensure that UI elements requiring revision ID have
                         # the correct version information.
                         $outputPage->setRevisionId($this->mPage->getLatest());
                         # Preload timestamp to avoid a DB hit
                         $cachedTimestamp = $this->mParserOutput->getTimestamp();
                         if ($cachedTimestamp !== null) {
                             $outputPage->setRevisionTimestamp($cachedTimestamp);
                             $this->mPage->setTimestamp($cachedTimestamp);
                         }
                         $outputDone = true;
                     }
                 }
                 break;
             case 3:
                 # This will set $this->mRevision if needed
                 $this->fetchContentObject();
                 # Are we looking at an old revision
                 if ($oldid && $this->mRevision) {
                     $this->setOldSubtitle($oldid);
                     if (!$this->showDeletedRevisionHeader()) {
                         wfDebug(__METHOD__ . ": cannot view deleted revision\n");
                         return;
                     }
                 }
                 # Ensure that UI elements requiring revision ID have
                 # the correct version information.
                 $outputPage->setRevisionId($this->getRevIdFetched());
                 # Preload timestamp to avoid a DB hit
                 $outputPage->setRevisionTimestamp($this->getTimestamp());
                 # Pages containing custom CSS or JavaScript get special treatment
                 if ($this->getTitle()->isCssOrJsPage() || $this->getTitle()->isCssJsSubpage()) {
                     wfDebug(__METHOD__ . ": showing CSS/JS source\n");
                     $this->showCssOrJsPage();
                     $outputDone = true;
                 } elseif (!Hooks::run('ArticleContentViewCustom', array($this->fetchContentObject(), $this->getTitle(), $outputPage))) {
                     # Allow extensions do their own custom view for certain pages
                     $outputDone = true;
                 } elseif (!ContentHandler::runLegacyHooks('ArticleViewCustom', array($this->fetchContentObject(), $this->getTitle(), $outputPage))) {
                     # Allow extensions do their own custom view for certain pages
                     $outputDone = true;
                 }
                 break;
             case 4:
                 # Run the parse, protected by a pool counter
                 wfDebug(__METHOD__ . ": doing uncached parse\n");
                 $content = $this->getContentObject();
                 $poolArticleView = new PoolWorkArticleView($this->getPage(), $parserOptions, $this->getRevIdFetched(), $useParserCache, $content);
                 if (!$poolArticleView->execute()) {
                     $error = $poolArticleView->getError();
                     if ($error) {
                         $outputPage->clearHTML();
                         // for release() errors
                         $outputPage->enableClientCache(false);
                         $outputPage->setRobotPolicy('noindex,nofollow');
                         $errortext = $error->getWikiText(false, 'view-pool-error');
                         $outputPage->addWikiText('<div class="errorbox">' . $errortext . '</div>');
                     }
                     # Connection or timeout error
                     return;
                 }
                 $this->mParserOutput = $poolArticleView->getParserOutput();
                 $outputPage->addParserOutput($this->mParserOutput);
                 if ($content->getRedirectTarget()) {
                     $outputPage->addSubtitle("<span id=\"redirectsub\">" . $this->getContext()->msg('redirectpagesub')->parse() . "</span>");
                 }
                 # Don't cache a dirty ParserOutput object
                 if ($poolArticleView->getIsDirty()) {
                     $outputPage->setSquidMaxage(0);
                     $outputPage->addHTML("<!-- parser cache is expired, " . "sending anyway due to pool overload-->\n");
                 }
                 $outputDone = true;
                 break;
                 # Should be unreachable, but just in case...
             # Should be unreachable, but just in case...
             default:
                 break 2;
         }
     }
     # Get the ParserOutput actually *displayed* here.
     # Note that $this->mParserOutput is the *current*/oldid version output.
     $pOutput = $outputDone instanceof ParserOutput ? $outputDone : $this->mParserOutput;
     # Adjust title for main page & pages with displaytitle
     if ($pOutput) {
         $this->adjustDisplayTitle($pOutput);
     }
     # For the main page, overwrite the <title> element with the con-
     # tents of 'pagetitle-view-mainpage' instead of the default (if
     # that's not empty).
     # This message always exists because it is in the i18n files
     if ($this->getTitle()->isMainPage()) {
         $msg = wfMessage('pagetitle-view-mainpage')->inContentLanguage();
         if (!$msg->isDisabled()) {
             $outputPage->setHTMLTitle($msg->title($this->getTitle())->text());
         }
     }
     # Check for any __NOINDEX__ tags on the page using $pOutput
     $policy = $this->getRobotPolicy('view', $pOutput);
     $outputPage->setIndexPolicy($policy['index']);
     $outputPage->setFollowPolicy($policy['follow']);
     $this->showViewFooter();
     $this->mPage->doViewUpdates($user, $oldid);
     $outputPage->addModules('mediawiki.action.view.postEdit');
 }
Пример #13
0
 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'];
     $page = $params['page'];
     $oldid = $params['oldid'];
     if (!is_null($page) && (!is_null($text) || $title != "API")) {
         $this->dieUsage("The page parameter cannot be used together with the text and title parameters", 'params');
     }
     $prop = array_flip($params['prop']);
     $revid = false;
     // The parser needs $wgTitle to be set, apparently the
     // $title parameter in Parser::parse isn't enough *sigh*
     global $wgParser, $wgUser, $wgTitle, $wgEnableParserCache;
     $popts = new ParserOptions();
     $popts->setTidy(true);
     $popts->enableLimitReport();
     $redirValues = null;
     if (!is_null($oldid) || !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');
             }
             if (!$rev->userCan(Revision::DELETED_TEXT)) {
                 $this->dieUsage("You don't have permission to view deleted revisions", 'permissiondenied');
             }
             $text = $rev->getText(Revision::FOR_THIS_USER);
             $titleObj = $rev->getTitle();
             $wgTitle = $titleObj;
             $p_result = $wgParser->parse($text, $titleObj, $popts);
         } else {
             if ($params['redirects']) {
                 $req = new FauxRequest(array('action' => 'query', 'redirects' => '', 'titles' => $page));
                 $main = new ApiMain($req);
                 $main->execute();
                 $data = $main->getResultData();
                 $redirValues = @$data['query']['redirects'];
                 $to = $page;
                 foreach ((array) $redirValues as $r) {
                     $to = $r['to'];
                 }
             } else {
                 $to = $page;
             }
             $titleObj = Title::newFromText($to);
             if (!$titleObj) {
                 $this->dieUsage("The page you specified doesn't exist", 'missingtitle');
             }
             $articleObj = new Article($titleObj);
             if (isset($prop['revid'])) {
                 $oldid = $articleObj->getRevIdFetched();
             }
             // Try the parser cache first
             $p_result = false;
             $pcache = ParserCache::singleton();
             if ($wgEnableParserCache) {
                 $p_result = $pcache->get($articleObj, $wgUser);
             }
             if (!$p_result) {
                 $p_result = $wgParser->parse($articleObj->getContent(), $titleObj, $popts);
                 if ($wgEnableParserCache) {
                     $pcache->save($p_result, $articleObj, $popts);
                 }
             }
         }
     } else {
         $titleObj = Title::newFromText($title);
         if (!$titleObj) {
             $titleObj = Title::newFromText("API");
         }
         $wgTitle = $titleObj;
         if ($params['pst'] || $params['onlypst']) {
             $text = $wgParser->preSaveTransform($text, $titleObj, $wgUser, $popts);
         }
         if ($params['onlypst']) {
             // Build a result and bail out
             $result_array['text'] = array();
             $this->getResult()->setContent($result_array['text'], $text);
             $this->getResult()->addValue(null, $this->getModuleName(), $result_array);
             return;
         }
         $p_result = $wgParser->parse($text, $titleObj, $popts);
     }
     // Return result
     $result = $this->getResult();
     $result_array = array();
     if ($params['redirects'] && !is_null($redirValues)) {
         $result_array['redirects'] = $redirValues;
     }
     if (isset($prop['text'])) {
         $result_array['text'] = array();
         $result->setContent($result_array['text'], $p_result->getText());
     }
     if (!is_null($params['summary'])) {
         $result_array['parsedsummary'] = array();
         $result->setContent($result_array['parsedsummary'], $wgUser->getSkin()->formatComment($params['summary'], $titleObj));
     }
     if (isset($prop['langlinks'])) {
         $result_array['langlinks'] = $this->formatLangLinks($p_result->getLanguageLinks());
     }
     if (isset($prop['categories'])) {
         $result_array['categories'] = $this->formatCategoryLinks($p_result->getCategories());
     }
     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'])) {
         $result_array['headitems'] = $this->formatHeadItems($p_result->getHeadItems());
     }
     if (isset($prop['headhtml'])) {
         $out = new OutputPage();
         $out->addParserOutputNoText($p_result);
         $result_array['headhtml'] = array();
         $result->setContent($result_array['headhtml'], $out->headElement($wgUser->getSkin()));
     }
     if (!is_null($oldid)) {
         $result_array['revid'] = intval($oldid);
     }
     $result_mapping = array('redirects' => 'r', 'langlinks' => 'll', 'categories' => 'cl', 'links' => 'pl', 'templates' => 'tl', 'images' => 'img', 'externallinks' => 'el', 'sections' => 's', 'headitems' => 'hi');
     $this->setIndexedTagNames($result_array, $result_mapping);
     $result->addValue(null, $this->getModuleName(), $result_array);
 }
Пример #14
0
 /**
  * Do standard deferred updates after page edit.
  * Update links tables, site stats, search index and message cache.
  * Purges pages that include this page if the text was changed here.
  * Every 100th edit, prune the recent changes table.
  *
  * @param Revision $revision
  * @param User $user User object that did the revision
  * @param array $options Array of options, following indexes are used:
  * - changed: boolean, whether the revision changed the content (default true)
  * - created: boolean, whether the revision created the page (default false)
  * - moved: boolean, whether the page was moved (default false)
  * - oldcountable: boolean, null, or string 'no-change' (default null):
  *   - boolean: whether the page was counted as an article before that
  *     revision, only used in changed is true and created is false
  *   - null: if created is false, don't update the article count; if created
  *     is true, do update the article count
  *   - 'no-change': don't update the article count, ever
  */
 public function doEditUpdates(Revision $revision, User $user, array $options = array())
 {
     $options += array('changed' => true, 'created' => false, 'moved' => false, 'oldcountable' => null);
     $content = $revision->getContent();
     // Parse the text
     // Be careful not to do pre-save transform twice: $text is usually
     // already pre-save transformed once.
     if (!$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag('vary-revision')) {
         wfDebug(__METHOD__ . ": No prepared edit or vary-revision is set...\n");
         $editInfo = $this->prepareContentForEdit($content, $revision, $user);
     } else {
         wfDebug(__METHOD__ . ": No vary-revision, using prepared edit...\n");
         $editInfo = $this->mPreparedEdit;
     }
     // Save it to the parser cache.
     // Make sure the cache time matches page_touched to avoid double parsing.
     ParserCache::singleton()->save($editInfo->output, $this, $editInfo->popts, $revision->getTimestamp(), $editInfo->revid);
     // Update the links tables and other secondary data
     if ($content) {
         $recursive = $options['changed'];
         // bug 50785
         $updates = $content->getSecondaryDataUpdates($this->getTitle(), null, $recursive, $editInfo->output);
         foreach ($updates as $update) {
             if ($update instanceof LinksUpdate) {
                 $update->setRevision($revision);
             }
             DeferredUpdates::addUpdate($update);
         }
     }
     Hooks::run('ArticleEditUpdates', array(&$this, &$editInfo, $options['changed']));
     if (Hooks::run('ArticleEditUpdatesDeleteFromRecentchanges', array(&$this))) {
         // Flush old entries from the `recentchanges` table
         if (mt_rand(0, 9) == 0) {
             JobQueueGroup::singleton()->lazyPush(RecentChangesUpdateJob::newPurgeJob());
         }
     }
     if (!$this->exists()) {
         return;
     }
     $id = $this->getId();
     $title = $this->mTitle->getPrefixedDBkey();
     $shortTitle = $this->mTitle->getDBkey();
     if ($options['oldcountable'] === 'no-change' || !$options['changed'] && !$options['moved']) {
         $good = 0;
     } elseif ($options['created']) {
         $good = (int) $this->isCountable($editInfo);
     } elseif ($options['oldcountable'] !== null) {
         $good = (int) $this->isCountable($editInfo) - (int) $options['oldcountable'];
     } else {
         $good = 0;
     }
     $edits = $options['changed'] ? 1 : 0;
     $total = $options['created'] ? 1 : 0;
     DeferredUpdates::addUpdate(new SiteStatsUpdate(0, $edits, $good, $total));
     DeferredUpdates::addUpdate(new SearchUpdate($id, $title, $content));
     // If this is another user's talk page, update newtalk.
     // Don't do this if $options['changed'] = false (null-edits) nor if
     // it's a minor edit and the user doesn't want notifications for those.
     if ($options['changed'] && $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $user->getTitleKey() && !($revision->isMinor() && $user->isAllowed('nominornewtalk'))) {
         $recipient = User::newFromName($shortTitle, false);
         if (!$recipient) {
             wfDebug(__METHOD__ . ": invalid username\n");
         } else {
             // Allow extensions to prevent user notification
             // when a new message is added to their talk page
             if (Hooks::run('ArticleEditUpdateNewTalk', array(&$this, $recipient))) {
                 if (User::isIP($shortTitle)) {
                     // An anonymous user
                     $recipient->setNewtalk(true, $revision);
                 } elseif ($recipient->isLoggedIn()) {
                     $recipient->setNewtalk(true, $revision);
                 } else {
                     wfDebug(__METHOD__ . ": don't need to notify a nonexistent user\n");
                 }
             }
         }
     }
     if ($this->mTitle->getNamespace() == NS_MEDIAWIKI) {
         // XXX: could skip pseudo-messages like js/css here, based on content model.
         $msgtext = $content ? $content->getWikitextForTransclusion() : null;
         if ($msgtext === false || $msgtext === null) {
             $msgtext = '';
         }
         MessageCache::singleton()->replace($shortTitle, $msgtext);
     }
     if ($options['created']) {
         self::onArticleCreate($this->mTitle);
     } elseif ($options['changed']) {
         // bug 50785
         self::onArticleEdit($this->mTitle, $revision);
     }
 }
Пример #15
0
 /**
  * @param Article $article
  * @param User    $user
  *
  * @return bool True if successful, else false.
  */
 public function tryParserCache(&$article, $user)
 {
     $parserCache = ParserCache::singleton();
     $parserOutput = $parserCache->get($article, $user);
     if ($parserOutput !== false) {
         $this->addParserOutput($parserOutput);
         return true;
     } else {
         return false;
     }
 }
 /**
  * @param Title $title
  * @return bool
  */
 protected function runForTitle(Title $title = null)
 {
     $linkCache = LinkCache::singleton();
     $linkCache->clear();
     if (is_null($title)) {
         $this->setLastError("refreshLinks: Invalid title");
         return false;
     }
     // Wait for the DB of the current/next slave DB handle to catch up to the master.
     // This way, we get the correct page_latest for templates or files that just changed
     // milliseconds ago, having triggered this job to begin with.
     if (isset($this->params['masterPos']) && $this->params['masterPos'] !== false) {
         wfGetLB()->waitFor($this->params['masterPos']);
     }
     $page = WikiPage::factory($title);
     // Fetch the current revision...
     $revision = Revision::newFromTitle($title, false, Revision::READ_NORMAL);
     if (!$revision) {
         $this->setLastError("refreshLinks: Article not found {$title->getPrefixedDBkey()}");
         return false;
         // XXX: what if it was just deleted?
     }
     $content = $revision->getContent(Revision::RAW);
     if (!$content) {
         // If there is no content, pretend the content is empty
         $content = $revision->getContentHandler()->makeEmptyContent();
     }
     $parserOutput = false;
     $parserOptions = $page->makeParserOptions('canonical');
     // If page_touched changed after this root job (with a good slave lag skew factor),
     // then it is likely that any views of the pages already resulted in re-parses which
     // are now in cache. This can be reused to avoid expensive parsing in some cases.
     if (isset($this->params['rootJobTimestamp'])) {
         $skewedTimestamp = wfTimestamp(TS_UNIX, $this->params['rootJobTimestamp']) + 5;
         if ($page->getLinksTimestamp() > wfTimestamp(TS_MW, $skewedTimestamp)) {
             // Something already updated the backlinks since this job was made
             return true;
         }
         if ($page->getTouched() > wfTimestamp(TS_MW, $skewedTimestamp)) {
             $parserOutput = ParserCache::singleton()->getDirty($page, $parserOptions);
             if ($parserOutput && $parserOutput->getCacheTime() <= $skewedTimestamp) {
                 $parserOutput = false;
                 // too stale
             }
         }
     }
     // Fetch the current revision and parse it if necessary...
     if ($parserOutput == false) {
         $start = microtime(true);
         // Revision ID must be passed to the parser output to get revision variables correct
         $parserOutput = $content->getParserOutput($title, $revision->getId(), $parserOptions, false);
         $ellapsed = microtime(true) - $start;
         // If it took a long time to render, then save this back to the cache to avoid
         // wasted CPU by other apaches or job runners. We don't want to always save to
         // cache as this can cause high cache I/O and LRU churn when a template changes.
         if ($ellapsed >= self::PARSE_THRESHOLD_SEC && $page->isParserCacheUsed($parserOptions, $revision->getId()) && $parserOutput->isCacheable()) {
             $ctime = wfTimestamp(TS_MW, (int) $start);
             // cache time
             ParserCache::singleton()->save($parserOutput, $page, $parserOptions, $ctime, $revision->getId());
         }
     }
     $updates = $content->getSecondaryDataUpdates($title, null, false, $parserOutput);
     DataUpdate::runUpdates($updates);
     InfoAction::invalidateCache($title);
     return true;
 }
Пример #17
0
 /**
  * Do standard deferred updates after page edit.
  * Update links tables, site stats, search index and message cache.
  * Purges pages that include this page if the text was changed here.
  * Every 100th edit, prune the recent changes table.
  *
  * @private
  * @param $revision Revision object
  * @param $user User object that did the revision
  * @param $options Array of options, following indexes are used:
  * - changed: boolean, whether the revision changed the content (default true)
  * - created: boolean, whether the revision created the page (default false)
  * - oldcountable: boolean or null (default null):
  *   - boolean: whether the page was counted as an article before that
  *     revision, only used in changed is true and created is false
  *   - null: don't change the article count
  */
 public function doEditUpdates(Revision $revision, User $user, array $options = array())
 {
     global $wgDeferredUpdateList, $wgEnableParserCache;
     wfProfileIn(__METHOD__);
     $options += array('changed' => true, 'created' => false, 'oldcountable' => null);
     $text = $revision->getText();
     # Parse the text
     # Be careful not to double-PST: $text is usually already PST-ed once
     if (!$this->mPreparedEdit || $this->mPreparedEdit->output->getFlag('vary-revision')) {
         wfDebug(__METHOD__ . ": No prepared edit or vary-revision is set...\n");
         $editInfo = $this->prepareTextForEdit($text, $revision->getId(), $user);
     } else {
         wfDebug(__METHOD__ . ": No vary-revision, using prepared edit...\n");
         $editInfo = $this->mPreparedEdit;
     }
     # Save it to the parser cache
     if ($wgEnableParserCache) {
         $parserCache = ParserCache::singleton();
         $parserCache->save($editInfo->output, $this, $editInfo->popts);
     }
     # Update the links tables
     $u = new LinksUpdate($this->mTitle, $editInfo->output);
     $u->doUpdate();
     wfRunHooks('ArticleEditUpdates', array(&$this, &$editInfo, $options['changed']));
     if (wfRunHooks('ArticleEditUpdatesDeleteFromRecentchanges', array(&$this))) {
         if (0 == mt_rand(0, 99)) {
             // Flush old entries from the `recentchanges` table; we do this on
             // random requests so as to avoid an increase in writes for no good reason
             global $wgRCMaxAge;
             $dbw = wfGetDB(DB_MASTER);
             $cutoff = $dbw->timestamp(time() - $wgRCMaxAge);
             $dbw->delete('recentchanges', array("rc_timestamp < '{$cutoff}'"), __METHOD__);
         }
     }
     $id = $this->getId();
     $title = $this->mTitle->getPrefixedDBkey();
     $shortTitle = $this->mTitle->getDBkey();
     if (0 == $id) {
         wfProfileOut(__METHOD__);
         return;
     }
     if (!$options['changed']) {
         $good = 0;
         $total = 0;
     } elseif ($options['created']) {
         $good = (int) $this->isCountable($editInfo);
         $total = 1;
     } elseif ($options['oldcountable'] !== null) {
         $good = (int) $this->isCountable($editInfo) - (int) $options['oldcountable'];
         $total = 0;
     } else {
         $good = 0;
         $total = 0;
     }
     $wgDeferredUpdateList[] = new SiteStatsUpdate(0, 1, $good, $total);
     $wgDeferredUpdateList[] = new SearchUpdate($id, $title, $text);
     # If this is another user's talk page, update newtalk.
     # Don't do this if $options['changed'] = false (null-edits) nor if
     # it's a minor edit and the user doesn't want notifications for those.
     if ($options['changed'] && $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $user->getTitleKey() && !($revision->isMinor() && $user->isAllowed('nominornewtalk'))) {
         if (wfRunHooks('ArticleEditUpdateNewTalk', array(&$this))) {
             $other = User::newFromName($shortTitle, false);
             if (!$other) {
                 wfDebug(__METHOD__ . ": invalid username\n");
             } elseif (User::isIP($shortTitle)) {
                 // An anonymous user
                 $other->setNewtalk(true);
             } elseif ($other->isLoggedIn()) {
                 $other->setNewtalk(true);
             } else {
                 wfDebug(__METHOD__ . ": don't need to notify a nonexistent user\n");
             }
         }
     }
     if ($this->mTitle->getNamespace() == NS_MEDIAWIKI) {
         MessageCache::singleton()->replace($shortTitle, $text);
     }
     if ($options['created']) {
         self::onArticleCreate($this->mTitle);
     } else {
         self::onArticleEdit($this->mTitle);
     }
     wfProfileOut(__METHOD__);
 }
 /**
  * Get data of requested article.
  * @param Title $title
  * @param boolean $noImages
  * @return array
  */
 private function getData(Title $title, $noImages)
 {
     global $wgMemc, $wgUseTidy, $wgMFTidyMobileViewSections, $wgMFMinCachedPageSize, $wgMFSpecialCaseMainPage;
     $wp = $this->makeWikiPage($title);
     if ($this->followRedirects && $wp->isRedirect()) {
         $newTitle = $wp->getRedirectTarget();
         if ($newTitle) {
             $title = $newTitle;
             $this->getResult()->addValue(null, $this->getModuleName(), array('redirected' => $title->getPrefixedText()));
             if ($title->getNamespace() < 0) {
                 $this->getResult()->addValue(null, $this->getModuleName(), array('viewable' => 'no'));
                 return array();
             }
             $wp = $this->makeWikiPage($title);
         }
     }
     $latest = $wp->getLatest();
     if ($this->file) {
         $key = wfMemcKey('mf', 'mobileview', self::CACHE_VERSION, $noImages, $latest, $this->noTransform, $this->file->getSha1(), $this->variant);
         $cacheExpiry = 3600;
     } else {
         if (!$latest) {
             // https://bugzilla.wikimedia.org/show_bug.cgi?id=53378
             // Title::exists() above doesn't seem to always catch recently deleted pages
             $this->dieUsageMsg(array('notanarticle', $title->getPrefixedText()));
         }
         $parserOptions = $this->makeParserOptions($wp);
         $parserCacheKey = ParserCache::singleton()->getKey($wp, $parserOptions);
         $key = wfMemcKey('mf', 'mobileview', self::CACHE_VERSION, $noImages, $latest, $this->noTransform, $parserCacheKey);
     }
     $data = $wgMemc->get($key);
     if ($data) {
         wfIncrStats('mobile.view.cache-hit');
         return $data;
     }
     wfIncrStats('mobile.view.cache-miss');
     if ($this->file) {
         $html = $this->getFilePage($title);
     } else {
         $parserOutput = $this->getParserOutput($wp, $parserOptions);
         $html = $parserOutput->getText();
         $cacheExpiry = $parserOutput->getCacheExpiry();
     }
     if (!$this->noTransform) {
         $mf = new MobileFormatter(MobileFormatter::wrapHTML($html), $title);
         $mf->setRemoveMedia($noImages);
         $mf->filterContent();
         $mf->setIsMainPage($this->mainPage && $wgMFSpecialCaseMainPage);
         $html = $mf->getText();
     }
     if ($this->mainPage || $this->file) {
         $data = array('sections' => array(), 'text' => array($html), 'refsections' => array());
     } else {
         $data = array();
         $data['sections'] = $parserOutput->getSections();
         $sectionCount = count($data['sections']);
         for ($i = 0; $i < $sectionCount; $i++) {
             $data['sections'][$i]['line'] = $title->getPageLanguage()->convert($data['sections'][$i]['line']);
         }
         $chunks = preg_split('/<h(?=[1-6]\\b)/i', $html);
         if (count($chunks) != count($data['sections']) + 1) {
             wfDebugLog('mobile', __METHOD__ . "(): mismatching number of " . "sections from parser and split on page {$title->getPrefixedText()}, oldid={$latest}");
             // We can't be sure about anything here, return all page HTML as one big section
             $chunks = array($html);
             $data['sections'] = array();
         }
         $data['text'] = array();
         $data['refsections'] = array();
         foreach ($chunks as $chunk) {
             if (count($data['text'])) {
                 $chunk = "<h{$chunk}";
             }
             if ($wgUseTidy && $wgMFTidyMobileViewSections && count($chunks) > 1) {
                 $chunk = MWTidy::tidy($chunk);
             }
             if (preg_match('/<ol\\b[^>]*?class="references"/', $chunk)) {
                 $data['refsections'][count($data['text'])] = true;
             }
             $data['text'][] = $chunk;
         }
         if ($this->usePageImages) {
             $image = $this->getPageImage($title);
             if ($image) {
                 $data['image'] = $image->getTitle()->getText();
             }
         }
     }
     $data['lastmodified'] = wfTimestamp(TS_ISO_8601, $wp->getTimestamp());
     // Page id
     $data['id'] = $wp->getId();
     $user = User::newFromId($wp->getUser());
     if (!$user->isAnon()) {
         $data['lastmodifiedby'] = array('name' => $wp->getUserText(), 'gender' => $user->getOption('gender'));
     } else {
         $data['lastmodifiedby'] = null;
     }
     $data['revision'] = $title->getLatestRevID();
     if (isset($parserOutput)) {
         $languages = $parserOutput->getLanguageLinks();
         $data['languagecount'] = count($languages);
         $data['displaytitle'] = $parserOutput->getDisplayTitle();
         // @fixme: Does no work for some extension properties that get added in LinksUpdate
         $data['pageprops'] = $parserOutput->getProperties();
     } else {
         $data['languagecount'] = 0;
         $data['displaytitle'] = $title->getPrefixedText();
         $data['pageprops'] = array();
     }
     if ($title->getPageLanguage()->hasVariants()) {
         $data['hasvariants'] = true;
     }
     // Don't store small pages to decrease cache size requirements
     if (strlen($html) >= $wgMFMinCachedPageSize) {
         // store for the same time as original parser output
         $wgMemc->set($key, $data, $cacheExpiry);
     }
     return $data;
 }
Пример #19
0
 public function execute()
 {
     $user = $this->getUser();
     $params = $this->extractRequestParams();
     $title = Title::newFromText($params['page']);
     if (!$title) {
         $this->dieUsageMsg('invalidtitle', $params['page']);
     }
     $isSafeAction = in_array($params['paction'], self::$SAFE_ACTIONS, true);
     $availableNamespaces = $this->veConfig->get('VisualEditorAvailableNamespaces');
     if (!$isSafeAction && (!isset($availableNamespaces[$title->getNamespace()]) || !$availableNamespaces[$title->getNamespace()])) {
         $this->dieUsage("VisualEditor is not enabled in namespace " . $title->getNamespace(), 'novenamespace');
     }
     $parserParams = array();
     if (isset($params['oldid'])) {
         $parserParams['oldid'] = $params['oldid'];
     }
     $html = $params['html'];
     if (substr($html, 0, 11) === 'rawdeflate,') {
         $deflated = base64_decode(substr($html, 11));
         wfSuppressWarnings();
         $html = gzinflate($deflated);
         wfRestoreWarnings();
         if ($deflated === $html || $html === false) {
             $this->dieUsage("HTML provided is not properly deflated", 'invaliddeflate');
         }
     }
     wfDebugLog('visualeditor', "called on '{$title}' with paction: '{$params['paction']}'");
     switch ($params['paction']) {
         case 'parse':
         case 'metadata':
             // Dirty hack to provide the correct context for edit notices
             global $wgTitle;
             // FIXME NOOOOOOOOES
             $wgTitle = $title;
             RequestContext::getMain()->setTitle($title);
             // Get information about current revision
             if ($title->exists()) {
                 $latestRevision = Revision::newFromTitle($title);
                 if ($latestRevision === null) {
                     $this->dieUsage('Could not find latest revision for title', 'latestnotfound');
                 }
                 $revision = null;
                 if (!isset($parserParams['oldid']) || $parserParams['oldid'] === 0) {
                     $parserParams['oldid'] = $latestRevision->getId();
                     $revision = $latestRevision;
                 } else {
                     $revision = Revision::newFromId($parserParams['oldid']);
                     if ($revision === null) {
                         $this->dieUsage('Could not find revision ID ' . $parserParams['oldid'], 'oldidnotfound');
                     }
                 }
                 $restoring = $revision && !$revision->isCurrent();
                 $baseTimestamp = $latestRevision->getTimestamp();
                 $oldid = intval($parserParams['oldid']);
                 // If requested, request HTML from Parsoid/RESTBase
                 if ($params['paction'] === 'parse') {
                     $content = $this->requestRestbase('GET', 'page/html/' . urlencode($title->getPrefixedDBkey()) . '/' . $oldid, array());
                     if ($content === false) {
                         $this->dieUsage('Error contacting the document server', 'docserver');
                     }
                 }
             } else {
                 $content = '';
                 $baseTimestamp = wfTimestampNow();
                 $oldid = 0;
                 $restoring = false;
             }
             // Get edit notices
             $notices = $title->getEditNotices();
             // Anonymous user notice
             if ($user->isAnon()) {
                 $notices[] = $this->msg('anoneditwarning', '{{fullurl:Special:UserLogin|returnto={{FULLPAGENAMEE}}}}', '{{fullurl:Special:UserLogin/signup|returnto={{FULLPAGENAMEE}}}}')->parseAsBlock();
             }
             // Old revision notice
             if ($restoring) {
                 $notices[] = $this->msg('editingold')->parseAsBlock();
             }
             // New page notices
             if (!$title->exists()) {
                 $notices[] = $this->msg($user->isLoggedIn() ? 'newarticletext' : 'newarticletextanon', wfExpandUrl(Skin::makeInternalOrExternalUrl($this->msg('helppage')->inContentLanguage()->text())))->parseAsBlock();
                 // Page protected from creation
                 if ($title->getRestrictions('create')) {
                     $notices[] = $this->msg('titleprotectedwarning')->parseAsBlock();
                 }
             }
             // Look at protection status to set up notices + surface class(es)
             $protectedClasses = array();
             if (MWNamespace::getRestrictionLevels($title->getNamespace()) !== array('')) {
                 // Page protected from editing
                 if ($title->isProtected('edit')) {
                     # Is the title semi-protected?
                     if ($title->isSemiProtected()) {
                         $protectedClasses[] = 'mw-textarea-sprotected';
                         $noticeMsg = 'semiprotectedpagewarning';
                     } else {
                         $protectedClasses[] = 'mw-textarea-protected';
                         # Then it must be protected based on static groups (regular)
                         $noticeMsg = 'protectedpagewarning';
                     }
                     $notices[] = $this->msg($noticeMsg)->parseAsBlock() . $this->getLastLogEntry($title, 'protect');
                 }
                 // Deal with cascading edit protection
                 list($sources, $restrictions) = $title->getCascadeProtectionSources();
                 if (isset($restrictions['edit'])) {
                     $protectedClasses[] = ' mw-textarea-cprotected';
                     $notice = $this->msg('cascadeprotectedwarning')->parseAsBlock() . '<ul>';
                     // Unfortunately there's no nice way to get only the pages which cause
                     // editing to be restricted
                     foreach ($sources as $source) {
                         $notice .= "<li>" . Linker::link($source) . "</li>";
                     }
                     $notice .= '</ul>';
                     $notices[] = $notice;
                 }
             }
             // Permission notice
             $permErrors = $title->getUserPermissionsErrors('create', $user);
             if ($permErrors && !$title->exists()) {
                 $notices[] = $this->msg('permissionserrorstext-withaction', 1, $this->msg('action-createpage')) . "<br>" . call_user_func_array(array($this, 'msg'), $permErrors[0])->parse();
             }
             // Show notice when editing user / user talk page of a user that doesn't exist
             // or who is blocked
             // HACK of course this code is partly duplicated from EditPage.php :(
             if ($title->getNamespace() == NS_USER || $title->getNamespace() == NS_USER_TALK) {
                 $parts = explode('/', $title->getText(), 2);
                 $targetUsername = $parts[0];
                 $targetUser = User::newFromName($targetUsername, false);
                 if (!($targetUser && $targetUser->isLoggedIn()) && !User::isIP($targetUsername)) {
                     // User does not exist
                     $notices[] = "<div class=\"mw-userpage-userdoesnotexist error\">\n" . $this->msg('userpage-userdoesnotexist', wfEscapeWikiText($targetUsername)) . "\n</div>";
                 } elseif ($targetUser->isBlocked()) {
                     // Show log extract if the user is currently blocked
                     $notices[] = $this->msg('blocked-notice-logextract', $targetUser->getName())->parseAsBlock() . $this->getLastLogEntry($targetUser->getUserPage(), 'block');
                 }
             }
             // Blocked user notice
             if ($user->isBlockedFrom($title) && $user->getBlock()->prevents('edit') !== false) {
                 $notices[] = call_user_func_array(array($this, 'msg'), $user->getBlock()->getPermissionsError($this->getContext()))->parseAsBlock();
             }
             // Blocked user notice for global blocks
             if (class_exists('GlobalBlocking')) {
                 $error = GlobalBlocking::getUserBlockErrors($user, $this->getRequest()->getIP());
                 if (count($error)) {
                     $notices[] = call_user_func_array(array($this, 'msg'), $error)->parseAsBlock();
                 }
             }
             // HACK: Build a fake EditPage so we can get checkboxes from it
             $article = new Article($title);
             // Deliberately omitting ,0 so oldid comes from request
             $ep = new EditPage($article);
             $req = $this->getRequest();
             $req->setVal('format', 'text/x-wiki');
             $ep->importFormData($req);
             // By reference for some reason (bug 52466)
             $tabindex = 0;
             $states = array('minor' => false, 'watch' => false);
             $checkboxes = $ep->getCheckboxes($tabindex, $states);
             // HACK: Find out which red links are on the page
             // We do the lookup for the current version. This might not be entirely complete
             // if we're loading an oldid, but it'll probably be close enough, and LinkCache
             // will automatically request any additional data it needs.
             $links = array();
             $wikipage = WikiPage::factory($title);
             $popts = $wikipage->makeParserOptions('canonical');
             $cached = ParserCache::singleton()->get($article, $popts, true);
             $links = array('missing' => array(), 'known' => $restoring || !$cached ? array() : 1);
             if ($cached) {
                 foreach ($cached->getLinks() as $namespace => $cachedTitles) {
                     foreach ($cachedTitles as $cachedTitleText => $exists) {
                         $cachedTitle = Title::makeTitle($namespace, $cachedTitleText);
                         if (!$cachedTitle->isKnown()) {
                             $links['missing'][] = $cachedTitle->getPrefixedText();
                         } elseif ($links['known'] !== 1) {
                             $links['known'][] = $cachedTitle->getPrefixedText();
                         }
                     }
                 }
             }
             // Add information about current page
             if (!$title->isKnown()) {
                 $links['missing'][] = $title->getPrefixedText();
             } elseif ($links['known'] !== 1) {
                 $links['known'][] = $title->getPrefixedText();
             }
             // On parser cache miss, just don't bother populating red link data
             $result = array('result' => 'success', 'notices' => $notices, 'checkboxes' => $checkboxes, 'links' => $links, 'protectedClasses' => implode(' ', $protectedClasses), 'watched' => $user->isWatched($title), 'basetimestamp' => $baseTimestamp, 'starttimestamp' => wfTimestampNow(), 'oldid' => $oldid);
             if ($params['paction'] === 'parse') {
                 $result['content'] = $content;
             }
             break;
         case 'parsefragment':
             $wikitext = $params['wikitext'];
             if ($params['pst']) {
                 $wikitext = $this->pstWikitext($title, $wikitext);
             }
             $content = $this->parseWikitextFragment($title, $wikitext);
             if ($content === false) {
                 $this->dieUsage('Error contacting the document server', 'docserver');
             } else {
                 $result = array('result' => 'success', 'content' => $content);
             }
             break;
         case 'serialize':
             if ($params['cachekey'] !== null) {
                 $content = $this->trySerializationCache($params['cachekey']);
                 if (!is_string($content)) {
                     $this->dieUsage('No cached serialization found with that key', 'badcachekey');
                 }
             } else {
                 if ($params['html'] === null) {
                     $this->dieUsageMsg('missingparam', 'html');
                 }
                 $content = $this->postHTML($title, $html, $parserParams, $params['etag']);
                 if ($content === false) {
                     $this->dieUsage('Error contacting the document server', 'docserver');
                 }
             }
             $result = array('result' => 'success', 'content' => $content);
             break;
         case 'diff':
             if ($params['cachekey'] !== null) {
                 $wikitext = $this->trySerializationCache($params['cachekey']);
                 if (!is_string($wikitext)) {
                     $this->dieUsage('No cached serialization found with that key', 'badcachekey');
                 }
             } else {
                 $wikitext = $this->postHTML($title, $html, $parserParams, $params['etag']);
                 if ($wikitext === false) {
                     $this->dieUsage('Error contacting the document server', 'docserver');
                 }
             }
             $diff = $this->diffWikitext($title, $wikitext);
             if ($diff['result'] === 'fail') {
                 $this->dieUsage('Diff failed', 'difffailed');
             }
             $result = $diff;
             break;
         case 'serializeforcache':
             if (!isset($parserParams['oldid'])) {
                 $parserParams['oldid'] = Revision::newFromTitle($title)->getId();
             }
             $key = $this->storeInSerializationCache($title, $parserParams['oldid'], $html, $params['etag']);
             $result = array('result' => 'success', 'cachekey' => $key);
             break;
         case 'getlanglinks':
             $langlinks = $this->getLangLinks($title);
             if ($langlinks === false) {
                 $this->dieUsage('Error querying MediaWiki API', 'api-langlinks-error');
             } else {
                 $result = array('result' => 'success', 'langlinks' => $langlinks);
             }
             break;
     }
     $this->getResult()->addValue(null, $this->getModuleName(), $result);
 }
Пример #20
0
 /**
  * @deprecated
  *
  * @param $article Article
  * @return Boolean: true if successful, else false.
  */
 public function tryParserCache(&$article)
 {
     wfDeprecated(__METHOD__);
     $parserOutput = ParserCache::singleton()->get($article, $article->getParserOptions());
     if ($parserOutput !== false) {
         $this->addParserOutput($parserOutput);
         return true;
     } else {
         return false;
     }
 }
Пример #21
0
 /**
  * Fetches a given article from the parser cache
  * 
  * @return $result string
  * @param $article Object
  */
 protected function fetchParserCache(&$article, &$id)
 {
     global $wgUser;
     $parserCache =& ParserCache::singleton();
     $po = $parserCache->get($article, $wgUser);
     if (is_object($po)) {
         $id = $po->getCacheTime();
         return $po->getText();
     }
     return null;
 }
Пример #22
0
 public function execute()
 {
     // Get parameters
     $params = $this->extractRequestParams();
     $text = $params['text'];
     $title = $params['title'];
     $page = $params['page'];
     if (!is_null($page) && (!is_null($text) || $title != "API")) {
         $this->dieUsage("The page parameter cannot be used together with the text and title parameters", 'params');
     }
     $prop = array_flip($params['prop']);
     global $wgParser, $wgUser;
     if (!is_null($page)) {
         $titleObj = Title::newFromText($page);
         if (!$titleObj) {
             $this->dieUsageMsg(array('missingtitle', $page));
         }
         // Try the parser cache first
         $articleObj = new Article($titleObj);
         $pcache =& ParserCache::singleton();
         $p_result = $pcache->get($articleObj, $wgUser);
         if (!$p_result) {
             $p_result = $wgParser->parse($articleObj->getContent(), $titleObj, new ParserOptions());
             global $wgUseParserCache;
             if ($wgUseParserCache) {
                 $pcache->save($p_result, $articleObj, $wgUser);
             }
         }
     } else {
         $titleObj = Title::newFromText($title);
         if (!$titleObj) {
             $titleObj = Title::newFromText("API");
         }
         $p_result = $wgParser->parse($text, $titleObj, new ParserOptions());
     }
     // Return result
     $result = $this->getResult();
     $result_array = array();
     if (isset($prop['text'])) {
         $result_array['text'] = array();
         $result->setContent($result_array['text'], $p_result->getText());
     }
     if (isset($prop['langlinks'])) {
         $result_array['langlinks'] = $this->formatLangLinks($p_result->getLanguageLinks());
     }
     if (isset($prop['categories'])) {
         $result_array['categories'] = $this->formatCategoryLinks($p_result->getCategories());
     }
     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();
     }
     $result_mapping = array('langlinks' => 'll', 'categories' => 'cl', 'links' => 'pl', 'templates' => 'tl', 'images' => 'img', 'externallinks' => 'el', 'sections' => 's');
     $this->setIndexedTagNames($result_array, $result_mapping);
     $result->addValue(null, $this->getModuleName(), $result_array);
 }
Пример #23
0
 function getCachedWork()
 {
     global $wgOut;
     $parserCache = ParserCache::singleton();
     $this->mArticle->mParserOutput = $parserCache->get($this->mArticle, $this->parserOptions);
     if ($this->mArticle->mParserOutput !== false) {
         wfDebug(__METHOD__ . ": showing contents parsed by someone else\n");
         $wgOut->addParserOutput($this->mArticle->mParserOutput);
         # Ensure that UI elements requiring revision ID have
         # the correct version information.
         $wgOut->setRevisionId($this->mArticle->getLatest());
         return true;
     }
     return false;
 }
Пример #24
0
	/**
	 * Retrieves an article parsed output either from parser cache or by
	 * parsing it again. If parsing again, stores it back into parser cache.
	 *
	 * @param $title Article title object.
	 * @param $feed Whether the result should be part of a feed.
	 * @return Two-element array containing the article and its parser output.
	 *
	 * @note Mw1.16+ provides Article::getParserOptions() and
	 *   Article::getParserOutput(), that could be used here in the future.
	 *   The problem is that getParserOutput() uses ParserCache exclusively,
	 *   which means that only ParserOptions control the key used to store
	 *   the output in the cache and there is no hook yet in
	 *   ParserCache::getKey() to set these extra bits (and the
	 *   'PageRenderingCache' hook is not useful here, it is in the wrong
	 *   place without access to the parser options). This is certainly
	 *   something that should be fixed in the future.  FIXME
	 *
	 * @note This function makes a clone of the parser if
	 *   $wgWikilogCloneParser is set, but cloning the parser is not
	 *   officially supported. The problem here is that we need a different
	 *   parser that we could mess up without interfering with normal page
	 *   rendering, and we can't create a new instance because of too many
	 *   broken extensions around. Check self::parserSanityCheck().
	 */
	public static function parsedArticle( Title $title, $feed = false ) {
		global $wgWikilogCloneParser;
		global $wgUser, $wgEnableParserCache;
		global $wgParser, $wgParserConf;

		static $parser = null;

		$article = new Article( $title );

		# First try the parser cache.
		$useParserCache = $wgEnableParserCache &&
			intval( $wgUser->getOption( 'stubthreshold' ) ) == 0 &&
			$article->exists();
		$parserCache = ParserCache::singleton();

		# Parser options.
		$parserOpt = ParserOptions::newFromUser( $wgUser );
		$parserOpt->setTidy( true );
		if ( $feed ) {
			$parserOpt->setEditSection( false );

			$parserOpt->addExtraKey( "WikilogFeed" );
		} else {
			$parserOpt->enableLimitReport();
		}

		if ( $useParserCache ) {
			# Look for the parsed article output in the parser cache.
			$parserOutput = $parserCache->get( $article, $parserOpt );

			# On success, return the object retrieved from the cache.
			if ( $parserOutput ) {
				return array( $article, $parserOutput );
			}
		}

		# Enable some feed-specific behavior.
		if ( $feed ) {
			$saveFeedParse = WikilogParser::enableFeedParsing();
			$saveExpUrls = WikilogParser::expandLocalUrls();
		}

		# Get a parser instance, if not already cached.
		if ( is_null( $parser ) ) {
			if ( !StubObject::isRealObject( $wgParser ) ) {
				$wgParser->_unstub();
			}
			if ( $wgWikilogCloneParser ) {
				$parser = clone $wgParser;
			} else {
				$class = $wgParserConf['class'];
				$parser = new $class( $wgParserConf );
			}
		}
		$parser->startExternalParse( $title, $parserOpt, Parser::OT_HTML );

		# Parse article.
		$arttext = $article->fetchContent();
		$parserOutput = $parser->parse( $arttext, $title, $parserOpt );

		# Save in parser cache.
		if ( $useParserCache && $parserOutput->getCacheTime() != -1 ) {
			$parserCache->save( $parserOutput, $article, $parserOpt );
		}

		# Restore default behavior.
		if ( $feed ) {
			WikilogParser::enableFeedParsing( $saveFeedParse );
			WikilogParser::expandLocalUrls( $saveExpUrls );
		}

		return array( $article, $parserOutput );
	}
Пример #25
0
 /**
  * Do standard deferred updates after page edit.
  * Update links tables, site stats, search index and message cache.
  * Purges pages that include this page if the text was changed here.
  * Every 100th edit, prune the recent changes table.
  *
  * @param Revision $revision
  * @param User $user User object that did the revision
  * @param array $options Array of options, following indexes are used:
  * - changed: boolean, whether the revision changed the content (default true)
  * - created: boolean, whether the revision created the page (default false)
  * - moved: boolean, whether the page was moved (default false)
  * - restored: boolean, whether the page was undeleted (default false)
  * - oldrevision: Revision object for the pre-update revision (default null)
  * - oldcountable: boolean, null, or string 'no-change' (default null):
  *   - boolean: whether the page was counted as an article before that
  *     revision, only used in changed is true and created is false
  *   - null: if created is false, don't update the article count; if created
  *     is true, do update the article count
  *   - 'no-change': don't update the article count, ever
  */
 public function doEditUpdates(Revision $revision, User $user, array $options = [])
 {
     global $wgRCWatchCategoryMembership, $wgContLang;
     $options += ['changed' => true, 'created' => false, 'moved' => false, 'restored' => false, 'oldrevision' => null, 'oldcountable' => null];
     $content = $revision->getContent();
     $logger = LoggerFactory::getInstance('SaveParse');
     // See if the parser output before $revision was inserted is still valid
     $editInfo = false;
     if (!$this->mPreparedEdit) {
         $logger->debug(__METHOD__ . ": No prepared edit...\n");
     } elseif ($this->mPreparedEdit->output->getFlag('vary-revision')) {
         $logger->info(__METHOD__ . ": Prepared edit has vary-revision...\n");
     } elseif ($this->mPreparedEdit->output->getFlag('vary-revision-id') && $this->mPreparedEdit->output->getSpeculativeRevIdUsed() !== $revision->getId()) {
         $logger->info(__METHOD__ . ": Prepared edit has vary-revision-id with wrong ID...\n");
     } elseif ($this->mPreparedEdit->output->getFlag('vary-user') && !$options['changed']) {
         $logger->info(__METHOD__ . ": Prepared edit has vary-user and is null...\n");
     } else {
         wfDebug(__METHOD__ . ": Using prepared edit...\n");
         $editInfo = $this->mPreparedEdit;
     }
     if (!$editInfo) {
         // Parse the text again if needed. Be careful not to do pre-save transform twice:
         // $text is usually already pre-save transformed once. Avoid using the edit stash
         // as any prepared content from there or in doEditContent() was already rejected.
         $editInfo = $this->prepareContentForEdit($content, $revision, $user, null, false);
     }
     // Save it to the parser cache.
     // Make sure the cache time matches page_touched to avoid double parsing.
     ParserCache::singleton()->save($editInfo->output, $this, $editInfo->popts, $revision->getTimestamp(), $editInfo->revid);
     // Update the links tables and other secondary data
     if ($content) {
         $recursive = $options['changed'];
         // bug 50785
         $updates = $content->getSecondaryDataUpdates($this->getTitle(), null, $recursive, $editInfo->output);
         foreach ($updates as $update) {
             if ($update instanceof LinksUpdate) {
                 $update->setRevision($revision);
                 $update->setTriggeringUser($user);
             }
             DeferredUpdates::addUpdate($update);
         }
         if ($wgRCWatchCategoryMembership && $this->getContentHandler()->supportsCategories() === true && ($options['changed'] || $options['created']) && !$options['restored']) {
             // Note: jobs are pushed after deferred updates, so the job should be able to see
             // the recent change entry (also done via deferred updates) and carry over any
             // bot/deletion/IP flags, ect.
             JobQueueGroup::singleton()->lazyPush(new CategoryMembershipChangeJob($this->getTitle(), ['pageId' => $this->getId(), 'revTimestamp' => $revision->getTimestamp()]));
         }
     }
     Hooks::run('ArticleEditUpdates', [&$this, &$editInfo, $options['changed']]);
     if (Hooks::run('ArticleEditUpdatesDeleteFromRecentchanges', [&$this])) {
         // Flush old entries from the `recentchanges` table
         if (mt_rand(0, 9) == 0) {
             JobQueueGroup::singleton()->lazyPush(RecentChangesUpdateJob::newPurgeJob());
         }
     }
     if (!$this->exists()) {
         return;
     }
     $id = $this->getId();
     $title = $this->mTitle->getPrefixedDBkey();
     $shortTitle = $this->mTitle->getDBkey();
     if ($options['oldcountable'] === 'no-change' || !$options['changed'] && !$options['moved']) {
         $good = 0;
     } elseif ($options['created']) {
         $good = (int) $this->isCountable($editInfo);
     } elseif ($options['oldcountable'] !== null) {
         $good = (int) $this->isCountable($editInfo) - (int) $options['oldcountable'];
     } else {
         $good = 0;
     }
     $edits = $options['changed'] ? 1 : 0;
     $total = $options['created'] ? 1 : 0;
     DeferredUpdates::addUpdate(new SiteStatsUpdate(0, $edits, $good, $total));
     DeferredUpdates::addUpdate(new SearchUpdate($id, $title, $content));
     // If this is another user's talk page, update newtalk.
     // Don't do this if $options['changed'] = false (null-edits) nor if
     // it's a minor edit and the user doesn't want notifications for those.
     if ($options['changed'] && $this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $user->getTitleKey() && !($revision->isMinor() && $user->isAllowed('nominornewtalk'))) {
         $recipient = User::newFromName($shortTitle, false);
         if (!$recipient) {
             wfDebug(__METHOD__ . ": invalid username\n");
         } else {
             // Allow extensions to prevent user notification
             // when a new message is added to their talk page
             if (Hooks::run('ArticleEditUpdateNewTalk', [&$this, $recipient])) {
                 if (User::isIP($shortTitle)) {
                     // An anonymous user
                     $recipient->setNewtalk(true, $revision);
                 } elseif ($recipient->isLoggedIn()) {
                     $recipient->setNewtalk(true, $revision);
                 } else {
                     wfDebug(__METHOD__ . ": don't need to notify a nonexistent user\n");
                 }
             }
         }
     }
     if ($this->mTitle->getNamespace() == NS_MEDIAWIKI) {
         // XXX: could skip pseudo-messages like js/css here, based on content model.
         $msgtext = $content ? $content->getWikitextForTransclusion() : null;
         if ($msgtext === false || $msgtext === null) {
             $msgtext = '';
         }
         MessageCache::singleton()->replace($shortTitle, $msgtext);
         if ($wgContLang->hasVariants()) {
             $wgContLang->updateConversionTable($this->mTitle);
         }
     }
     if ($options['created']) {
         self::onArticleCreate($this->mTitle);
     } elseif ($options['changed']) {
         // bug 50785
         self::onArticleEdit($this->mTitle, $revision);
     }
     ResourceLoaderWikiModule::invalidateModuleCache($this->mTitle, $options['oldrevision'], $revision, wfWikiID());
 }
Пример #26
0
 /** Lightweight method to get the parser output for a page, checking the parser cache
  * and so on. Doesn't consider most of the stuff that Article::view is forced to
  * consider, so it's not appropriate to use there.
  */
 function getParserOutput($oldid = null)
 {
     global $wgEnableParserCache, $wgUser, $wgOut;
     // Should the parser cache be used?
     $useParserCache = $wgEnableParserCache && intval($wgUser->getOption('stubthreshold')) == 0 && $this->exists() && $oldid === null;
     wfDebug(__METHOD__ . ': using parser cache: ' . ($useParserCache ? 'yes' : 'no') . "\n");
     if ($wgUser->getOption('stubthreshold')) {
         wfIncrStats('pcache_miss_stub');
     }
     $parserOutput = false;
     if ($useParserCache) {
         $parserOutput = ParserCache::singleton()->get($this, $this->getParserOptions());
     }
     if ($parserOutput === false) {
         // Cache miss; parse and output it.
         $rev = Revision::newFromTitle($this->getTitle(), $oldid);
         return $this->getOutputFromWikitext($rev->getText(), $useParserCache);
     } else {
         return $parserOutput;
     }
 }
Пример #27
0
 /**
  * @param Title $title
  * @return bool
  */
 protected function runForTitle(Title $title)
 {
     // Wait for the DB of the current/next slave DB handle to catch up to the master.
     // This way, we get the correct page_latest for templates or files that just changed
     // milliseconds ago, having triggered this job to begin with.
     if (isset($this->params['masterPos']) && $this->params['masterPos'] !== false) {
         wfGetLB()->waitFor($this->params['masterPos']);
     }
     // Fetch the current page and revision...
     $page = WikiPage::factory($title);
     $revision = Revision::newFromTitle($title, false, Revision::READ_NORMAL);
     if (!$revision) {
         $this->setLastError("refreshLinks: Article not found {$title->getPrefixedDBkey()}");
         return false;
         // XXX: what if it was just deleted?
     }
     $content = $revision->getContent(Revision::RAW);
     if (!$content) {
         // If there is no content, pretend the content is empty
         $content = $revision->getContentHandler()->makeEmptyContent();
     }
     $parserOutput = false;
     $parserOptions = $page->makeParserOptions('canonical');
     // If page_touched changed after this root job, then it is likely that
     // any views of the pages already resulted in re-parses which are now in
     // cache. The cache can be reused to avoid expensive parsing in some cases.
     if (isset($this->params['rootJobTimestamp'])) {
         $opportunistic = !empty($this->params['isOpportunistic']);
         $skewedTimestamp = $this->params['rootJobTimestamp'];
         if ($opportunistic) {
             // Neither clock skew nor DB snapshot/slave lag matter much for such
             // updates; focus on reusing the (often recently updated) cache
         } else {
             // For transclusion updates, the template changes must be reflected
             $skewedTimestamp = wfTimestamp(TS_MW, wfTimestamp(TS_UNIX, $skewedTimestamp) + self::CLOCK_FUDGE);
         }
         if ($page->getLinksTimestamp() > $skewedTimestamp) {
             // Something already updated the backlinks since this job was made
             return true;
         }
         if ($page->getTouched() >= $skewedTimestamp || $opportunistic) {
             // Something bumped page_touched since this job was made
             // or the cache is otherwise suspected to be up-to-date
             $parserOutput = ParserCache::singleton()->getDirty($page, $parserOptions);
             if ($parserOutput && $parserOutput->getCacheTime() < $skewedTimestamp) {
                 $parserOutput = false;
                 // too stale
             }
         }
     }
     // Fetch the current revision and parse it if necessary...
     if ($parserOutput == false) {
         $start = microtime(true);
         // Revision ID must be passed to the parser output to get revision variables correct
         $parserOutput = $content->getParserOutput($title, $revision->getId(), $parserOptions, false);
         $elapsed = microtime(true) - $start;
         // If it took a long time to render, then save this back to the cache to avoid
         // wasted CPU by other apaches or job runners. We don't want to always save to
         // cache as this can cause high cache I/O and LRU churn when a template changes.
         if ($elapsed >= self::PARSE_THRESHOLD_SEC && $page->shouldCheckParserCache($parserOptions, $revision->getId()) && $parserOutput->isCacheable()) {
             $ctime = wfTimestamp(TS_MW, (int) $start);
             // cache time
             ParserCache::singleton()->save($parserOutput, $page, $parserOptions, $ctime, $revision->getId());
         }
     }
     $updates = $content->getSecondaryDataUpdates($title, null, !empty($this->params['useRecursiveLinksUpdate']), $parserOutput);
     foreach ($updates as $key => $update) {
         if ($update instanceof LinksUpdate) {
             if (!empty($this->params['triggeredRecursive'])) {
                 $update->setTriggeredRecursive();
             }
             if (!empty($this->params['triggeringUser'])) {
                 $userInfo = $this->params['triggeringUser'];
                 if ($userInfo['userId']) {
                     $user = User::newFromId($userInfo['userId']);
                 } else {
                     // Anonymous, use the username
                     $user = User::newFromName($userInfo['userName'], false);
                 }
                 $update->setTriggeringUser($user);
             }
             if (!empty($this->params['triggeringRevisionId'])) {
                 $revision = Revision::newFromId($this->params['triggeringRevisionId']);
                 if ($revision === null) {
                     $revision = Revision::newFromId($this->params['triggeringRevisionId'], Revision::READ_LATEST);
                 }
                 $update->setRevision($revision);
             }
         }
     }
     DataUpdate::runUpdates($updates);
     InfoAction::invalidateCache($title);
     return true;
 }
Пример #28
0
 static function clearCache()
 {
     if (self::$cache_control) {
         global $parserMemc;
         $parserCache = ParserCache::singleton();
         $key = $parserCache->getKey(self::$article, self::$user);
         $parserMemc->delete($key);
         if (method_exists('Article', 'doPurge')) {
             self::$article->doPurge();
         } else {
             WikiPage::factory(self::$title)->doPurge();
         }
     }
 }
Пример #29
0
 /**
  * Show the new revision of the page.
  */
 function renderNewRevision()
 {
     global $wgOut, $wgUser;
     wfProfileIn(__METHOD__);
     # Add "current version as of X" title
     $wgOut->addHTML("<hr class='diff-hr' />\n\t\t<h2 class='diff-currentversion-title'>{$this->mPagetitle}</h2>\n");
     # Page content may be handled by a hooked call instead...
     if (wfRunHooks('ArticleContentOnDiff', array($this, $wgOut))) {
         # Use the current version parser cache if applicable
         $pCache = true;
         if (!$this->mNewRev->isCurrent()) {
             $oldEditSectionSetting = $wgOut->parserOptions()->setEditSection(false);
             $pCache = false;
         }
         $this->loadNewText();
         $wgOut->setRevisionId($this->mNewRev->getId());
         if ($this->mTitle->isCssJsSubpage() || $this->mTitle->isCssOrJsPage()) {
             // Stolen from Article::view --AG 2007-10-11
             // Give hooks a chance to customise the output
             // @TODO: standardize this crap into one function
             if (wfRunHooks('ShowRawCssJs', array($this->mNewtext, $this->mTitle, $wgOut))) {
                 // Wrap the whole lot in a <pre> and don't parse
                 $m = array();
                 preg_match('!\\.(css|js)$!u', $this->mTitle->getText(), $m);
                 $wgOut->addHTML("<pre class=\"mw-code mw-{$m[1]}\" dir=\"ltr\">\n");
                 $wgOut->addHTML(htmlspecialchars($this->mNewtext));
                 $wgOut->addHTML("\n</pre>\n");
             }
         } elseif ($pCache) {
             $article = new Article($this->mTitle, 0);
             $pOutput = ParserCache::singleton()->get($article, $wgOut->parserOptions());
             if ($pOutput) {
                 $wgOut->addParserOutput($pOutput);
             } else {
                 $article->doViewParse();
             }
         } else {
             $wgOut->addWikiTextTidy($this->mNewtext);
         }
         if (!$this->mNewRev->isCurrent()) {
             $wgOut->parserOptions()->setEditSection($oldEditSectionSetting);
         }
     }
     # Add redundant patrol link on bottom...
     if ($this->mRcidMarkPatrolled && $this->mTitle->quickUserCan('patrol')) {
         $sk = $wgUser->getSkin();
         $token = $wgUser->editToken($this->mRcidMarkPatrolled);
         $wgOut->preventClickjacking();
         $wgOut->addHTML("<div class='patrollink'>[" . $sk->link($this->mTitle, wfMsgHtml('markaspatrolleddiff'), array(), array('action' => 'markpatrolled', 'rcid' => $this->mRcidMarkPatrolled, 'token' => $token)) . ']</div>');
     }
     wfProfileOut(__METHOD__);
 }
Пример #30
0
 /**
  * Do standard deferred updates after page edit.
  * Update links tables, site stats, search index and message cache.
  * Every 1000th edit, prune the recent changes table.
  * 
  * @private
  * @param $text New text of the article
  * @param $summary Edit summary
  * @param $minoredit Minor edit
  * @param $timestamp_of_pagechange Timestamp associated with the page change
  * @param $newid rev_id value of the new revision
  * @param $changed Whether or not the content actually changed
  */
 function editUpdates($text, $summary, $minoredit, $timestamp_of_pagechange, $newid, $changed = true)
 {
     global $wgDeferredUpdateList, $wgMessageCache, $wgUser, $wgParser;
     wfProfileIn(__METHOD__);
     # Parse the text
     $options = new ParserOptions();
     $options->setTidy(true);
     $poutput = $wgParser->parse($text, $this->mTitle, $options, true, true, $newid);
     # Save it to the parser cache
     $parserCache =& ParserCache::singleton();
     $parserCache->save($poutput, $this, $wgUser);
     # Update the links tables
     $u = new LinksUpdate($this->mTitle, $poutput);
     $u->doUpdate();
     if (wfRunHooks('ArticleEditUpdatesDeleteFromRecentchanges', array(&$this))) {
         wfSeedRandom();
         if (0 == mt_rand(0, 999)) {
             # Periodically flush old entries from the recentchanges table.
             global $wgRCMaxAge;
             $dbw =& wfGetDB(DB_MASTER);
             $cutoff = $dbw->timestamp(time() - $wgRCMaxAge);
             $recentchanges = $dbw->tableName('recentchanges');
             $sql = "DELETE FROM {$recentchanges} WHERE rc_timestamp < '{$cutoff}'";
             $dbw->query($sql);
         }
     }
     $id = $this->getID();
     $title = $this->mTitle->getPrefixedDBkey();
     $shortTitle = $this->mTitle->getDBkey();
     if (0 == $id) {
         wfProfileOut(__METHOD__);
         return;
     }
     $u = new SiteStatsUpdate(0, 1, $this->mGoodAdjustment, $this->mTotalAdjustment);
     array_push($wgDeferredUpdateList, $u);
     $u = new SearchUpdate($id, $title, $text);
     array_push($wgDeferredUpdateList, $u);
     # If this is another user's talk page, update newtalk
     # Don't do this if $changed = false otherwise some idiot can null-edit a
     # load of user talk pages and piss people off, nor if it's a minor edit
     # by a properly-flagged bot.
     if ($this->mTitle->getNamespace() == NS_USER_TALK && $shortTitle != $wgUser->getTitleKey() && $changed && !($minoredit && $wgUser->isAllowed('nominornewtalk'))) {
         if (wfRunHooks('ArticleEditUpdateNewTalk', array(&$this))) {
             $other = User::newFromName($shortTitle);
             if (is_null($other) && User::isIP($shortTitle)) {
                 // An anonymous user
                 $other = new User();
                 $other->setName($shortTitle);
             }
             if ($other) {
                 $other->setNewtalk(true);
             }
         }
     }
     if ($this->mTitle->getNamespace() == NS_MEDIAWIKI) {
         $wgMessageCache->replace($shortTitle, $text);
     }
     wfProfileOut(__METHOD__);
 }